Public: anyone can read this; the crew can change it.
small: the home page no longer ends with "This forge is not taking new accoun… #11328
Check · Review · Land · Live done — Landed and live in production.
torque/forge:torque/11325-small-the-home-page-no-longer-ends-with → main 4 commits fork 3 threads · 3 unresolved
5 files
changed +38
−15
src/web/pages/site.ts +9 −3
| @@ -241,8 +241,8 @@ interface AuthErrors { | ||
| 241 | 241 | const SIGNUP_SENTENCES: Record<SignupMode, string | undefined> = { |
| 242 | 242 | open: undefined, |
| 243 | 243 | request: "New accounts here start after someone who runs the forge allows them.", |
| 244 | invite: "This forge is invite-only: new accounts need an invite link.", | |
| 245 | closed: "This forge is closed to new accounts.", | |
| 244 | invite: undefined, | |
| 245 | closed: undefined, | |
| 246 | 246 | }; |
| 247 | 247 | |
| 248 | 248 | /** |
| @@ -316,7 +316,8 @@ function authForm( | ||
| 316 | 316 | <button type="button" id="passkey-signin" class="btn" data-variant="primary" data-size="lg">Sign in with a passkey</button> |
| 317 | 317 | ${passkeyNote("passkey-signin-note")} |
| 318 | 318 | </div> |
| 319 | ${when(mode === undefined ? undefined : SIGNUP_SENTENCES[mode], (said) => html`<p class="muted small mb" id="signup-mode">${said}</p>`)}`, | |
| 319 | ${when(mode === undefined ? undefined : SIGNUP_SENTENCES[mode], (said) => html`<p class="muted small mb" id="signup-mode">${said}</p>`)} | |
| 320 | ${when(mode === "invite" || mode === "closed" ? mode : undefined, () => byInvitationLine())}`, | |
| 320 | 321 | ); |
| 321 | 322 | |
| 322 | 323 | const body = html`${unknownLink} |
| @@ -488,6 +489,11 @@ export function inviteOf(ctx: Ctx): string | undefined { | ||
| 488 | 489 | return token === "" ? undefined : token; |
| 489 | 490 | } |
| 490 | 491 | |
| 492 | /** The one place the forge says its accounts are closed: `/signin` (and `/signup` through its own message), never the home page (#11325). */ | |
| 493 | function byInvitationLine(): Html { | |
| 494 | return html`<p class="small mb" id="by-invitation">Accounts on this forge are by invitation. <a href="/#yours">Start your own forge instead.</a></p>`; | |
outdated
Anchor #start-forge is only present when the home start form renders; use /#yours or guard on the form existing. Otherwise the link lands on nothing. |
||
| 495 | } | |
| 496 | ||
| 491 | 497 | /** The whole of `/signup` while the forge makes no new accounts: a sentence, no form. */ |
| 492 | 498 | function signupClosedPage(ctx: Ctx): Response { |
| 493 | 499 | const said = inviteOf(ctx) === undefined ? SIGNUP_CLOSED_MESSAGE : INVITE_SPENT_MESSAGE; |
src/web/studio/home.ts −1
| @@ -920,7 +920,6 @@ bun run src/main.ts</code></pre> | ||
| 920 | 920 | Running this for a team? <a href="https://www.open-platform.sh/#platform">The Platform</a> runs on your own cloud, and |
| 921 | 921 | <a href="https://www.espodev.com">EspoDev</a> delivers enterprise implementations. |
| 922 | 922 | </p> |
| 923 | ${accounts ? "" : html`<p class="muted small">${mode === "invite" ? "New accounts on this forge are by invitation." : "This forge is not taking new accounts."}</p>`} | |
| 924 | 923 | ${when(admin, () => html`<div class="door-section front-admin">${frontControls(ctx, front)}</div>`)} |
| 925 | 924 | </div>`; |
| 926 | 925 | const res = page({ |
test/front.test.ts +2 −1
| @@ -559,7 +559,8 @@ describe("the front page", () => { | ||
| 559 | 559 | expect(closed).toContain(">Suggest a change<"); |
| 560 | 560 | expect(closed).not.toContain(">Help build<"); |
| 561 | 561 | expect(closed).toContain('href="/signup"'); |
| 562 | expect(closed).toContain("New accounts on this forge are by invitation."); | |
| 562 | expect(closed).not.toContain("by invitation"); | |
| 563 | expect(closed).not.toContain("not taking new accounts"); | |
| 563 | 564 | changeSignup(app, admin, "open"); |
| 564 | 565 | expect((await put(adminToken, { pinned: [], hidden: [] })).status).toBe(200); |
| 565 | 566 | }); |
test/plans-row.test.ts +19
| @@ -159,3 +159,22 @@ test("changing a price in the plans setting changes the page", async () => { | ||
| 159 | 159 | expect(section).toContain("$7 of crew credit each month"); |
| 160 | 160 | expect(section).not.toContain("$19 a month"); |
| 161 | 161 | }); |
| 162 | ||
| 163 | test("the anonymous home never says the forge is not taking accounts; /signin says it once, linking /#yours (#11325)", async () => { | |
| 164 | app.db.run("DELETE FROM forge_setting WHERE key = 'billing.links'"); | |
| 165 | put("billing.links", JSON.stringify({ small: "https://buy.example/s" })); | |
| 166 | for (const mode of ["closed", "invite"]) { | |
| 167 | app.db.run("DELETE FROM forge_setting WHERE key = 'signup'"); | |
| 168 | put("signup", mode); | |
| 169 | const home = await (await fetch(`${servers.url}/`)).text(); | |
| 170 | expect(home).not.toContain("not taking new accounts"); | |
| 171 | expect(home).not.toContain("are by invitation"); | |
| 172 | const signin = await (await fetch(`${servers.url}/signin`)).text(); | |
| 173 | expect(signin).toContain("Accounts on this forge are by invitation."); | |
| 174 | expect(signin).toContain('<a href="/#yours">Start your own forge instead.</a>'); | |
| 175 | expect(signin).not.toContain("closed to new accounts"); | |
| 176 | expect(signin).not.toContain("invite-only"); | |
| 177 | const signup = await (await fetch(`${servers.url}/signup`)).text(); | |
| 178 | expect(signup).not.toContain("id=\"by-invitation\""); | |
| 179 | } | |
| 180 | }); |
test/signin-page.test.ts +8 −10
| @@ -90,16 +90,14 @@ describe("/signin", () => { | ||
| 90 | 90 | expect(page).not.toContain(CLOSED); |
| 91 | 91 | }); |
| 92 | 92 | |
| 93 | test("invite says the forge is invite-only", async () => { | |
| 94 | const page = await signin("invite"); | |
| 95 | expect(page).toContain(INVITE); | |
| 96 | expect(page).not.toContain(CLOSED); | |
| 97 | }); | |
| 98 | ||
| 99 | test("closed says the forge is closed to new accounts", async () => { | |
| 100 | const page = await signin("closed"); | |
| 101 | expect(page).toContain(CLOSED); | |
| 102 | expect(page).not.toContain(INVITE); | |
| 93 | test("invite and closed say it once: by invitation, linking /#yours", async () => { | |
| 94 | for (const mode of ["invite", "closed"] as const) { | |
| 95 | const page = await signin(mode); | |
| 96 | expect(page).not.toContain(INVITE); | |
| 97 | expect(page).not.toContain(CLOSED); | |
| 98 | expect(page.split("are by invitation").length - 1).toBe(1); | |
| 99 | expect(page).toContain('href="/#yours"'); | |
| 100 | } | |
| 103 | 101 | }); |
| 104 | 102 | }); |
| 105 | 103 |
new above
offline
Principle 1: with SIGNUP_SENTENCES already printing a sentence just above (and "This forge is closed to new accounts." on /signin) this adds a third statement of the same fact. Replace the existing sentence for closed/invite with this one line instead of stacking; update the tests that assert the old wording rather than leaving duplicates.