| 1 | import { useCallback, type Dispatch, type MutableRefObject, type SetStateAction } from "react"; |
| 2 | import { app } from "./bridge"; |
| 3 | import type { TabMeta } from "./types"; |
| 4 | |
| 5 | type RemoteTabSwitchOptions = { |
| 6 | activeTabIdRef: MutableRefObject<string | undefined>; |
| 7 | setActiveTabId: Dispatch<SetStateAction<string | undefined>>; |
| 8 | beginNavigation: () => number; |
| 9 | requireRegisteredNavigation: (seq: number) => Promise<void>; |
| 10 | navigationCanComplete: (seq: number, kind: string, tabId: string) => boolean; |
| 11 | navigationIsCurrent: (seq: number) => boolean; |
| 12 | confirmBackendActiveTab: (tabId: string) => void; |
| 13 | reassertVisibleTab: (kind: string, staleTabId: string) => Promise<void>; |
| 14 | }; |
| 15 | |
| 16 | // Remote surfaces hydrate from Serve events, not the local session history API. |
| 17 | export function useRemoteTabSwitch(options: RemoteTabSwitchOptions) { |
| 18 | const { |
| 19 | activeTabIdRef, setActiveTabId, beginNavigation, requireRegisteredNavigation, navigationCanComplete, |
| 20 | navigationIsCurrent, confirmBackendActiveTab, reassertVisibleTab, |
| 21 | } = options; |
| 22 | return useCallback(async (meta: TabMeta, navigationIntentSeq?: number): Promise<void> => { |
| 23 | const tabId = meta.id; |
| 24 | const navigationSeq = navigationIntentSeq ?? beginNavigation(); |
| 25 | await requireRegisteredNavigation(navigationSeq); |
| 26 | if (!meta.remote || !navigationCanComplete(navigationSeq, "tab.switch-remote", tabId)) return; |
| 27 | const previousTabId = activeTabIdRef.current; |
| 28 | setActiveTabId(tabId); |
| 29 | activeTabIdRef.current = tabId; |
| 30 | try { |
| 31 | await app.SetActiveTab(tabId); |
| 32 | if (!navigationIsCurrent(navigationSeq) || activeTabIdRef.current !== tabId) { |
| 33 | await reassertVisibleTab("tab.switch-remote", tabId); |
| 34 | return; |
| 35 | } |
| 36 | confirmBackendActiveTab(tabId); |
| 37 | } catch (error) { |
| 38 | if (navigationIsCurrent(navigationSeq) && activeTabIdRef.current === tabId && previousTabId) { |
| 39 | setActiveTabId(previousTabId); |
| 40 | activeTabIdRef.current = previousTabId; |
| 41 | } |
| 42 | throw error; |
| 43 | } |
| 44 | }, [activeTabIdRef, beginNavigation, confirmBackendActiveTab, navigationCanComplete, navigationIsCurrent, reassertVisibleTab, requireRegisteredNavigation, setActiveTabId]); |
| 45 | } |
| 46 |