Skip to content
Source
Sign inSign up

threads.ts

113 lines4.6 KB

1import { createChildThreadApprovalsAtom } from "@t3tools/client-runtime/state/thread-requests";
2import { useAtomValue } from "@effect/atom-react";
3import {
4 createEnvironmentThreadDetailAtoms,
5 createEnvironmentThreadShellAtoms,
6 createEnvironmentThreadStateAtoms,
7 EMPTY_ENVIRONMENT_THREAD_STATE,
8 type EnvironmentThreadState,
9 createOlderSettledThreadAtoms,
10 createThreadEnvironmentAtoms,
11 createThreadFamilyAtoms,
12} from "@t3tools/client-runtime/state/threads";
13import { createSteadyShellAtoms } from "@t3tools/client-runtime/state/shell";
14import type { EnvironmentId, ThreadId } from "@t3tools/contracts";
15import { AsyncResult, Atom } from "effect/reactivity";
16​
17import { environmentCatalog } from "../connection/catalog";
18import { connectionAtomRuntime } from "../connection/runtime";
19import { scopedThreadKey } from "../lib/scopedEntities";
20import { appAtomRegistry } from "./atom-registry";
21import {
22 pendingThreadCreationOutcomesAtom,
23 selectThreadCreationDetail,
24} from "./pending-thread-creation";
25import { serverEnvironment } from "./server";
26import { environmentShell } from "./shell";
27import { threadOutboxManager } from "./thread-outbox";
28​
29export const threadFamilies = createThreadFamilyAtoms(
30 connectionAtomRuntime,
31 environmentShell.stateAtom,
32);
33export const olderSettledThreads = createOlderSettledThreadAtoms(
34 connectionAtomRuntime,
35 environmentShell.stateAtom,
36);
37export const threadEnvironment = createThreadEnvironmentAtoms(
38 connectionAtomRuntime,
39 threadFamilies.snapshotAtom,
40);
41export const environmentThreads = createEnvironmentThreadStateAtoms(connectionAtomRuntime);
42export const environmentThreadDetails = createEnvironmentThreadDetailAtoms(
43 environmentThreads.stateAtom,
44);
45export const environmentThreadShells = createEnvironmentThreadShellAtoms({
46 catalogValueAtom: environmentCatalog.catalogValueAtom,
47 snapshotAtom: threadEnvironment.snapshotAtom,
48 hiddenThreadTagsAtom: serverEnvironment.hiddenThreadTagsAtom,
49});
50const steadyShells = createSteadyShellAtoms({
51 catalogValueAtom: environmentCatalog.catalogValueAtom,
52 snapshotAtom: threadEnvironment.snapshotAtom,
53 shellStateAtom: environmentShell.stateValueAtom,
54 rowActionsAtom: threadEnvironment.rowActionsAtom,
55 connectionStateAtom: environmentCatalog.stateAtom,
56});
57/**
58 * The threads that lists arrange. Rows wait while their environment synchronizes, so a list
59 * changes once when environments connect, not once per environment.
60 */
61export const steadyShellsHoldingAtom = steadyShells.holdingAtom;
62export const steadyNavigationThreadShellsAtom = createEnvironmentThreadShellAtoms({
63 catalogValueAtom: environmentCatalog.catalogValueAtom,
64 snapshotAtom: steadyShells.snapshotAtom,
65 hiddenThreadTagsAtom: serverEnvironment.hiddenThreadTagsAtom,
66}).navigationThreadShellsAtom;
67export const childThreadApprovalsAtom = createChildThreadApprovalsAtom({
68 childThreadInputsAtom: environmentThreadShells.childThreadInputsAtom,
69 pendingRequestsAtom: environmentThreadDetails.pendingRequestsAtom,
70});
71​
72// Long enough for the opened screen to subscribe; its subscription keeps the thread after this.
73const OPENING_THREAD_HOLD_MS = 3_000;
74​
75/**
76 * Starts loading a thread being opened before its screen renders and subscribes, which
77 * otherwise waits for navigation to commit.
78 */
79export function startOpeningEnvironmentThread(environmentId: EnvironmentId, threadId: ThreadId) {
80 // A thread whose creation is still queued would load as not found and stay deleted;
81 // the opened screen loads it once creation is delivered.
82 const { detailRef } = selectThreadCreationDetail({
83 threadRef: { environmentId, threadId },
84 threadKey: scopedThreadKey(environmentId, threadId),
85 queuedMessagesByThreadKey: appAtomRegistry.get(
86 threadOutboxManager.queuedMessagesByThreadKeyAtom,
87 ),
88 outcomes: appAtomRegistry.get(pendingThreadCreationOutcomesAtom),
89 });
90 if (detailRef === null) return;
91 const release = appAtomRegistry.mount(environmentThreads.stateAtom(environmentId, threadId));
92 setTimeout(release, OPENING_THREAD_HOLD_MS);
94​
95const EMPTY_THREAD_STATE_ATOM = Atom.make(AsyncResult.success(EMPTY_ENVIRONMENT_THREAD_STATE)).pipe(
96 Atom.withLabel("mobile-environment-thread:empty"),
97);
98​
99export function useEnvironmentThread(
100 environmentId: EnvironmentId | null,
101 threadId: ThreadId | null,
102): EnvironmentThreadState {
103 const result = useAtomValue(
104 environmentId !== null && threadId !== null
105 ? environmentThreads.stateAtom(environmentId, threadId)
106 : EMPTY_THREAD_STATE_ATOM,
107 );
108 const state = AsyncResult.getOrElse(
109 result,
110 () => EMPTY_ENVIRONMENT_THREAD_STATE,
111 ) as EnvironmentThreadState;
112 return state;