1import { useAtomValue } from "@effect/atom-react";
2import type { PendingThreadRequests } from "@t3tools/client-runtime/state/thread-requests";
3import type { EnvironmentThread } from "@t3tools/client-runtime/state/shell";
4import type { EnvironmentId, OrchestrationV2ThreadProjection, ThreadId } from "@t3tools/contracts";
5import { Atom } from "effect/reactivity";
6
7import { environmentThreadDetails, useEnvironmentThread } from "./threads";
8import { useThreadSelection } from "./use-thread-selection";
9
10const EMPTY_THREAD_PROJECTION_ATOM = Atom.make<EnvironmentThread | null>(null).pipe(
11 Atom.withLabel("mobile-thread-projection:empty"),
12);
13const EMPTY_VISIBLE_TURN_ITEMS_ATOM = Atom.make<
14 OrchestrationV2ThreadProjection["visibleTurnItems"]
15>(Object.freeze([])).pipe(Atom.withLabel("mobile-thread-visible-turn-items:empty"));
16
17export interface ThreadDetailTarget {
18 readonly environmentId: EnvironmentId | null;
19 readonly threadId: ThreadId | null;
20}
21
22export function useThreadDetail(target: ThreadDetailTarget) {
23 return useEnvironmentThread(target.environmentId, target.threadId);
24}
25
26/**
27 * The selection owns the subscription so it can hold it back while a queued
28 * creation has not reached the server yet.
29 */
30export function useSelectedThreadDetailState() {
31 const { selectedThreadDetailRef } = useThreadSelection();
32 return useEnvironmentThread(
33 selectedThreadDetailRef?.environmentId ?? null,
34 selectedThreadDetailRef?.threadId ?? null,
35 );
36}
37
38export function useThreadProjection(target: ThreadDetailTarget): EnvironmentThread | null {
39 return useAtomValue(
40 target.environmentId === null || target.threadId === null
41 ? EMPTY_THREAD_PROJECTION_ATOM
42 : environmentThreadDetails.threadAtom({
43 environmentId: target.environmentId,
44 threadId: target.threadId,
45 }),
46 );
47}
48
49export function useSelectedThreadProjection(): EnvironmentThread | null {
50 const { selectedThreadDetailRef } = useThreadSelection();
51 return useThreadProjection({
52 environmentId: selectedThreadDetailRef?.environmentId ?? null,
53 threadId: selectedThreadDetailRef?.threadId ?? null,
54 });
55}
56
57export function useThreadVisibleTurnItems(
58 target: ThreadDetailTarget,
59): OrchestrationV2ThreadProjection["visibleTurnItems"] {
60 return useAtomValue(
61 target.environmentId === null || target.threadId === null
62 ? EMPTY_VISIBLE_TURN_ITEMS_ATOM
63 : environmentThreadDetails.visibleTurnItemsAtom({
64 environmentId: target.environmentId,
65 threadId: target.threadId,
66 }),
67 );
68}
69
70export function useSelectedThreadVisibleTurnItems(): OrchestrationV2ThreadProjection["visibleTurnItems"] {
71 const { selectedThreadDetailRef } = useThreadSelection();
72 return useThreadVisibleTurnItems({
73 environmentId: selectedThreadDetailRef?.environmentId ?? null,
74 threadId: selectedThreadDetailRef?.threadId ?? null,
75 });
76}
77
78const EMPTY_WORKTREE_PATH_ATOM = Atom.make<string | null>(null);
79const EMPTY_PENDING_REQUESTS_ATOM = Atom.make<PendingThreadRequests | null>(null);
80
81export function useSelectedThreadWorktreePath() {
82 const { selectedThreadDetailRef } = useThreadSelection();
83 return useAtomValue(
84 selectedThreadDetailRef === null
85 ? EMPTY_WORKTREE_PATH_ATOM
86 : environmentThreadDetails.worktreePathAtom(selectedThreadDetailRef),
87 );
88}
89
90export function useSelectedThreadPendingRequests() {
91 const { selectedThreadDetailRef } = useThreadSelection();
92 return useAtomValue(
93 selectedThreadDetailRef === null
94 ? EMPTY_PENDING_REQUESTS_ATOM
95 : environmentThreadDetails.pendingRequestsAtom(selectedThreadDetailRef),
96 );
97}