import { useMemo } from "react"; import { environment } from "../environments/environment"; import { useAuthServiceContext } from "../services"; import { newSendOperation, sendUnconfirmed, type SendOperation } from "../utils/messaging/sendOperation"; /** What a sender says when its answer was lost: the words are kept and a resend cannot duplicate them. */ export const MAY_HAVE_BEEN_SENT = "It may have been sent. Sending the same words again will not send them twice."; // Outlives the screen that sent, so the same words resent after closing and reopening it still go under one name. const held = new Map(); /** How one named send ended: its own success, an exact answer that it was or was not sent, or anything else. */ export type HeldOutcome = "owned-success" | "closed" | "held"; const heldFor = (signer: string) => ({ take: (key: string): SendOperation => { const slot = JSON.stringify([signer, key]); const earlier = held.get(slot); if (earlier) return earlier; const send = newSendOperation(); held.set(slot, send); return send; }, /** Settles one attempt; true when it may have been written and its name is kept for the retry. */ settle: (key: string, error?: unknown): boolean => { const unsure = error !== undefined && sendUnconfirmed(error); if (!unsure) held.delete(JSON.stringify([signer, key])); return unsure; }, /** * Settles exactly the send named by its original operation id; true when it is released. Only its own success or * an exact close answer releases it, so a later send's answer never clears an earlier one. */ settleOutcome: (key: string, originalOperationId: string, outcome: HeldOutcome): boolean => { if (outcome === "held") return false; const slot = JSON.stringify([signer, key]); if (held.get(slot)?.operationId === originalOperationId) held.delete(slot); return true; }, /** Forgets exactly this send once Check has settled it, wherever its words were held. */ forget: (operationId: string) => { for (const [slot, send] of held) if (send.operationId === operationId) held.delete(slot); }, }); /** * The name each draft's send goes out under, keyed by who is signed in (and for whom), at which practice and data * stack, where it goes and what it says. The same words sent again after an unconfirmed attempt reuse it, so the * server returns the first attempt's message instead of writing a second; a confirmed send or a definite refusal lets * the next one start fresh. A send settles under the signer it began with, so another sign-in can never retire it. */ export const useHeldSendOperations = () => { const { currentUser, actingAs, currentOrganization } = useAuthServiceContext(); const signer = JSON.stringify([currentUser?.fhir?.resourceType, currentUser?.fhir?.id, actingAs?.subjectPatientId, currentOrganization?.id ?? "orgless", environment.fhirClinicalApi]); return useMemo(() => heldFor(signer), [signer]); };