import { useAtomValue } from "@effect/atom-react"; import { useCallback, useMemo, useState } from "react"; import { ApprovalRequestId, type ProviderApprovalDecision } from "@t3tools/contracts"; import { Atom } from "effect/unstable/reactivity"; import { threadEnvironment } from "../state/threads"; import { scopedRequestKey } from "../lib/scopedEntities"; import { buildPendingUserInputAnswers, derivePendingApprovals, derivePendingUserInputs, setPendingUserInputCustomAnswer, type PendingUserInputDraftAnswer, } from "../lib/threadActivity"; import { appAtomRegistry } from "./atom-registry"; import { useSelectedThreadDetail } from "./use-thread-detail"; import { useThreadSelection } from "./use-thread-selection"; import { useAtomCommand } from "./use-atom-command"; const userInputDraftsByRequestKeyAtom = Atom.make< Record> >({}).pipe(Atom.keepAlive, Atom.withLabel("mobile:user-input-drafts")); function setUserInputDraftOption(requestKey: string, questionId: string, label: string): void { const current = appAtomRegistry.get(userInputDraftsByRequestKeyAtom); appAtomRegistry.set(userInputDraftsByRequestKeyAtom, { ...current, [requestKey]: { ...current[requestKey], [questionId]: { selectedOptionLabel: label, }, }, }); } function setUserInputDraftCustomAnswer( requestKey: string, questionId: string, customAnswer: string, ): void { const current = appAtomRegistry.get(userInputDraftsByRequestKeyAtom); appAtomRegistry.set(userInputDraftsByRequestKeyAtom, { ...current, [requestKey]: { ...current[requestKey], [questionId]: setPendingUserInputCustomAnswer( current[requestKey]?.[questionId], customAnswer, ), }, }); } export function useSelectedThreadRequests() { const respondToApproval = useAtomCommand( threadEnvironment.respondToApproval, "thread approval response", ); const respondToUserInput = useAtomCommand( threadEnvironment.respondToUserInput, "thread user input response", ); const { selectedThread: selectedThreadShell } = useThreadSelection(); const selectedThread = useSelectedThreadDetail(); const userInputDraftsByRequestKey = useAtomValue(userInputDraftsByRequestKeyAtom); const [respondingApprovalId, setRespondingApprovalId] = useState(null); const [respondingUserInputId, setRespondingUserInputId] = useState( null, ); const activePendingApprovals = useMemo( () => (selectedThread ? derivePendingApprovals(selectedThread.activities) : []), [selectedThread], ); const activePendingApproval = activePendingApprovals[0] ?? null; const activePendingUserInputs = useMemo( () => (selectedThread ? derivePendingUserInputs(selectedThread.activities) : []), [selectedThread], ); const activePendingUserInput = activePendingUserInputs[0] ?? null; const activePendingUserInputDrafts = activePendingUserInput && selectedThreadShell ? (userInputDraftsByRequestKey[ scopedRequestKey(selectedThreadShell.environmentId, activePendingUserInput.requestId) ] ?? {}) : {}; const activePendingUserInputAnswers = activePendingUserInput ? buildPendingUserInputAnswers(activePendingUserInput.questions, activePendingUserInputDrafts) : null; const onSelectUserInputOption = useCallback( (requestId: ApprovalRequestId, questionId: string, label: string) => { if (!selectedThreadShell) { return; } const requestKey = scopedRequestKey(selectedThreadShell.environmentId, requestId); setUserInputDraftOption(requestKey, questionId, label); }, [selectedThreadShell], ); const onChangeUserInputCustomAnswer = useCallback( (requestId: ApprovalRequestId, questionId: string, customAnswer: string) => { if (!selectedThreadShell) { return; } const requestKey = scopedRequestKey(selectedThreadShell.environmentId, requestId); setUserInputDraftCustomAnswer(requestKey, questionId, customAnswer); }, [selectedThreadShell], ); const onRespondToApproval = useCallback( async (requestId: ApprovalRequestId, decision: ProviderApprovalDecision) => { if (!selectedThreadShell) { return; } setRespondingApprovalId(requestId); const result = await respondToApproval({ environmentId: selectedThreadShell.environmentId, input: { threadId: selectedThreadShell.id, requestId, decision, }, }); setRespondingApprovalId((current) => (current === requestId ? null : current)); return result; }, [respondToApproval, selectedThreadShell], ); const onSubmitUserInput = useCallback(async () => { if (!selectedThreadShell || !activePendingUserInput || !activePendingUserInputAnswers) { return; } setRespondingUserInputId(activePendingUserInput.requestId); const result = await respondToUserInput({ environmentId: selectedThreadShell.environmentId, input: { threadId: selectedThreadShell.id, requestId: activePendingUserInput.requestId, answers: activePendingUserInputAnswers, }, }); setRespondingUserInputId((current) => current === activePendingUserInput.requestId ? null : current, ); return result; }, [ activePendingUserInput, activePendingUserInputAnswers, respondToUserInput, selectedThreadShell, ]); return { activePendingApproval, activePendingUserInput, activePendingUserInputDrafts, activePendingUserInputAnswers, respondingApprovalId, respondingUserInputId, onRespondToApproval, onSelectUserInputOption, onChangeUserInputCustomAnswer, onSubmitUserInput, }; }