import { SaveRetiredError } from './saveOperation'; /** * What an open referral dialog hands its requests: admitted before it is prepared and again just before it is sent, * cancelled when the dialog's opening retires, and pinned to the practice it was opened in. */ export interface ReferralRequestScope { admit: () => void; signal: AbortSignal; organizationId?: string; onSend?: () => void; } /** Whether a dialog's opening still holds: false once it has retired, so a late answer shows and starts nothing. */ export const stillOpen = (scope?: ReferralRequestScope): boolean => { if (!scope) return true; try { scope.admit(); return true; } catch { return false; } }; /** * One action under an opening: admitted only while the opening holds and the action is still allowed, checked before * the request is prepared, just before it is sent and before its answer is used. The first failed check retires it for * good, so regaining the permission does not bring this attempt back; a new action may run. */ export interface ActionAttempt extends ReferralRequestScope { /** Retire it now, for good: called when its permission is seen to go, so a later return does not revive it. */ retire: () => void; } export const actionAttempt = (opening: ReferralRequestScope | undefined, allowed: () => boolean): ActionAttempt => { const controller = new AbortController(); let retired = false; const retire = () => { retired = true; controller.abort(); }; opening?.signal.addEventListener('abort', retire, { once: true }); const admit = () => { if (!retired && (!stillOpen(opening) || !allowed())) retire(); if (retired) throw new SaveRetiredError(); }; return { admit, retire, signal: controller.signal, organizationId: opening?.organizationId }; }; /** A signal aborted as soon as either given one is. */ export const eitherAborted = (a: AbortSignal, b: AbortSignal): AbortSignal => { const controller = new AbortController(); const stop = () => controller.abort(); if (a.aborted || b.aborted) { stop(); } else { a.addEventListener('abort', stop, { once: true }); b.addEventListener('abort', stop, { once: true }); } return controller.signal; };