Skip to content
Source
Sign inSign up
Open History to see the last commit

thread-preview-route.tsx

100 lines3.1 KB

1import {
2 createNavigatorFactory,
3 NavigationContainer,
4 NavigationIndependentTree,
5 StackRouter,
6 useNavigation,
7 useNavigationBuilder,
8 type NavigatorTypeBagBase,
9 type ParamListBase,
10 type RouteProp,
11 type StackActionHelpers,
12 type StackNavigationState,
13 type StackRouterOptions,
14 type StaticConfig,
15 type TypedNavigator,
16} from "@react-navigation/native";
17import type {
18 NativeStackNavigationEventMap,
19 NativeStackNavigationOptions,
20 NativeStackNavigatorProps,
21 NativeStackTypeBag,
22} from "@react-navigation/native-stack";
23import type { ReactNode } from "react";
24​
25import { useMobileNavigationTheme } from "../../lib/useMobileNavigationTheme";
26​
27function ThreadPreviewNavigator({
28 id,
29 initialRouteName,
30 UNSTABLE_routeNamesChangeBehavior,
31 children,
32 layout,
33 screenListeners,
34 screenOptions,
35 screenLayout,
36 UNSTABLE_router,
37}: NativeStackNavigatorProps) {
38 const { state, descriptors, NavigationContent } = useNavigationBuilder<
39 StackNavigationState<ParamListBase>,
40 StackRouterOptions,
41 StackActionHelpers<ParamListBase>,
42 NativeStackNavigationOptions,
43 NativeStackNavigationEventMap
44 >(StackRouter, {
45 id,
46 initialRouteName,
47 UNSTABLE_routeNamesChangeBehavior,
48 children,
49 layout,
50 screenListeners,
51 screenOptions,
52 screenLayout,
53 UNSTABLE_router,
54 });
55 const route = state.routes[state.index];
56 return <NavigationContent>{route ? descriptors[route.key]?.render() : null}</NavigationContent>;
58​
59type ThreadPreviewTypeBag<ParamList extends ParamListBase> = Omit<
60 NativeStackTypeBag<ParamList, undefined>,
61 "Navigator"
62> & { Navigator: typeof ThreadPreviewNavigator };
63​
64function createThreadPreviewNavigator<
65 const ParamList extends ParamListBase,
66 const TypeBag extends NavigatorTypeBagBase = ThreadPreviewTypeBag<ParamList>,
67 const Config extends StaticConfig<TypeBag> = StaticConfig<TypeBag>,
68>(config?: Config): TypedNavigator<TypeBag, Config> {
69 return createNavigatorFactory(ThreadPreviewNavigator)(config);
71​
72type ThreadPreviewParams = { readonly environmentId: string; readonly threadId: string };
73​
74const Preview = createThreadPreviewNavigator<{ Thread: ThreadPreviewParams }>();
75​
76/**
77 * A `Thread` route of its own for a thread shown inside another screen. Thread state resolves the
78 * thread from route params, so the preview needs its own params, and its `replace` must not swap
79 * the host thread. Draws no native UI; actions it can't handle go to the host.
80 */
81export function ThreadPreviewRoute(
82 props: ThreadPreviewParams & {
83 readonly children: (route: RouteProp<{ Thread: ThreadPreviewParams }, "Thread">) => ReactNode;
84 },
85) {
86 const host = useNavigation();
87 const theme = useMobileNavigationTheme();
88 const { environmentId, threadId } = props;
89 return (
90 <NavigationIndependentTree>
91 <NavigationContainer theme={theme} onUnhandledAction={host.dispatch}>
92 <Preview.Navigator key={`${environmentId}:${threadId}`}>
93 <Preview.Screen name="Thread" initialParams={{ environmentId, threadId }}>
94 {(screen) => props.children(screen.route)}
95 </Preview.Screen>
96 </Preview.Navigator>
97 </NavigationContainer>
98 </NavigationIndependentTree>
99 );