import { atom } from 'jotai'; import { atomWithStorage, createJSONStorage } from 'jotai/utils'; import { IContent } from 'matrix-js-sdk'; export type ScheduledMessage = { delayId: string; roomId: string; content: IContent; sendAt: number; // Unix timestamp ms }; const STORAGE_KEY = 'cinny_scheduled_messages_v1'; // Internal atom persists as a plain Record (JSON-serializable). const internalAtom = atomWithStorage>( STORAGE_KEY, {}, createJSONStorage(() => localStorage), ); /** * Global atom: Map * Stores all locally-tracked scheduled messages across rooms. * Loaded from the server's MSC4140 `GET /delayed_events` when the tray mounts * (Gitea #322); localStorage is a cache for the first paint. */ export const scheduledMessagesAtom = atom( (get): Map => new Map(Object.entries(get(internalAtom))), ( _get, set, updater: | Map | ((prev: Map) => Map), ) => { set(internalAtom, (prevObj) => { const prevMap = new Map(Object.entries(prevObj)); const nextMap = typeof updater === 'function' ? updater(prevMap) : updater; return Object.fromEntries(nextMap.entries()); }); }, ); /** * Wipe persisted scheduled messages. Called on logout — the stored content is * decrypted message plaintext (the E2EE `body`), so it must not survive a * session on a shared device. */ export const clearScheduledMessages = (): void => { try { localStorage.removeItem(STORAGE_KEY); } catch { /* localStorage unavailable — nothing to clear */ } };