import type { ConsentInstrumentId } from '../config/consentInstruments.ts'; // A practice agreement wording save sent with no answer yet, per practice and document. It may have made a new version, // and nothing on the client can prove it no longer will: no clock does, and a lost connection does not cancel the // write, and a read showing its wording served does not say which request served it. So no editor offers another save // of that document until the request's own answer arrives. Kept for the browser session, so a reload is not taken as a // failure. export interface UnconfirmedAgreementSave { attempt: string; /** The version the editor held when it saved, and the wording it sent. */ fromVersion?: number; text: string; /** The server answered this very request, though nobody was left on screen to be told. */ answer?: 'saved' | 'refused'; } const STORAGE_KEY = 'akashic.unconfirmedAgreementSaves'; // Where session storage cannot be used (blocked, private mode), the tab's memory still holds the barrier. let fallback: Record = {}; const readAll = (): Record => { try { const raw = window.sessionStorage.getItem(STORAGE_KEY); return raw ? { ...fallback, ...JSON.parse(raw) } : { ...fallback }; } catch { return { ...fallback }; } }; const writeAll = (all: Record) => { fallback = all; try { window.sessionStorage.setItem(STORAGE_KEY, JSON.stringify(all)); } catch { // The in-memory copy above stands in. } }; const keyFor = (organizationId: string | undefined, instrument: ConsentInstrumentId) => `${organizationId ?? ''}|${instrument}`; export const unconfirmedAgreementSave = (organizationId: string | undefined, instrument: ConsentInstrumentId): UnconfirmedAgreementSave | undefined => readAll()[keyFor(organizationId, instrument)]; /** Recorded as the request is sent; the returned attempt is what its answer is noted against. */ export const recordUnconfirmedAgreementSave = ( organizationId: string | undefined, instrument: ConsentInstrumentId, fromVersion: number | undefined, text: string, ): string => { const attempt = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`; writeAll({ ...readAll(), [keyFor(organizationId, instrument)]: { attempt, fromVersion, text } }); return attempt; }; export const noteAgreementSaveAnswer = ( organizationId: string | undefined, instrument: ConsentInstrumentId, attempt: string, answer: 'saved' | 'refused', ): void => { const all = readAll(); const key = keyFor(organizationId, instrument); if (all[key]?.attempt === attempt) writeAll({ ...all, [key]: { ...all[key], answer } }); }; export const settleUnconfirmedAgreementSave = (organizationId: string | undefined, instrument: ConsentInstrumentId): void => { const all = readAll(); delete all[keyFor(organizationId, instrument)]; writeAll(all); }; // Tests only: the barrier outlives a test the same way it outlives an editor. export const forgetUnconfirmedAgreementSaves = (): void => { writeAll({}); };