From 9f53f25f9498af31caaffcce95b2ff163da5622b Mon Sep 17 00:00:00 2001 From: Matt Toohey Date: Fri, 28 Aug 2026 10:43:42 +1000 Subject: [PATCH] refactor(desktop): extract community destination restore hook AppShell.tsx crossed the 1000-line desktop ratchet again after the forward-history wiring. Move the community destination restore effect into its own hook alongside useCommunityNavigationTransitions instead of squeezing the call site further. Signed-off-by: Matt Toohey --- desktop/src/app/AppShell.tsx | 55 ++---------- .../src/app/useCommunityDestinationRestore.ts | 83 +++++++++++++++++++ 2 files changed, 89 insertions(+), 49 deletions(-) create mode 100644 desktop/src/app/useCommunityDestinationRestore.ts diff --git a/desktop/src/app/AppShell.tsx b/desktop/src/app/AppShell.tsx index de50d19451..5576fb05c6 100644 --- a/desktop/src/app/AppShell.tsx +++ b/desktop/src/app/AppShell.tsx @@ -15,6 +15,7 @@ import { import { useAppNavigation } from "@/app/navigation/useAppNavigation"; import { describeHistoryLocation } from "@/app/navigation/navigationHistory"; import { useBackForwardControls } from "@/app/navigation/useBackForwardControls"; +import { useCommunityDestinationRestore } from "@/app/useCommunityDestinationRestore"; import { useCommunityNavigationTransitions } from "@/app/useCommunityNavigationTransitions"; import { useLiveHomeFeedActions } from "@/app/useLiveHomeFeedActions"; import { useChannelBrowserDialog } from "@/app/useChannelBrowserDialog"; @@ -82,11 +83,6 @@ import { CommunityRail } from "@/features/sidebar/ui/CommunityRail"; import { useChannelMutes } from "@/features/sidebar/lib/useChannelMutes"; import { useChannelStars } from "@/features/sidebar/lib/useChannelStars"; import { useCommunities } from "@/features/communities/useCommunities"; -import { - consumePendingCommunityRestore, - loadCommunityDestination, - saveCommunityDestination, -} from "@/features/communities/communityNavigationStorage"; import { useAddCommunityDialogState } from "@/features/communities/addCommunityPrefill"; import { useApplyTemplate } from "@/features/channel-templates/useApplyTemplate"; import { relayClient } from "@/shared/api/relayClient"; @@ -276,54 +272,15 @@ export function AppShell() { ), [huddleBackingChannelIds, memberChannels, revealedHuddleChannelIds], ); - const hasRestoredCommunityDestinationRef = React.useRef(false); - React.useEffect(() => { - const activeCommunityId = communitiesHook.activeCommunity?.id; - if ( - hasRestoredCommunityDestinationRef.current || - !channelsQuery.isSuccess || - channelsQuery.dataUpdatedAt === 0 || - !activeCommunityId - ) { - return; - } - hasRestoredCommunityDestinationRef.current = true; - - // Restoration belongs to an explicit community transition. Cold boot and - // reconnect remounts must preserve the route the user explicitly opened. - if (!consumePendingCommunityRestore(activeCommunityId)) { - return; - } - - const destination = loadCommunityDestination(activeCommunityId); - if (!destination || destination.kind === "home") { - return; - } - - const channelIsAvailable = sidebarChannels.some( - (channel) => channel.id === destination.channelId, - ); - if (!channelIsAvailable) { - saveCommunityDestination(activeCommunityId, { kind: "home" }); - void goHome({ replace: true }); - return; - } - - // The normal switch path writes the remembered channel into the hash before - // the target community mounts, so no intermediate Inbox frame is painted. - // Older transition callers may still arrive at neutral Home; repair those. - if (selectedView === "home") { - void goChannel(destination.channelId, { replace: true }); - } - }, [ - channelsQuery.dataUpdatedAt, - channelsQuery.isSuccess, - communitiesHook.activeCommunity?.id, + useCommunityDestinationRestore({ + activeCommunityId: communitiesHook.activeCommunity?.id, + channelsDataUpdatedAt: channelsQuery.dataUpdatedAt, + channelsLoaded: channelsQuery.isSuccess, goChannel, goHome, selectedView, sidebarChannels, - ]); + }); const [terminalContextOverride, setTerminalContextOverride] = React.useState(null); const { activeChannel, terminalContext } = useTerminalContext({ diff --git a/desktop/src/app/useCommunityDestinationRestore.ts b/desktop/src/app/useCommunityDestinationRestore.ts new file mode 100644 index 0000000000..502e2ffbbc --- /dev/null +++ b/desktop/src/app/useCommunityDestinationRestore.ts @@ -0,0 +1,83 @@ +import * as React from "react"; + +import type { deriveShellRoute } from "@/app/AppShell.helpers"; +import type { useAppNavigation } from "@/app/navigation/useAppNavigation"; +import { + consumePendingCommunityRestore, + loadCommunityDestination, + saveCommunityDestination, +} from "@/features/communities/communityNavigationStorage"; +import type { Channel } from "@/shared/api/types"; + +type ShellRoute = ReturnType; +type AppNavigation = ReturnType; + +/** + * Restores the channel a community was last viewed on, once for the first + * successful channel load after an explicit community transition. + */ +export function useCommunityDestinationRestore({ + activeCommunityId, + channelsDataUpdatedAt, + channelsLoaded, + goChannel, + goHome, + selectedView, + sidebarChannels, +}: { + activeCommunityId: string | undefined; + channelsDataUpdatedAt: number; + channelsLoaded: boolean; + goChannel: AppNavigation["goChannel"]; + goHome: AppNavigation["goHome"]; + selectedView: ShellRoute["selectedView"]; + sidebarChannels: Channel[]; +}) { + const hasRestoredCommunityDestinationRef = React.useRef(false); + React.useEffect(() => { + if ( + hasRestoredCommunityDestinationRef.current || + !channelsLoaded || + channelsDataUpdatedAt === 0 || + !activeCommunityId + ) { + return; + } + hasRestoredCommunityDestinationRef.current = true; + + // Restoration belongs to an explicit community transition. Cold boot and + // reconnect remounts must preserve the route the user explicitly opened. + if (!consumePendingCommunityRestore(activeCommunityId)) { + return; + } + + const destination = loadCommunityDestination(activeCommunityId); + if (!destination || destination.kind === "home") { + return; + } + + const channelIsAvailable = sidebarChannels.some( + (channel) => channel.id === destination.channelId, + ); + if (!channelIsAvailable) { + saveCommunityDestination(activeCommunityId, { kind: "home" }); + void goHome({ replace: true }); + return; + } + + // The normal switch path writes the remembered channel into the hash before + // the target community mounts, so no intermediate Inbox frame is painted. + // Older transition callers may still arrive at neutral Home; repair those. + if (selectedView === "home") { + void goChannel(destination.channelId, { replace: true }); + } + }, [ + activeCommunityId, + channelsDataUpdatedAt, + channelsLoaded, + goChannel, + goHome, + selectedView, + sidebarChannels, + ]); +}