From aff98d6a348f50ca6b26b33b75635b9ba7dec37c Mon Sep 17 00:00:00 2001 From: Pixel Perfect Date: Sat, 17 Oct 2026 13:35:12 -0700 Subject: [PATCH] fix(android): the sidebar opens threads while New thread is open The sidebar and navigation rail reset the workspace stack with a state-callback dispatch from the root layout. That dispatch goes to the focused navigator, so with the New thread flow open the reset reached its nested stack, which has no Home or Thread route, and was dropped. Address the root stack by its key instead. --- .../AndroidWorkspaceNavigation.android.tsx | 7 ++-- .../workspace/useAndroidWorkspaceSelection.ts | 32 +++++++++++++++++-- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/apps/mobile/src/features/workspace/AndroidWorkspaceNavigation.android.tsx b/apps/mobile/src/features/workspace/AndroidWorkspaceNavigation.android.tsx index addf9718b09dd..734319b000b55 100644 --- a/apps/mobile/src/features/workspace/AndroidWorkspaceNavigation.android.tsx +++ b/apps/mobile/src/features/workspace/AndroidWorkspaceNavigation.android.tsx @@ -1,5 +1,5 @@ import { requireNativeView } from "expo"; -import { CommonActions, useNavigation } from "@react-navigation/native"; +import { useNavigation } from "@react-navigation/native"; import { androidWorkspaceNavigationState } from "../../lib/android-workspace-navigation"; import { Keyboard, @@ -16,6 +16,7 @@ import { androidWorkspaceNavigationColors } from "./android-workspace-navigation import { useAppearancePreferences } from "../settings/appearance/AppearancePreferencesProvider"; import { useWorkspaceTabs } from "./WorkspaceTabsContext"; import type { WorkspaceTab } from "./workspace-tabs-state"; +import { resetAndroidWorkspace } from "./useAndroidWorkspaceSelection"; export const ANDROID_WORKSPACE_RAIL_WIDTH = 80; @@ -81,9 +82,7 @@ export function AndroidWorkspaceNavigation(props: { const tab = event.nativeEvent.tab; if (tab !== workspace.state.selectedTab) { workspace.dispatch({ type: "select", tab }); - navigation.dispatch((state) => - CommonActions.reset(androidWorkspaceNavigationState(state)), - ); + resetAndroidWorkspace(navigation, (state) => androidWorkspaceNavigationState(state)); } props.onSelect?.(); }} diff --git a/apps/mobile/src/features/workspace/useAndroidWorkspaceSelection.ts b/apps/mobile/src/features/workspace/useAndroidWorkspaceSelection.ts index 46f49e9c071e5..76e5c81e5e034 100644 --- a/apps/mobile/src/features/workspace/useAndroidWorkspaceSelection.ts +++ b/apps/mobile/src/features/workspace/useAndroidWorkspaceSelection.ts @@ -1,4 +1,9 @@ -import { CommonActions, useNavigation } from "@react-navigation/native"; +import { + CommonActions, + useNavigation, + type NavigationAction, + type NavigationState, +} from "@react-navigation/native"; import { useCallback } from "react"; import { Keyboard } from "react-native"; import { @@ -7,6 +12,27 @@ import { } from "../../lib/android-workspace-navigation"; import { useWorkspaceTabs } from "./WorkspaceTabsContext"; +/** + * The sidebar and navigation rail sit in the root layout, where a state-callback dispatch goes + * to the focused navigator. While a nested flow such as New thread has focus, that navigator has + * no Home or Thread route and drops the reset, so address the root stack by its key. + */ +interface WorkspaceNavigation { + getParent(): WorkspaceNavigation | undefined; + getState(): NavigationState | undefined; + dispatch(action: NavigationAction): void; +} + +export function resetAndroidWorkspace( + navigation: WorkspaceNavigation, + next: (state: NavigationState) => Parameters[0], +) { + let root = navigation; + for (let parent = root.getParent(); parent; parent = parent.getParent()) root = parent; + const state = root.getState(); + if (state) root.dispatch({ ...CommonActions.reset(next(state)), target: state.key }); +} + export function useAndroidWorkspaceSelection() { const navigation = useNavigation(); const dispatch = useWorkspaceTabs()?.dispatch; @@ -20,8 +46,8 @@ export function useAndroidWorkspaceSelection() { type: "select", tab: destination.name === "Thread" ? "threads" : "pullRequests", }); - navigation.dispatch((state) => - CommonActions.reset(androidWorkspaceNavigationState(state, destination, reviewers)), + resetAndroidWorkspace(navigation, (state) => + androidWorkspaceNavigationState(state, destination, reviewers), ); }, [navigation, dispatch],