import { useEffect, useRef, useState } from "react"; import { ExternalLink, Loader2 } from "lucide-react"; import { useReferralWorkbenchService, UnreadableFaxOriginalError } from "../../../services/ReferralWorkbenchService"; import { eitherAborted, stillOpen, type ReferralRequestScope } from "../../../utils/referralRequestScope"; import { relayServerFailure } from "../../../utils/fetchFailureMessage"; import { isSaveRetired, useSaveScope } from "../../../utils/saveOperation"; import { viewerEpoch } from "../../../utils/writeThroughHold"; /** Who a prepared link was issued to: this opening, this person signed in. Only they may follow it. */ type Issued = { id: number; scope?: ReferralRequestScope; epoch: number; until?: number }; let issues = 0; type Phase = | { at: "idle" } | { at: "opening" } | { at: "opened" } | { at: "link"; url: string; issued: Issued } | { at: "failed"; message: string }; export type ReferralFaxOriginalProps = { referralId: string; scope?: ReferralRequestScope; /** The route authority as this practice's capabilities read it; the server decides again on every open. */ allowed: boolean; disabled?: boolean; }; const NOTHING_OPENED = "Nothing was opened."; /** What a refused or unreadable open says. None of these reads as an empty fax or as a way around the check. */ const refusalOf = (failure: unknown): string => { if (failure instanceof UnreadableFaxOriginalError) return `The original could not be opened. ${NOTHING_OPENED}`; const status = (failure as { status?: unknown } | null | undefined)?.status; const scan = ((failure as { body?: { document?: unknown } } | null | undefined)?.body)?.document; if (status === 409) { if (scan === "held") return `This fax is still being checked for viruses. Try again in a few minutes. ${NOTHING_OPENED}`; if (scan === "blocked") return `This fax did not pass the virus check, so it cannot be opened. ${NOTHING_OPENED}`; if (scan === "unscannable") return `This fax could not be checked for viruses, so it cannot be opened. ${NOTHING_OPENED}`; return `This fax is not ready to open. ${NOTHING_OPENED}`; } if (status === 404) return `This fax's original is not available here. ${NOTHING_OPENED}`; return relayServerFailure(failure, `The original could not be opened just now. ${NOTHING_OPENED}`); }; /** * Opens a received fax's original as it arrived, through the server's audited link. It reads nothing from the * fax and changes nothing on the referral; reading the fax into suggestions stays its own action. */ export const ReferralFaxOriginal = ({ referralId, scope, allowed, disabled }: ReferralFaxOriginalProps) => { const { openReferralFaxOriginal } = useReferralWorkbenchService(); const [phase, setPhase] = useState({ at: "idle" }); const expiry = useRef>(); const allowedNow = useRef(allowed); allowedNow.current = allowed; // A change of item, of authority or of the person signed in retires an open under way and withdraws its link. const lives = useSaveScope(`${referralId}|${allowed ? "may" : "may-not"}`, () => { clearTimeout(expiry.current); setPhase({ at: "idle" }); }); const scopeNow = useRef(scope); scopeNow.current = scope; const mounted = useRef(true); useEffect(() => { mounted.current = true; return () => { mounted.current = false; clearTimeout(expiry.current); }; }, []); // The issuing owner, checked again at the moment the link is followed: an old opening's link is never followed // under a new one. const current = (issued: Issued) => mounted.current && issued.scope === scopeNow.current && stillOpen(issued.scope) && viewerEpoch() === issued.epoch && allowedNow.current && issued.until !== undefined && Date.now() < issued.until; const withdraw = (issued: Issued) => setPhase((now) => (now.at === "link" && now.issued === issued ? { at: "idle" } : now)); useEffect(() => { if (phase.at === "link" && phase.issued.scope !== scope) withdraw(phase.issued); }, [scope, phase]); // A native listener stays on the anchor even after it leaves the page, so a click that races its removal is // still refused. const guarded = useRef(new WeakSet()); const guard = (issued: Issued) => (anchor: HTMLAnchorElement | null) => { if (!anchor || guarded.current.has(anchor)) return; guarded.current.add(anchor); anchor.addEventListener("click", (event) => { if (current(issued)) return; event.preventDefault(); withdraw(issued); }); }; if (!allowed) return null; const open = async () => { if (!stillOpen(scope) || !allowedNow.current) return; clearTimeout(expiry.current); // Opened before the await, while the click still counts as the user's; closed unless this open lands in it. const tab = window.open("", "_blank"); if (tab) tab.opener = null; let landed = false; const issued: Issued = { id: ++issues, scope, epoch: viewerEpoch() }; setPhase({ at: "opening" }); const operation = lives.start(); const owned: ReferralRequestScope = { admit: () => { operation.admit(); scope?.admit(); if (!allowedNow.current) throw new Error("The permission to open this fax changed."); }, signal: scope ? eitherAborted(operation.transport.signal, scope.signal) : operation.transport.signal, organizationId: scope?.organizationId, }; try { const original = await operation.step(() => openReferralFaxOriginal(referralId, owned)); if (!operation.belongs() || !stillOpen(scope) || !allowedNow.current) return; if (tab && !tab.closed) { tab.location.href = original.url; landed = true; setPhase({ at: "opened" }); return; } // No tab could be opened: the link stays here, for this opening only, until the server's link lapses. issued.until = Date.now() + original.expiresIn * 1000; setPhase({ at: "link", url: original.url, issued }); scope?.signal.addEventListener("abort", () => withdraw(issued), { once: true }); expiry.current = setTimeout(() => withdraw(issued), original.expiresIn * 1000); } catch (failure: unknown) { if (isSaveRetired(failure) || !operation.belongs() || !stillOpen(scope) || !allowedNow.current) return; setPhase({ at: "failed", message: refusalOf(failure) }); } finally { if (!landed) tab?.close(); operation.retire(); } }; return (
{phase.at === "opening" ? (

Opening the original.

) : phase.at === "link" && current(phase.issued) ? (

Your browser did not open a new tab.{" "} Open the original {" "}This link works for a few minutes.

) : (

{phase.at === "failed" ? phase.message : phase.at === "opened" ? "The original opened in a new tab." : "See the fax as it arrived. Opening it reads nothing from it and changes nothing."}

)} {phase.at !== "opening" && !(phase.at === "link" && current(phase.issued)) && ( )}
); }; export default ReferralFaxOriginal;