import { useRoute, type RouteProp } from "@react-navigation/native"; import { useMemo, useRef } from "react"; import { EnvironmentId, type OrchestrationThread, ThreadId, type ScopedProjectRef, type ScopedThreadRef, } from "@t3tools/contracts"; import type { EnvironmentThreadShell } from "@t3tools/client-runtime/state/shell"; import * as Option from "effect/Option"; import { useProject, useThreadShell } from "../state/entities"; import { useEnvironmentThread } from "../state/threads"; import { useRemoteEnvironmentRuntime, useSavedRemoteConnection, } from "./use-remote-environment-registry"; type ThreadSelectionRouteParams = { readonly environmentId?: string | string[]; readonly threadId?: string | string[]; }; function firstRouteParam(value: string | string[] | undefined): string | null { if (Array.isArray(value)) { return value[0] ?? null; } return value ?? null; } function latestUserMessageAt(thread: OrchestrationThread): OrchestrationThread["updatedAt"] | null { for (let index = thread.messages.length - 1; index >= 0; index -= 1) { const message = thread.messages[index]; if (message?.role === "user") { return message.createdAt; } } return null; } function threadDetailToShell( environmentId: EnvironmentId, thread: OrchestrationThread, ): EnvironmentThreadShell { return { environmentId, id: thread.id, projectId: thread.projectId, title: thread.title, modelSelection: thread.modelSelection, runtimeMode: thread.runtimeMode, interactionMode: thread.interactionMode, branch: thread.branch, worktreePath: thread.worktreePath, latestTurn: thread.latestTurn, createdAt: thread.createdAt, updatedAt: thread.updatedAt, archivedAt: thread.archivedAt, settledOverride: thread.settledOverride, settledAt: thread.settledAt, snoozedUntil: thread.snoozedUntil ?? null, snoozedAt: thread.snoozedAt ?? null, session: thread.session, latestUserMessageAt: latestUserMessageAt(thread), hasPendingApprovals: false, hasPendingUserInput: false, hasActionableProposedPlan: false, }; } function useResolvedThreadSelection(params: ThreadSelectionRouteParams | undefined) { const routeParams = params ?? {}; const routeThreadRef = useMemo(() => { const environmentId = firstRouteParam(routeParams.environmentId); const threadId = firstRouteParam(routeParams.threadId); if (!environmentId || !threadId) { return null; } return { environmentId: EnvironmentId.make(environmentId), threadId: ThreadId.make(threadId), }; }, [routeParams.environmentId, routeParams.threadId]); const lastRouteThreadRef = useRef(null); if (routeThreadRef !== null) { lastRouteThreadRef.current = routeThreadRef; } const selectedThreadRef = routeThreadRef ?? lastRouteThreadRef.current; const selectedThreadShell = useThreadShell(selectedThreadRef); const selectedThreadDetailState = useEnvironmentThread( selectedThreadRef?.environmentId ?? null, selectedThreadRef?.threadId ?? null, ); const selectedThreadDetail = Option.getOrNull(selectedThreadDetailState.data); const selectedThread = useMemo( () => selectedThreadShell ?? (selectedThreadRef !== null && selectedThreadDetail !== null ? threadDetailToShell(selectedThreadRef.environmentId, selectedThreadDetail) : null), [selectedThreadDetail, selectedThreadRef, selectedThreadShell], ); const selectedProjectRef = useMemo( () => selectedThread === null ? null : { environmentId: selectedThread.environmentId, projectId: selectedThread.projectId, }, [selectedThread], ); const selectedThreadProject = useProject(selectedProjectRef); const selectedEnvironmentId = selectedThread?.environmentId ?? null; const selectedEnvironmentConnection = useSavedRemoteConnection(selectedEnvironmentId); const selectedEnvironmentRuntime = useRemoteEnvironmentRuntime(selectedEnvironmentId); return useMemo( () => ({ selectedThreadRef, selectedThread, selectedThreadProject, selectedEnvironmentConnection, selectedEnvironmentRuntime, }), [ selectedEnvironmentConnection, selectedEnvironmentRuntime, selectedThread, selectedThreadProject, selectedThreadRef, ], ); } type ThreadSelectionState = ReturnType; export function useThreadSelection(): ThreadSelectionState { const route = useRoute>>(); return useResolvedThreadSelection(route.params); }