diff --git a/apps/shell/src/auth/AuthWizard.tsx b/apps/shell/src/auth/AuthWizard.tsx index a2f5696..657fb70 100644 --- a/apps/shell/src/auth/AuthWizard.tsx +++ b/apps/shell/src/auth/AuthWizard.tsx @@ -24,7 +24,9 @@ import { type AtomAccountType, } from "./hostedAccount.js"; import { + chooserActionButtonLabel, chooserActions, + chooserConflictCopy, emailsEqualIgnoreCase, mayClearLocalSignupState, resolveChooserIdentity, @@ -1950,15 +1952,20 @@ export function AuthWizard({ mode, onClose, embedded = false }: AuthWizardProps) {chooserPrimaryEmail || "Your account"} {chooserConflictEmail ? ( -

- Signed in as {chooserPrimaryEmail}. A different - unfinished signup for {chooserConflictEmail} will - be discarded if you continue. -

+ <> +

+ {chooserConflictCopy(chooserPrimaryEmail, chooserConflictEmail)} +

+ {chooserFinishPayHint ? ( +

+ You’re signed in — finish payment to open Atom. +

+ ) : null} + ) : (

{chooserFinishPayHint - ? "Payment isn’t finished for this account." + ? "You’re signed in — finish payment to open Atom." : "You’re already signed in on this device."}

)} @@ -1976,14 +1983,10 @@ export function AuthWizard({ mode, onClose, embedded = false }: AuthWizardProps) disabled={chooserBusy} onClick={() => void handleChooserAction(action.id)} > - {action.label} - {action.id === "complete_signup" && chooserPrimaryEmail - ? ` for ${chooserPrimaryEmail}` - : action.id === "continue_session" && chooserPrimaryEmail - ? ` as ${chooserPrimaryEmail}` - : action.id === "resume_pending" && chooserPrimaryEmail - ? ` for ${chooserPrimaryEmail}` - : ""} + {chooserActionButtonLabel({ + action, + primaryEmail: chooserPrimaryEmail, + })} ))} diff --git a/apps/shell/src/auth/accountChooser.test.ts b/apps/shell/src/auth/accountChooser.test.ts index dca9586..291da1a 100644 --- a/apps/shell/src/auth/accountChooser.test.ts +++ b/apps/shell/src/auth/accountChooser.test.ts @@ -1,6 +1,8 @@ import { describe, expect, it } from "vitest"; import { + chooserActionButtonLabel, chooserActions, + chooserConflictCopy, emailsEqualIgnoreCase, mayClearLocalSignupState, resolveChooserIdentity, @@ -62,7 +64,7 @@ describe("resolveChooserIdentity", () => { }); describe("chooserActions", () => { - it("unpaid register session offers Complete + Different", () => { + it("unpaid register session offers Continue as + Different (not Complete signup)", () => { const actions = chooserActions({ mode: "register", hasSession: true, @@ -70,6 +72,7 @@ describe("chooserActions", () => { provisionable: false, }); expect(actions.map((a) => a.id)).toEqual(["complete_signup", "different_account"]); + expect(actions[0]?.label).toBe("Continue as"); }); it("pending-only offers Resume + Start over, not Log in as", () => { @@ -82,7 +85,7 @@ describe("chooserActions", () => { expect(actions.map((a) => a.id)).toEqual(["resume_pending", "start_over"]); }); - it("unpaid login session offers Finish payment then Different", () => { + it("unpaid login session offers Continue as then Different", () => { const actions = chooserActions({ mode: "login", hasSession: true, @@ -90,6 +93,28 @@ describe("chooserActions", () => { provisionable: false, }); expect(actions.map((a) => a.id)).toEqual(["complete_signup", "different_account"]); + expect(actions[0]?.label).toBe("Continue as"); + }); +}); + +describe("chooserConflictCopy", () => { + it("explains signed-in wins and draft is discarded", () => { + const copy = chooserConflictCopy("a@x.com", "b@y.com"); + expect(copy).toContain("signed in as a@x.com"); + expect(copy).toContain("b@y.com"); + expect(copy).toContain("discards that draft"); + expect(copy.toLowerCase()).not.toContain("complete signup"); + }); +}); + +describe("chooserActionButtonLabel", () => { + it("uses Continue as email for signed-in primary", () => { + expect( + chooserActionButtonLabel({ + action: { id: "complete_signup", label: "Continue as", primary: true }, + primaryEmail: "a@x.com", + }), + ).toBe("Continue as a@x.com"); }); }); diff --git a/apps/shell/src/auth/accountChooser.ts b/apps/shell/src/auth/accountChooser.ts index 8253951..52f2d3f 100644 --- a/apps/shell/src/auth/accountChooser.ts +++ b/apps/shell/src/auth/accountChooser.ts @@ -76,35 +76,19 @@ export function chooserActions(opts: { const actions: ChooserAction[] = []; if (opts.hasSession) { - if (opts.mode === "register") { - if (unpaid) { - actions.push({ - id: "complete_signup", - label: "Complete signup", - primary: true, - }); - } else { - actions.push({ - id: "continue_session", - label: "Continue setup", - primary: true, - }); - } + // Already signed in — never say "Complete signup for …" (reads as register again). + if (unpaid) { + actions.push({ + id: "complete_signup", + label: "Continue as", + primary: true, + }); } else { - // login - if (unpaid) { - actions.push({ - id: "complete_signup", - label: "Finish payment", - primary: true, - }); - } else { - actions.push({ - id: "continue_session", - label: "Continue", - primary: true, - }); - } + actions.push({ + id: "continue_session", + label: "Continue as", + primary: true, + }); } actions.push({ id: "different_account", label: "Use a different account" }); return actions; @@ -114,7 +98,7 @@ export function chooserActions(opts: { if (opts.pendingKind === "register") { actions.push({ id: "resume_pending", - label: "Resume signup", + label: "Resume signup for", primary: true, }); } @@ -122,6 +106,31 @@ export function chooserActions(opts: { return actions; } +/** Body copy when signed-in email ≠ unfinished local draft. */ +export function chooserConflictCopy(sessionEmail: string, draftEmail: string): string { + return ( + `You’re signed in as ${sessionEmail}. This device also has an unfinished signup draft ` + + `for ${draftEmail}. Continuing uses the signed-in account and discards that draft.` + ); +} + +/** Button caption: session actions use the email after “Continue as”. */ +export function chooserActionButtonLabel(opts: { + action: ChooserAction; + primaryEmail: string; +}): string { + const email = opts.primaryEmail.trim(); + if (!email) return opts.action.label; + if ( + opts.action.id === "complete_signup" || + opts.action.id === "continue_session" || + opts.action.id === "resume_pending" + ) { + return `${opts.action.label} ${email}`; + } + return opts.action.label; +} + /** Fail-closed: only clear local signup state after sign-out succeeded. */ export function mayClearLocalSignupState(opts: { signOutSucceeded: boolean;