| 1 | import { createV5StackNavigator as createNativeStackNavigator } from "../../native/createV5StackNavigator"; |
| 2 | import { NavigationContainer, NavigationIndependentTree } from "@react-navigation/native"; |
| 3 | import { type NativeStackNavigationOptions } from "@react-navigation/native-stack"; |
| 4 | import type { ReactNode } from "react"; |
| 5 | import { Platform } from "react-native"; |
| 6 | |
| 7 | import { getCompactBrandHeaderOptions } from "../../components/CompactBrandTitle"; |
| 8 | import { NATIVE_LIQUID_GLASS_SUPPORTED } from "../../native/native-glass"; |
| 9 | import { nativeHeaderScrollEdgeEffects } from "../../native/StackHeader"; |
| 10 | import { useMobileNavigationTheme } from "../../lib/useMobileNavigationTheme"; |
| 11 | |
| 12 | const SCROLL_EDGE_EFFECTS = nativeHeaderScrollEdgeEffects(Platform.OS, Platform.Version); |
| 13 | |
| 14 | type SidebarScreenOptions = NativeStackNavigationOptions & { |
| 15 | // Same patched RNS option the GLASS/SOLID presets in Stack.tsx use — the |
| 16 | // iOS 26 "editor" navigation-item style leading-aligns the inline title. |
| 17 | readonly unstable_navigationItemStyle?: "editor"; |
| 18 | }; |
| 19 | |
| 20 | /** |
| 21 | * Static chrome for the sidebar column: a real UINavigationBar with a fixed |
| 22 | * inline title (no large title — saves vertical space, left-aligned via the |
| 23 | * editor item style) and the search bar pinned below it, scroll-edge blur |
| 24 | * sampling the list. Only genuinely dynamic values (search callbacks, header |
| 25 | * items) are set by the screen content via NativeStackScreenOptions. |
| 26 | */ |
| 27 | const SIDEBAR_SCREEN_OPTIONS: SidebarScreenOptions = { |
| 28 | contentStyle: { backgroundColor: "transparent" }, |
| 29 | headerLargeTitle: false, |
| 30 | headerShadowVisible: false, |
| 31 | headerShown: true, |
| 32 | headerStyle: NATIVE_LIQUID_GLASS_SUPPORTED ? { backgroundColor: "transparent" } : undefined, |
| 33 | ...getCompactBrandHeaderOptions({ fontSize: 18, fontWeight: "800" }), |
| 34 | headerTransparent: NATIVE_LIQUID_GLASS_SUPPORTED, |
| 35 | scrollEdgeEffects: NATIVE_LIQUID_GLASS_SUPPORTED ? SCROLL_EDGE_EFFECTS : undefined, |
| 36 | unstable_navigationItemStyle: NATIVE_LIQUID_GLASS_SUPPORTED ? "editor" : undefined, |
| 37 | }; |
| 38 | |
| 39 | const SidebarStack = createNativeStackNavigator(); |
| 40 | |
| 41 | /** |
| 42 | * Hosts the iPad sidebar pane inside its own single-screen native stack. |
| 43 | * |
| 44 | * The stack is navigation-inert — nothing is ever pushed onto it. It exists so |
| 45 | * the sidebar column owns a real UINavigationBar (large title, native bar |
| 46 | * button items, UISearchController), mirroring how each column of a |
| 47 | * UISplitViewController has its own UINavigationController. All real |
| 48 | * navigation still flows through the root stack via callbacks minted in |
| 49 | * AdaptiveWorkspaceLayout; NavigationIndependentTree only isolates the |
| 50 | * navigation hooks used for header configuration inside the pane. |
| 51 | */ |
| 52 | export function SidebarNavigationShell(props: { readonly children: ReactNode }) { |
| 53 | const navigationTheme = useMobileNavigationTheme("sidebar"); |
| 54 | |
| 55 | return ( |
| 56 | <NavigationIndependentTree> |
| 57 | <NavigationContainer theme={navigationTheme}> |
| 58 | <SidebarStack.Navigator |
| 59 | screenOptions={SIDEBAR_SCREEN_OPTIONS} |
| 60 | initialRouteName="SidebarThreads" |
| 61 | > |
| 62 | <SidebarStack.Screen name="SidebarThreads">{() => props.children}</SidebarStack.Screen> |
| 63 | </SidebarStack.Navigator> |
| 64 | </NavigationContainer> |
| 65 | </NavigationIndependentTree> |
| 66 | ); |
| 67 | } |