diff --git a/desktop/src/app/App.tsx b/desktop/src/app/App.tsx index 93fb836739..cb09cbdc5f 100644 --- a/desktop/src/app/App.tsx +++ b/desktop/src/app/App.tsx @@ -2,7 +2,6 @@ import { isTauri } from "@tauri-apps/api/core"; import { emit } from "@tauri-apps/api/event"; import { QueryClientProvider } from "@tanstack/react-query"; import { RouterProvider } from "@tanstack/react-router"; -import { Hexagon } from "lucide-react"; import { type ReactNode, useCallback, @@ -26,7 +25,11 @@ import type { Community } from "@/features/communities/types"; import { useCommunityInit } from "@/features/communities/useCommunityInit"; import { useNestNotifications } from "@/features/communities/useNestNotifications"; import { useCommunities } from "@/features/communities/useCommunities"; -import { WelcomeSetup } from "@/features/communities/ui/WelcomeSetup"; +import { + WELCOME_SETUP_PAGE_HEADINGS, + WelcomeSetup, + type WelcomeSetupPage, +} from "@/features/communities/ui/WelcomeSetup"; import { CommunityApplyErrorScreen } from "@/features/communities/ui/CommunityApplyErrorScreen"; import { CommunityChangeOverlay } from "@/features/communities/ui/CommunityChangeOverlay"; import { createBuzzQueryClient } from "@/shared/api/queryClient"; @@ -178,8 +181,17 @@ function CommunitySwitchGate() { ); } -function OnboardingLoadingGate() { +// Shown while a first-run community handoff settles (config apply + relay +// round trips). For the default-community path the user pressed a button on +// the welcome page itself, so the gate keeps rendering that same page with +// the buttons disabled — seamless continuity. Every other WelcomeSetup +// sub-page (key import, invite, join community) instead gets a forward-motion +// connecting view under its own heading; showing the welcome replica there +// reads as being kicked back to step 1. +function OnboardingLoadingGate({ source }: { source: WelcomeSetupPage }) { const systemColorScheme = useSystemColorScheme(); + const connectingHeading = + source !== "welcome" ? WELCOME_SETUP_PAGE_HEADINGS[source] : null; return (
-- Choose your first community to get started. -
- -+ Choose your first community to get started. +
+ +Import your Nostr private key to use that identity with Buzz. If this @@ -138,8 +154,9 @@ export function WelcomeSetup({ } // The parent moves this community into React state so first-run setup - // can continue without a full page reload. - onComplete(community); + // can continue without a full page reload. The source page lets the + // parent's loading gate keep matching the page the user came from. + onComplete(community, page); } catch (err) { setError( err instanceof Error ? err.message : "Failed to connect. Try again.", @@ -147,7 +164,7 @@ export function WelcomeSetup({ setIsConnecting(false); } }, - [onComplete], + [onComplete, page], ); const handleNostrImport = React.useCallback( @@ -327,7 +344,7 @@ export function WelcomeSetup({ >
Communities are where teammates and agents collaborate across @@ -362,7 +379,7 @@ export function WelcomeSetup({ >
Paste an invite link or code from a relay admin to join their diff --git a/desktop/tests/e2e/onboarding.spec.ts b/desktop/tests/e2e/onboarding.spec.ts index 6ee97dba43..bbda990706 100644 --- a/desktop/tests/e2e/onboarding.spec.ts +++ b/desktop/tests/e2e/onboarding.spec.ts @@ -588,6 +588,7 @@ test("first-run default community handoff gives immediate stepper feedback", asy ); await page.waitForTimeout(240); await expect(page.getByTestId("welcome-continue-nostr")).toBeVisible(); + await expect(page.getByTestId("onboarding-connecting-gate")).toHaveCount(0); await expect(page.getByRole("progressbar")).toHaveAttribute( "aria-valuenow", "2", @@ -638,6 +639,54 @@ test("welcome can continue using an existing Nostr key", async ({ page }) => { await expectHomeView(page); }); +test("key import handoff shows a connecting gate, not the welcome replica", async ({ + page, +}) => { + // Delay the profile read so the post-import handoff gate stays on screen + // long enough to assert its contents. + await installMockBridge( + page, + { profileReadDelayMs: 2_000 }, + { + relayWsUrl: "wss://default.example.com", + skipOnboardingSeed: true, + skipCommunitySeed: true, + }, + ); + await page.goto("/"); + + await page.getByTestId("welcome-continue-nostr").click(); + await expect( + page.getByRole("heading", { name: "Use your existing key" }), + ).toBeVisible(); + + const importedNsec = nsecEncode(hexToBytes(TEST_IDENTITIES.alice.privateKey)); + await page.getByTestId("nostr-import-nsec-input").fill(importedNsec); + await expect(page.getByTestId("nostr-import-npub-preview")).toBeVisible(); + await page.getByTestId("nostr-import-submit").click(); + + // The handoff gate reads as the key-import page still loading: same + // heading with forward-motion connecting copy — never the step-1 welcome + // replica, which looks like being kicked back to the start. + const connectingGate = page.getByTestId("onboarding-connecting-gate"); + await expect(connectingGate).toBeVisible(); + await expect( + connectingGate.getByRole("heading", { name: "Use your existing key" }), + ).toBeVisible(); + await expect(connectingGate).toContainText("Connecting to your community…"); + await expect(page.getByRole("progressbar")).toHaveAttribute( + "aria-valuenow", + "2", + ); + await expect(page.getByText("Choose your first community")).toHaveCount(0); + await expect(page.getByTestId("welcome-continue-nostr")).toHaveCount(0); + + // Alice already has a relay profile with a display name, so onboarding + // auto-completes into the app once the handoff settles. + await expect(page.getByTestId("onboarding-gate")).toHaveCount(0); + await expectHomeView(page); +}); + test("welcome presents custom community setup as joining a community", async ({ page, }) => {