// The encounter page's finish, reachable from inside its call. On a phone the call covers the page, so // the host finishes from the call (practitioner#s-video-visit board 15); it must be the page's own finish // - end the call, then flip the encounter - never a second implementation of that order. import { useSyncExternalStore } from 'react'; import type { FinishRefusal } from '../../services/scribe/recordingDisposition'; /** Resolves with a refusal when the visit cannot finish yet; existing void finishers stay valid. */ export type EncounterFinisher = () => void | Promise; const finishers = new Map(); const listeners = new Set<() => void>(); const notify = () => listeners.forEach((listener) => listener()); const subscribe = (listener: () => void) => { listeners.add(listener); return () => { listeners.delete(listener); }; }; /** Registers the finish for a call context (`encounter#`); the returned function withdraws it. */ export const registerEncounterFinisher = (context: string, finisher: EncounterFinisher): (() => void) => { finishers.set(context, finisher); notify(); return () => { if (finishers.get(context) === finisher) finishers.delete(context); notify(); }; }; /** The finish registered for this call's context, if its encounter page is mounted. */ export const useEncounterFinisher = (context: string | undefined): EncounterFinisher | undefined => useSyncExternalStore(subscribe, () => (context ? finishers.get(context) : undefined));