import { writable } from 'svelte/store'; import { api } from './api'; import { setRole } from './role-store'; import type { MeContextDto } from './types'; /** * Live event lock/release state, so the composer can reflect a close/reopen the *instant* * it happens instead of a guest discovering the lock via a rejected upload. Seeded from * `/me/context` on boot and kept current by the `event-closed`/`event-opened` SSE events * (see the root layout, which subscribes once). */ export interface EventState { uploadsLocked: boolean; galleryReleased: boolean; } export const eventState = writable({ uploadsLocked: false, galleryReleased: false }); // Monotonic sequence bumped by every synchronous state transition (markClosed/markOpened). // `refreshEventState` captures it before its async fetch and only applies the result if no // newer transition happened meanwhile — so a slow `/me/context` reflecting a pre-reopen // snapshot can't clobber a subsequent `event-opened` with stale `locked=true`. let stateSeq = 0; /** True when uploads are closed for any reason (event locked or gallery released). */ export function uploadsClosed(s: EventState): boolean { return s.uploadsLocked || s.galleryReleased; } /** Refresh from the server. Non-fatal on failure — the next SSE event reconciles. */ export async function refreshEventState(): Promise { const seq = stateSeq; try { const ctx = await api.get('/me/context'); // The role is unaffected by the close/reopen race guarded below, so apply it // unconditionally — this is one of the refreshes that used to drop it. setRole(ctx.role); // A close/reopen landed while this was in flight — its result is now authoritative; // don't overwrite it with our possibly-stale snapshot. if (seq !== stateSeq) return; eventState.set({ uploadsLocked: ctx.uploads_locked, galleryReleased: ctx.gallery_released }); } catch { // non-fatal } } /** Apply an `event-closed` SSE event (uploads just locked). */ export function markClosed(): void { stateSeq++; eventState.update((s) => ({ ...s, uploadsLocked: true })); } /** Apply an `event-opened` SSE event (uploads reopened → release also cleared). */ export function markOpened(): void { stateSeq++; eventState.set({ uploadsLocked: false, galleryReleased: false }); }