1import type {
2 EnvironmentProject,
3 EnvironmentThreadShell,
4} from "@t3tools/client-runtime/state/shell";
5import { EnvironmentId, ThreadId } from "@t3tools/contracts";
6import { useNavigation, useNavigationState } from "@react-navigation/native";
7
8import { scopedThreadKey } from "../../../lib/scopedEntities";
9import { uuidv4 } from "../../../lib/uuid";
10import { usePendingTaskListActions } from "../../home/usePendingTaskListActions";
11import { useAdaptiveWorkspaceLayout, useWorkspacePaneState } from "../adaptive-workspace-context";
12import { openSettings } from "../../settings/settings-navigation";
13import { ThreadNavigationSidebar } from "../../threads/ThreadNavigationSidebar";
14import { useOpenThreadPullRequests } from "../../threads/use-open-thread-pull-requests";
15import { useSelectWorkspaceDetail } from "./useSelectWorkspaceDetail";
16
17type Route = { readonly name: string; readonly params?: object };
18
19function selectedThreadKey(state: { readonly index: number; readonly routes: readonly Route[] }) {
20 const params = state.routes
21 .slice(0, state.index + 1)
22 .findLast((route) => route.name.startsWith("Thread"))?.params;
23 if (
24 !params ||
25 !("environmentId" in params) ||
26 !("threadId" in params) ||
27 typeof params.environmentId !== "string" ||
28 typeof params.threadId !== "string"
29 ) {
30 return null;
31 }
32 try {
33 return scopedThreadKey(
34 EnvironmentId.make(params.environmentId),
35 ThreadId.make(params.threadId),
36 );
37 } catch {
38 return null;
39 }
40}
41
42/** The thread list in the workspace's sidebar column. */
43export function WorkspaceThreadSidebar() {
44 const navigation = useNavigation();
45 const { layout, setPrimarySidebarSearchQuery, revealPrimarySidebar, openWorkspaceTab } =
46 useAdaptiveWorkspaceLayout();
47 const { panes, primarySidebarSearchQuery } = useWorkspacePaneState();
48 const selectDetail = useSelectWorkspaceDetail();
49 const openThreadPullRequests = useOpenThreadPullRequests();
50 const selectedThread = useNavigationState(selectedThreadKey);
51 const selectThreadRoute = (params: ReactNavigation.RootParamList["Thread"]) =>
52 selectDetail({ name: "Thread", params });
53 const { openPendingTask } = usePendingTaskListActions(selectThreadRoute);
54 return (
55 <ThreadNavigationSidebar
56 width={layout.listPaneWidth ?? 0}
57 visible={panes.primarySidebarVisible}
58 onRequestVisibility={revealPrimarySidebar}
59 selectedThreadKey={selectedThread}
60 onOpenSettings={() => openWorkspaceTab("settings")}
61 onOpenEnvironmentSettings={() => openSettings(navigation, "SettingsEnvironments")}
62 onNewThreadInProject={(project: EnvironmentProject) =>
63 selectThreadRoute({
64 draft: {
65 requestId: uuidv4(),
66 environmentId: String(project.environmentId),
67 projectId: String(project.id),
68 title: project.title,
69 },
70 })
71 }
72 onNewThreadOnBranch={(thread: EnvironmentThreadShell) =>
73 selectThreadRoute({
74 draft: {
75 requestId: uuidv4(),
76 environmentId: String(thread.environmentId),
77 projectId: String(thread.projectId),
78 branch: thread.branch,
79 worktreePath: thread.worktreePath,
80 },
81 })
82 }
83 onOpenPendingTask={openPendingTask}
84 onSelectThread={(thread) =>
85 selectThreadRoute({
86 environmentId: String(thread.environmentId),
87 threadId: String(thread.id),
88 })
89 }
90 onOpenThreadPullRequests={openThreadPullRequests}
91 onSearchQueryChange={setPrimarySidebarSearchQuery}
92 searchQuery={primarySidebarSearchQuery}
93 />
94 );
95}