()
| 19 | import { useScopedProjectViewSync } from './useScopedProjectViewSync' |
| 20 | |
| 21 | export function useAppShellController() { |
| 22 | const queryClient = useQueryClient() |
| 23 | const [appLaunchedAtMs] = useState(() => Date.now()) |
| 24 | const [state, dispatch] = useReducer(workspaceReducer, [], createInitialWorkspaceState) |
| 25 | const [archivedThreads, setArchivedThreads] = useState<ArchivedThread[]>([]) |
| 26 | const [composerState, setComposerState] = useState<ComposerState | null>(null) |
| 27 | const [liveThreadData, setLiveThreadData] = useState<ThreadData | null>(null) |
| 28 | const [projectGitState, setProjectGitState] = useState<ProjectGitState | null>(null) |
| 29 | const [projectGitLoading, setProjectGitLoading] = useState(false) |
| 30 | const [extensionsProjectScopeActive, setExtensionsProjectScopeActive] = useState(false) |
| 31 | const [skillsProjectScopeActive, setSkillsProjectScopeActive] = useState(false) |
| 32 | const [threadRefreshKey, setThreadRefreshKey] = useState(0) |
| 33 | const [threadHistoryCompactions, setThreadHistoryCompactions] = useState(0) |
| 34 | const [selectedChatGroupId, setSelectedChatGroupId] = useState<string | null>(null) |
| 35 | const [chatSidebarState, setChatSidebarState] = |
| 36 | useState<Awaited<ReturnType<typeof getChatSidebarStateQuery>>>(null) |
| 37 | const [chatSidebarLoading, setChatSidebarLoading] = useState(false) |
| 38 | const { toast, showToast } = useToast() |
| 39 | const { |
| 40 | shellState, |
| 41 | shellLoading, |
| 42 | loadArchivedThreads, |
| 43 | loadComposerState, |
| 44 | listComposerAttachmentEntries, |
| 45 | loadProjectGitState, |
| 46 | loadProjectThreads, |
| 47 | applyProjectOrder, |
| 48 | pickComposerAttachments, |
| 49 | refreshShellState, |
| 50 | scheduleShellStateRefresh, |
| 51 | } = useDesktopShell() |
| 52 | const invokeDesktopAction = useDesktopBridge() |
| 53 | const projects = shellState?.projects ?? [] |
| 54 | const threadQuery = useDesktopThreadQuery( |
| 55 | state.selectedSessionPath, |
| 56 | threadRefreshKey, |
| 57 | threadHistoryCompactions, |
| 58 | ) |
| 59 | const threadData = threadQuery.data ?? null |
| 60 | const selectedPersistedSessionPath = getPersistedSessionPath(state.selectedSessionPath) |
| 61 | const threadDataMatchesSelection = threadData?.sessionPath === selectedPersistedSessionPath |
| 62 | const activeThreadLoading = Boolean( |
| 63 | selectedPersistedSessionPath && |
| 64 | (threadQuery.isLoading || threadQuery.isFetching) && |
| 65 | !(liveThreadData?.sessionPath === selectedPersistedSessionPath || threadDataMatchesSelection), |
| 66 | ) |
| 67 | const effectiveThreadData = |
| 68 | threadHistoryCompactions === 0 && liveThreadData?.sessionPath === state.selectedSessionPath |
| 69 | ? liveThreadData |
| 70 | : threadDataMatchesSelection |
| 71 | ? threadData |
| 72 | : null |
| 73 | const inboxQuery = useDesktopInbox() |
| 74 | const inboxThreads = inboxQuery.data ?? [] |
| 75 | const selectedInboxThread = useMemo( |
| 76 | () => |
| 77 | inboxThreads.find((thread) => thread.sessionPath === state.selectedInboxSessionPath) ?? null, |
| 78 | [inboxThreads, state.selectedInboxSessionPath], |
no test coverage detected