import { STORAGE_KEY_GENERAL_SETTINGS, STORAGE_KEY_LAST_VIEWED_CHAT } from '../../core/constants'; import { getUserScopedStorageKey } from '../../core/storage/current-user-storage'; import { runWhenIdle } from '../../shared/rxjs'; export interface GeneralSettings { reopenLastViewedChat: boolean; } export const DEFAULT_GENERAL_SETTINGS: GeneralSettings = { reopenLastViewedChat: true }; export interface LastViewedChatSnapshot { userId: string; roomId: string; channelId: string | null; } const pendingWrites = new Map(); let flushScheduled = false; let cancelScheduledFlush: (() => void) | null = null; function scheduleStorageFlush(): void { if (flushScheduled) { return; } flushScheduled = true; cancelScheduledFlush = runWhenIdle(() => { flushScheduled = false; cancelScheduledFlush = null; const snapshot = Array.from(pendingWrites.entries()); pendingWrites.clear(); for (const [key, value] of snapshot) { try { if (value === null) { localStorage.removeItem(key); } else { localStorage.setItem(key, value); } } catch { // storage not available } } }); } function scheduleStorageWrite(key: string, serialised: string): void { pendingWrites.set(key, serialised); scheduleStorageFlush(); } function scheduleStorageRemove(key: string): void { pendingWrites.set(key, null); scheduleStorageFlush(); } function readMaybePending(key: string): string | null { if (pendingWrites.has(key)) { return pendingWrites.get(key) ?? null; } try { return localStorage.getItem(key); } catch { return null; } } export function loadGeneralSettingsFromStorage(): GeneralSettings { try { const raw = readMaybePending(getUserScopedStorageKey(STORAGE_KEY_GENERAL_SETTINGS)) ?? readMaybePending(STORAGE_KEY_GENERAL_SETTINGS); if (!raw) { return { ...DEFAULT_GENERAL_SETTINGS }; } return normaliseGeneralSettings(JSON.parse(raw) as Partial); } catch { return { ...DEFAULT_GENERAL_SETTINGS }; } } export function saveGeneralSettingsToStorage(patch: Partial): GeneralSettings { const nextSettings = normaliseGeneralSettings({ ...loadGeneralSettingsFromStorage(), ...patch }); scheduleStorageWrite(getUserScopedStorageKey(STORAGE_KEY_GENERAL_SETTINGS), JSON.stringify(nextSettings)); return nextSettings; } export function loadLastViewedChatFromStorage(userId?: string | null): LastViewedChatSnapshot | null { try { const raw = readMaybePending(getUserScopedStorageKey(STORAGE_KEY_LAST_VIEWED_CHAT, userId)) ?? readMaybePending(STORAGE_KEY_LAST_VIEWED_CHAT); if (!raw) { return null; } const snapshot = normaliseLastViewedChatSnapshot(JSON.parse(raw) as Partial); if (!snapshot) { return null; } if (userId && snapshot.userId !== userId) { return null; } return snapshot; } catch { return null; } } export function saveLastViewedChatToStorage(snapshot: LastViewedChatSnapshot): void { const normalised = normaliseLastViewedChatSnapshot(snapshot); if (!normalised) { return; } scheduleStorageWrite( getUserScopedStorageKey(STORAGE_KEY_LAST_VIEWED_CHAT, normalised.userId), JSON.stringify(normalised) ); } export function clearLastViewedChatFromStorage(userId?: string | null): void { scheduleStorageRemove(getUserScopedStorageKey(STORAGE_KEY_LAST_VIEWED_CHAT, userId)); scheduleStorageRemove(STORAGE_KEY_LAST_VIEWED_CHAT); } /** Force-flush any pending app-resume writes (e.g. before unload). */ export function flushAppResumeStorage(): void { if (cancelScheduledFlush) { cancelScheduledFlush(); cancelScheduledFlush = null; } flushScheduled = false; const snapshot = Array.from(pendingWrites.entries()); pendingWrites.clear(); for (const [key, value] of snapshot) { try { if (value === null) { localStorage.removeItem(key); } else { localStorage.setItem(key, value); } } catch { // storage not available } } } function normaliseGeneralSettings(raw: Partial): GeneralSettings { return { reopenLastViewedChat: typeof raw.reopenLastViewedChat === 'boolean' ? raw.reopenLastViewedChat : DEFAULT_GENERAL_SETTINGS.reopenLastViewedChat }; } function normaliseLastViewedChatSnapshot(raw: Partial): LastViewedChatSnapshot | null { const userId = typeof raw.userId === 'string' ? raw.userId.trim() : ''; const roomId = typeof raw.roomId === 'string' ? raw.roomId.trim() : ''; const channelId = typeof raw.channelId === 'string' ? raw.channelId.trim() || null : null; if (!userId || !roomId) { return null; } return { userId, roomId, channelId }; }