From 0da894b408e711e57c4ab45cbc837445621f1130 Mon Sep 17 00:00:00 2001 From: npub1t490dek5vslqxv0ft0jlft6j5fjlp74jery2tyj7ngf8ggd0pcnst8w25n <5d4af6e6d4643e0331e95be5f4af52a265f0fab2c8c8a5925e9a127421af0e27@sprout-oss.stage.blox.sqprod.co> Date: Fri, 17 Jul 2026 16:13:54 -0700 Subject: [PATCH] fix(desktop): continue onboarding after key import Keep explicitly imported identities in machine onboarding so the setup step can render after the identity cache changes. Co-authored-by: npub1t490dek5vslqxv0ft0jlft6j5fjlp74jery2tyj7ngf8ggd0pcnst8w25n <5d4af6e6d4643e0331e95be5f4af52a265f0fab2c8c8a5925e9a127421af0e27@sprout-oss.stage.blox.sqprod.co> Signed-off-by: npub1t490dek5vslqxv0ft0jlft6j5fjlp74jery2tyj7ngf8ggd0pcnst8w25n <5d4af6e6d4643e0331e95be5f4af52a265f0fab2c8c8a5925e9a127421af0e27@sprout-oss.stage.blox.sqprod.co> --- desktop/src/app/App.tsx | 1 + .../features/onboarding/machineOnboarding.ts | 13 ++++++++++++- .../onboarding/ui/MachineOnboardingFlow.tsx | 5 ++++- desktop/tests/e2e/onboarding.spec.ts | 17 +++++++++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/desktop/src/app/App.tsx b/desktop/src/app/App.tsx index 6b799636f3..d8a499a0eb 100644 --- a/desktop/src/app/App.tsx +++ b/desktop/src/app/App.tsx @@ -463,6 +463,7 @@ function MachineBootstrap({ sharedIdentity }: { sharedIdentity: boolean }) { <> ( null, ); + const continuingPubkeyRef = React.useRef(null); const startupPubkeyRef = React.useRef(null); const [bootedLost, setBootedLost] = React.useState(false); const [bootedLocked, setBootedLocked] = React.useState(false); @@ -157,6 +158,10 @@ export function useMachineOnboardingState({ [currentPubkey], ); + const continueWithIdentity = React.useCallback((pubkey: string) => { + continuingPubkeyRef.current = pubkey; + }, []); + const reopen = React.useCallback(() => { clearMachineOnboardingCompletion(currentPubkey); setCompletedPubkey((pubkey) => (pubkey === currentPubkey ? null : pubkey)); @@ -188,7 +193,12 @@ export function useMachineOnboardingState({ identityQuery.fetchStatus === "fetching", ) || !currentPubkey || - (!hasCompletedCurrentPubkey && evaluatedPubkey !== currentPubkey) + // Imported identities are published before the flow can advance to setup. + // Keep that explicitly requested identity switch in onboarding; only the + // startup identity needs the one-render evaluation gate above. + (!hasCompletedCurrentPubkey && + evaluatedPubkey !== currentPubkey && + continuingPubkeyRef.current !== currentPubkey) ) { stage = "blocking"; } else if (identityLost || !hasCompletedCurrentPubkey) { @@ -199,6 +209,7 @@ export function useMachineOnboardingState({ return { complete, + continueWithIdentity, currentPubkey, identityLost, queryClient, diff --git a/desktop/src/features/onboarding/ui/MachineOnboardingFlow.tsx b/desktop/src/features/onboarding/ui/MachineOnboardingFlow.tsx index 4d6bf54c76..d7e5da4e93 100644 --- a/desktop/src/features/onboarding/ui/MachineOnboardingFlow.tsx +++ b/desktop/src/features/onboarding/ui/MachineOnboardingFlow.tsx @@ -33,11 +33,13 @@ export type MachineOnboardingPage = export function MachineOnboardingFlow({ complete, + continueWithIdentity, identityLost, initialPage, queryClient, }: { complete: (pubkey?: string) => void; + continueWithIdentity: (pubkey: string) => void; identityLost: boolean; initialPage?: MachineOnboardingPage; queryClient: QueryClient; @@ -141,12 +143,13 @@ export function MachineOnboardingFlow({ const importExistingIdentity = React.useCallback( async (nsec: string) => { const identity = await importIdentity(nsec); + continueWithIdentity(identity.pubkey); queryClient.setQueryData(["identity"], identity); setIdentityWasImported(true); setSelectedPubkey(identity.pubkey); setPage("setup"); }, - [queryClient], + [continueWithIdentity, queryClient], ); return ( diff --git a/desktop/tests/e2e/onboarding.spec.ts b/desktop/tests/e2e/onboarding.spec.ts index 08b5b82123..9a82b2feca 100644 --- a/desktop/tests/e2e/onboarding.spec.ts +++ b/desktop/tests/e2e/onboarding.spec.ts @@ -565,6 +565,23 @@ test("completed users skip the loading gate while profile is still settling", as await expectHomeView(page); }); +test("first-launch key import continues to machine setup", async ({ page }) => { + await installMockBridge(page, undefined, { + skipCommunitySeed: true, + skipOnboardingSeed: true, + }); + await page.goto("/"); + + await page.getByRole("button", { name: "Enter a key" }).click(); + const importedNsec = nsecEncode(hexToBytes(TEST_IDENTITIES.alice.privateKey)); + await page.getByTestId("nostr-import-nsec-input").fill(importedNsec); + await page.getByTestId("nostr-import-submit").click(); + + await expect(page.getByTestId("onboarding-page-2")).toBeVisible(); + await expect(page.getByTestId("machine-onboarding-gate")).toBeVisible(); + await expect(page.getByTestId("app-loading-gate")).toHaveCount(0); +}); + test("first-community choices expose npub and invite input", async ({ page, }) => {