diff --git a/desktop/src/features/channels/ui/ChannelPane.tsx b/desktop/src/features/channels/ui/ChannelPane.tsx index 13790d779f..410b05a2cc 100644 --- a/desktop/src/features/channels/ui/ChannelPane.tsx +++ b/desktop/src/features/channels/ui/ChannelPane.tsx @@ -216,7 +216,11 @@ export const ChannelPane = React.memo(function ChannelPane({ bannerState: welcomeComposerBannerState, completeBanner: completeWelcomeComposerBanner, dismissBanner: handleDismissWelcomeBanner, - } = useWelcomeComposerBanner(activeChannelId, isActiveWelcomeChannel); + } = useWelcomeComposerBanner( + activeChannelId, + isActiveWelcomeChannel, + currentPubkey ?? null, + ); const isEditInThread = editTarget != null && threadHeadMessage != null && diff --git a/desktop/src/features/channels/ui/WelcomeComposerBanner.tsx b/desktop/src/features/channels/ui/WelcomeComposerBanner.tsx index f050dd4b9f..9a8db082ed 100644 --- a/desktop/src/features/channels/ui/WelcomeComposerBanner.tsx +++ b/desktop/src/features/channels/ui/WelcomeComposerBanner.tsx @@ -443,6 +443,10 @@ export function WelcomeComposerGuidanceLayer({ settingUp, state, }: WelcomeComposerGuidanceLayerProps) { + if (state === "hidden") { + return null; + } + return (
(); + /** * Manages the Welcome-channel composer hint banner's state machine. * - * Tracks which channels have been completed within the session so the banner - * stays hidden on re-entry. Exposes three transitions: + * Remembers completion across the Welcome experience per identity for this app + * session, so the hint stays hidden while moving between the private and + * starter Welcome channels without leaking dismissal to another identity. * - `completeBanner`: agent-mention path — plays the "Nice work." success * animation before auto-dismissing. * - `dismissBanner`: manual X-button path — immediately begins the slide-down @@ -21,12 +24,12 @@ import { export function useWelcomeComposerBanner( activeChannelId: string | null, isActiveWelcomeChannel: boolean, + identityPubkey: string | null, ): { bannerState: WelcomeComposerBannerState; completeBanner: () => void; dismissBanner: () => void; } { - const completedChannelIdsRef = React.useRef(new Set()); const dismissTimerRef = React.useRef(null); const hideTimerRef = React.useRef(null); const [bannerState, setBannerState] = @@ -48,15 +51,15 @@ export function useWelcomeComposerBanner( React.useEffect(() => { clearTimers(); if ( - activeChannelId && isActiveWelcomeChannel && - completedChannelIdsRef.current.has(activeChannelId) + identityPubkey && + completedWelcomeComposerIdentityPubkeys.has(identityPubkey) ) { setBannerState("hidden"); return; } setBannerState("prompt"); - }, [activeChannelId, clearTimers, isActiveWelcomeChannel]); + }, [clearTimers, identityPubkey, isActiveWelcomeChannel]); const scheduleHide = React.useCallback(() => { hideTimerRef.current = window.setTimeout( @@ -70,30 +73,42 @@ export function useWelcomeComposerBanner( }, []); const completeBanner = React.useCallback(() => { - if (!activeChannelId || !isActiveWelcomeChannel) { + if (!activeChannelId || !isActiveWelcomeChannel || !identityPubkey) { return; } clearTimers(); - completedChannelIdsRef.current.add(activeChannelId); + completedWelcomeComposerIdentityPubkeys.add(identityPubkey); setBannerState("complete"); dismissTimerRef.current = window.setTimeout(() => { setBannerState("dismissing"); dismissTimerRef.current = null; scheduleHide(); }, WELCOME_PERSONA_ROTATION_MS + WELCOME_COMPOSER_BANNER_SUCCESS_SETTLE_MS); - }, [activeChannelId, clearTimers, isActiveWelcomeChannel, scheduleHide]); + }, [ + activeChannelId, + clearTimers, + identityPubkey, + isActiveWelcomeChannel, + scheduleHide, + ]); const dismissBanner = React.useCallback(() => { - if (!activeChannelId || !isActiveWelcomeChannel) { + if (!activeChannelId || !isActiveWelcomeChannel || !identityPubkey) { return; } clearTimers(); - completedChannelIdsRef.current.add(activeChannelId); + completedWelcomeComposerIdentityPubkeys.add(identityPubkey); setBannerState("dismissing"); scheduleHide(); - }, [activeChannelId, clearTimers, isActiveWelcomeChannel, scheduleHide]); + }, [ + activeChannelId, + clearTimers, + identityPubkey, + isActiveWelcomeChannel, + scheduleHide, + ]); return { bannerState, completeBanner, dismissBanner }; } diff --git a/desktop/tests/e2e/onboarding.spec.ts b/desktop/tests/e2e/onboarding.spec.ts index 5e0e6a4fa2..403edbcda1 100644 --- a/desktop/tests/e2e/onboarding.spec.ts +++ b/desktop/tests/e2e/onboarding.spec.ts @@ -3218,7 +3218,9 @@ test("welcome-everywhere banner: dismiss persists after channel re-entry", async // Return — banner must stay hidden. await page.getByTestId("channel-welcome-everyone").click(); - await expect(page.getByTestId("chat-title")).toContainText("Welcome"); + await expect(page.getByTestId("chat-title")).toContainText( + "welcome-everyone", + ); await expect(banner).toHaveCount(0); });