/** * Central state store with persistence. * Keys: user, windows, wallpaper, achievements, connection */ const STORAGE_KEY = 'meridian:state'; const State = (() => { const listeners = new Map(); let _state = loadState(); function loadState() { try { const raw = localStorage.getItem(STORAGE_KEY); if (raw) { const parsed = JSON.parse(raw); // achievements is stored as array, reconstruct as Set if (parsed.achievements) { parsed._achievementSet = new Set(parsed.achievements); } return parsed; } } catch (e) { console.warn('State load failed:', e); } return { user: null, windows: [], wallpaper: 'graphite', achievements: [], _achievementSet: new Set(), connection: navigator.onLine }; } function saveState() { try { const toSave = { user: _state.user, windows: _state.windows, wallpaper: _state.wallpaper, achievements: Array.from(_state._achievementSet || _state.achievements) }; localStorage.setItem(STORAGE_KEY, JSON.stringify(toSave)); } catch (e) { console.warn('State save failed:', e); } } function set(key, val) { _state[key] = val; saveState(); notify(key); } function get(key) { return _state[key]; } function subscribe(key, cb) { if (!listeners.has(key)) listeners.set(key, new Set()); listeners.get(key).add(cb); // immediate call with current value try { cb(_state[key]); } catch (e) { console.error('State subscribe error:', e); } return () => off(key, cb); } function off(key, cb) { const set = listeners.get(key); if (set) { set.delete(cb); if (set.size === 0) listeners.delete(key); } } function notify(key) { const set = listeners.get(key); if (set) { for (const cb of set) { try { cb(_state[key]); } catch (e) { console.error('State notify error:', e); } } } } function addAchievement(id) { if (!_state._achievementSet) { _state._achievementSet = new Set(_state.achievements || []); _state.achievements = Array.from(_state._achievementSet); } _state._achievementSet.add(id); _state.achievements = Array.from(_state._achievementSet); saveState(); notify('achievements'); } function initConnection() { _state.connection = navigator.onLine; window.addEventListener('online', () => { _state.connection = true; notify('connection'); saveState(); }); window.addEventListener('offline', () => { _state.connection = false; notify('connection'); saveState(); }); } return { set, get, subscribe, off, addAchievement, initConnection }; })(); export default State; export { State };