()
| 105 | * arrives before folders/tabs hydrate is queued and replayed. |
| 106 | */ |
| 107 | export function PetFocusBridge() { |
| 108 | const { foldersHydrated, folders, addFolderToWorkspaceById } = |
| 109 | useAppWorkspace() |
| 110 | const { tabsHydrated, openTab } = useTabContext() |
| 111 | |
| 112 | const stateRef = useRef({ |
| 113 | foldersHydrated, |
| 114 | folders, |
| 115 | addFolderToWorkspaceById, |
| 116 | tabsHydrated, |
| 117 | openTab, |
| 118 | }) |
| 119 | useEffect(() => { |
| 120 | stateRef.current = { |
| 121 | foldersHydrated, |
| 122 | folders, |
| 123 | addFolderToWorkspaceById, |
| 124 | tabsHydrated, |
| 125 | openTab, |
| 126 | } |
| 127 | }, [ |
| 128 | foldersHydrated, |
| 129 | folders, |
| 130 | addFolderToWorkspaceById, |
| 131 | tabsHydrated, |
| 132 | openTab, |
| 133 | ]) |
| 134 | |
| 135 | // Holds the latest focus request until the workspace has hydrated. The event |
| 136 | // is one-shot, so a pet-panel click during startup/reload (before folders & |
| 137 | // tabs hydrate) must not be dropped — replay it once hydration completes. |
| 138 | const pendingRef = useRef<FocusRequest | null>(null) |
| 139 | |
| 140 | const attempt = useCallback(() => { |
| 141 | const req = pendingRef.current |
| 142 | if (!req) return |
| 143 | const s = stateRef.current |
| 144 | if (!s.foldersHydrated || !s.tabsHydrated) return // wait for hydration |
| 145 | // One-shot after hydration (mirrors DeepLinkBootstrap): clear before the |
| 146 | // async work so a later state change can't double-open. |
| 147 | pendingRef.current = null |
| 148 | void (async () => { |
| 149 | // Ensure the folder is in the workspace so the tab has a home. |
| 150 | if (!s.folders.some((f) => f.id === req.folderId)) { |
| 151 | try { |
| 152 | await s.addFolderToWorkspaceById(req.folderId) |
| 153 | } catch (err) { |
| 154 | console.error("[PetFocusBridge] open folder failed:", err) |
| 155 | return |
| 156 | } |
| 157 | } |
| 158 | // The event is backend-originated for a live session, so the conversation |
| 159 | // exists; open the tab directly and let its title/content hydrate. We do |
| 160 | // NOT gate on the conversations list — it loads independently of folders, |
| 161 | // and waiting on it (without a ready flag) would drop the request. |
| 162 | stateRef.current.openTab( |
| 163 | req.folderId, |
| 164 | req.conversationId, |
nothing calls this directly
no test coverage detected