import AsyncStorage from '@react-native-async-storage/async-storage'; import { useSyncExternalStore } from 'react'; import type { ChecklistState, ReportUpsert, WorkOrderStatus } from '@siop/shared'; /** La file d'écriture (D1) : chaque geste hors-ligne devient une saisie * datée, persistée dans AsyncStorage — elle survit au redémarrage — et * rejouée DANS L'ORDRE au retour du réseau. Chaque saisie porte la version * de l'OT lue au moment du geste (D2, verrou optimiste). */ export type Saisie = { id: string; creeA: string; // ISO — affiché à l'écran Synchro otId: string; otReference: string; libelle: string; // « Coche “Jeu des coulisseaux” » baseUpdatedAt: string; // version lue (D2) statut: 'EN_ATTENTE' | 'ENVOI' | 'CONFLIT'; erreur?: string; // message du 409 / refus métier — montré tel quel } & ( | { type: 'TRANSITION'; payload: { to: WorkOrderStatus } } | { type: 'COCHE'; payload: { itemId: string; state: ChecklistState } } | { type: 'BILAN'; payload: Omit } | { type: 'PHOTO'; payload: { uri: string; nom: string; mime: string } } ); const CLE = 'siop.file.v1'; let file: Saisie[] = []; const abonnes = new Set<() => void>(); function notifier(): void { for (const a of abonnes) a(); void AsyncStorage.setItem(CLE, JSON.stringify(file)); } /** À l'ouverture de l'app : la file survit au redémarrage (D1). */ export async function chargerFile(): Promise { const brut = await AsyncStorage.getItem(CLE); if (brut) { // Un ENVOI interrompu par un crash redevient EN_ATTENTE (rejouable). file = (JSON.parse(brut) as Saisie[]).map((s) => s.statut === 'ENVOI' ? { ...s, statut: 'EN_ATTENTE' } : s, ); for (const a of abonnes) a(); } } export const lireFile = (): Saisie[] => file; export function enfiler(saisie: Omit): Saisie { const complete = { ...saisie, id: `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`, creeA: new Date().toISOString(), statut: 'EN_ATTENTE', } as Saisie; file = [...file, complete]; notifier(); return complete; } export function majSaisie(id: string, patch: Partial>): void { file = file.map((s) => (s.id === id ? ({ ...s, ...patch } as Saisie) : s)); notifier(); } export function retirer(id: string): void { file = file.filter((s) => s.id !== id); notifier(); } export function useFile(): Saisie[] { return useSyncExternalStore( (cb) => { abonnes.add(cb); return () => abonnes.delete(cb); }, lireFile, lireFile, ); } /** Déconnexion : la file appartient à la SESSION — on la purge (sécurité : * un autre compte sur le même téléphone ne doit jamais rejouer les saisies * du précédent), avec sa persistance. */ export function viderFile(): void { file = []; notifier(); } /** Réservé aux tests : repartir d'une file vide (sans persistance). */ export function _viderPourTests(): void { file = []; }