diff --git a/apps/web/package.json b/apps/web/package.json index 614c306..f169a2f 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -8,6 +8,7 @@ }, "dependencies": { "@profullstack/affiliates": "0.3.0", + "@profullstack/footer": "latest", "@profullstack/profile": "0.1.6", "@profullstack/redeem-credits": "0.2.0", "@profullstack/sealed-secrets": "0.1.0", diff --git a/apps/web/public/index.html b/apps/web/public/index.html index b8c6939..f2c58ba 100644 --- a/apps/web/public/index.html +++ b/apps/web/public/index.html @@ -33,6 +33,7 @@
Scheduling, prescriptions, labs and AI follow-up calls for your practice. Join with this invite and you both get ${credits.INVITE_BONUS_DAYS} days of the paid plan, on top of the 14-day trial.
diff --git a/apps/web/src/pit-footer.js b/apps/web/src/pit-footer.js deleted file mode 100644 index 7046f5c..0000000 --- a/apps/web/src/pit-footer.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * The pit.world network footer, fetched server-side from pit.world/snippets and - * appended as the last thing in on every page. - * - * Module-level cache: the first page waits for the first fetch (3 s timeout); - * after that a copy older than an hour refreshes in the background and a failed - * fetch keeps the stale copy. With no copy at all the page is left unchanged. - */ -const PIT_FOOTER_URL = 'https://pit.world/snippets/footer.html?site=tleehealth.com'; -const TTL_MS = 60 * 60 * 1000; - -let html = ''; -let fetchedAt = 0; -let inflight = null; - -function refresh() { - inflight ??= fetch(PIT_FOOTER_URL, { signal: AbortSignal.timeout(3000) }) - .then(async (res) => { - if (!res.ok) throw new Error(`pit footer ${res.status}`); - const text = await res.text(); - if (!text.includes('data-pit-footer')) throw new Error('pit footer: unexpected body'); - html = text; - fetchedAt = Date.now(); - }) - .catch(() => {}) - .finally(() => { - inflight = null; - }); - return inflight; -} - -/** The page with the network footer inserted before its last . */ -export async function withPitFooter(page) { - if (Date.now() - fetchedAt > TTL_MS) { - if (html) refresh(); - else await refresh(); - } - const i = page.lastIndexOf(''); - if (i === -1) return page; - const registration = page.includes('/pwa-register.js') ? '' : '\n'; - return `${page.slice(0, i)}${registration}${html ? `${html}\n` : ''}${page.slice(i)}`; -} diff --git a/apps/web/src/universal-footer.js b/apps/web/src/universal-footer.js new file mode 100644 index 0000000..1114bd8 --- /dev/null +++ b/apps/web/src/universal-footer.js @@ -0,0 +1,41 @@ +import { footerHtml, footerHtmlSync } from '@profullstack/footer'; + +const options = { + site: 'https://tleehealth.com/', + owner: { name: 'tleehealth', url: 'https://tleehealth.com/' }, + links: [ + { label: 'Start a practice', href: '/app' }, + { label: 'Terms', href: '/terms' }, + { label: 'Privacy', href: '/privacy' }, + { label: 'Contact', href: 'mailto:hello@tleehealth.com' }, + ], + pwamartHref: 'https://pwamart.com/for?site=https://tleehealth.com/', + builtIndieHref: 'https://built.indie.pit.world/for?site=https://tleehealth.com/', +}; +const TTL_MS = 15 * 60 * 1000; + +let html = footerHtmlSync(options); +let fetchedAt = 0; +let inflight = null; + +async function refresh() { + inflight ??= footerHtml(options) + .then((value) => { + html = value; + fetchedAt = Date.now(); + }) + .catch(() => {}) + .finally(() => { + inflight = null; + }); + await inflight; +} + +/** Append the universal footer to each HTML page, keeping it in server markup. */ +export async function withUniversalFooter(page) { + if (Date.now() - fetchedAt > TTL_MS) await refresh(); + const i = page.lastIndexOf(''); + if (i === -1) return page; + const registration = page.includes('/pwa-register.js') ? '' : '\n'; + return `${page.slice(0, i)}${registration}${html}\n${page.slice(i)}`; +} diff --git a/bun.lock b/bun.lock index f6de829..c7c0f13 100644 --- a/bun.lock +++ b/bun.lock @@ -10,6 +10,7 @@ "version": "0.1.2", "dependencies": { "@profullstack/affiliates": "0.3.0", + "@profullstack/footer": "latest", "@profullstack/profile": "0.1.6", "@profullstack/redeem-credits": "0.2.0", "@profullstack/sealed-secrets": "0.1.0", @@ -92,6 +93,8 @@ "@profullstack/affiliates": ["@profullstack/affiliates@0.3.0", "", { "dependencies": { "@profullstack/referrals": "^0.1.0" }, "bin": { "affiliates": "dist/cli.js" } }, "sha512-bp0iREciWetDWrQYOsa6SJRTzviekKwgJQuATT7vql+4equu7pN76q8tb3SP1oBMczydsv6rYUMvPpS6+QT7yQ=="], + "@profullstack/footer": ["@profullstack/footer@0.6.10", "", { "peerDependencies": { "react": ">=18" }, "optionalPeers": ["react"], "bin": { "profullstack-footer": "bin/profullstack-footer.js" } }, "sha512-egRG4zNRbeCklTdKqeIlYGaePb0aC5wuIYyu6NWiJ1j9XqnK/UO9p1C64cOCAKSmmJWH7hc714WgnMCegmI4fg=="], + "@profullstack/hqtui": ["@profullstack/hqtui@0.11.1", "", { "bin": { "hqtui": "bin/hqtui.mjs" } }, "sha512-c/UzTmiRDjdLZQEO/gdVinUKtx6Q0WBPdixIm90MBTvC3DqFPqpersRs0BIDPS7XVokCSr1DtELI91kj5J1kCg=="], "@profullstack/keys": ["@profullstack/keys@0.1.0", "", {}, "sha512-Vys47LvS5ivhdZu4SYOxh9Le3DEmWhjP0+CtrU45RhXAbY0ZFG/2Dk9V1bIIUGDp8UWMJqctvUD21JrNjfh04w=="], diff --git a/test/app.test.js b/test/app.test.js index 6ce8128..b2b81f2 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -44,8 +44,9 @@ describe('web', () => { }); test('landing footer server-renders the Profullstack webring row, one copyright line', async () => { const home = await (await get('/')).text(); - const foot = home.slice(home.indexOf('', home.indexOf('