1import type { EnvironmentThreadShell } from "@t3tools/client-runtime/state/shell";
2import {
3 CommonActions,
4 StackActions,
5 useNavigation,
6 type NavigationState,
7} from "@react-navigation/native";
8import type { NativeStackNavigationProp } from "@react-navigation/native-stack";
9import { useCallback, useEffect, useRef } from "react";
10import { Platform } from "react-native";
11import { useAndroidWorkspaceSelection } from "../workspace/useAndroidWorkspaceSelection";
12
13type ThreadSelection = Pick<EnvironmentThreadShell, "environmentId" | "id">;
14
15export function createHomeThreadNavigationAction(input: {
16 readonly state: Pick<NavigationState, "index" | "routes">;
17 readonly dismissingRouteKey: string | null;
18 readonly thread: ThreadSelection;
19}) {
20 const currentRoute = input.state.routes[input.state.index];
21 const params = {
22 environmentId: input.thread.environmentId,
23 threadId: input.thread.id,
24 };
25
26 // Native swipe-back pops the outgoing route after its animation. Reusing
27 // that key would also discard this selection when the dismissal arrives.
28 if (input.dismissingRouteKey !== null && currentRoute?.key === input.dismissingRouteKey) {
29 return StackActions.push("Thread", params);
30 }
31
32 return CommonActions.navigate("Thread", params);
33}
34
35export function useHomeThreadSelection() {
36 const navigation =
37 useNavigation<NativeStackNavigationProp<ReactNavigation.RootParamList, "Home">>();
38 const dismissingRouteKey = useRef<string | null>(null);
39 const selectAndroidWorkspace = useAndroidWorkspaceSelection();
40
41 useEffect(() => {
42 const clear = () => {
43 dismissingRouteKey.current = null;
44 };
45 // This listener belongs to Home, so swipe-back is its opening transition.
46 // Thread's closing event is targeted at the outgoing Thread route.
47 const removeTransitionStart = navigation.addListener("transitionStart", ({ data }) => {
48 const state = navigation.getState();
49 const currentRoute = state.routes[state.index];
50 dismissingRouteKey.current =
51 !data.closing && currentRoute?.name === "Thread" ? currentRoute.key : null;
52 });
53 const removeFocus = navigation.addListener("focus", clear);
54
55 return () => {
56 clear();
57 removeTransitionStart();
58 removeFocus();
59 };
60 }, [navigation]);
61
62 return useCallback(
63 (thread: ThreadSelection) => {
64 if (Platform.OS === "android") {
65 selectAndroidWorkspace({
66 name: "Thread",
67 params: { environmentId: String(thread.environmentId), threadId: String(thread.id) },
68 });
69 return;
70 }
71 navigation.dispatch((state) =>
72 createHomeThreadNavigationAction({
73 state,
74 dismissingRouteKey: dismissingRouteKey.current,
75 thread,
76 }),
77 );
78 },
79 [navigation, selectAndroidWorkspace],
80 );
81}