1/** 2 * Pomodoro Store - LocalStorage Wrapper & State Management 3 * Handles history, settings, and day closure. 4 */ 5 6import { 7 getProtectedStreak, 8 recordProtectedDayClosure, 9 shouldClearDayClosure 10} from './day_closure.mjs'; 11 12const HISTORY_KEY = 'rox_pomo_history'; 13const SETTINGS_KEY = 'rox_pomo_settings'; 14const CLOSURE_KEY = 'rox_day_closure'; 15const CLOSURE_HISTORY_KEY = 'rox_day_closure_history'; 16 17const DEFAULT_SETTINGS = { 18 focusTime: 25, 19 shortBreakTime: 5, 20 longBreakTime: 15, 21 longBreakInterval: 4, 22 dailyGoalMin: 120, 23 volume: 50, 24 autoStartPomodoros: false, 25 autoStartBreaks: false, 26 startShortcut: 'space', 27 sleepBedtime: "23:00", 28 sleepWakeup: "07:00" 29}; 30 31class PomodoroStore { 32 constructor() { 33 this.bus = null; 34 this.history = this._loadJSON(HISTORY_KEY, []); 35 this.settings = { ...DEFAULT_SETTINGS, ...this._loadJSON(SETTINGS_KEY, {}) }; 36 this.dayClosure = this._loadJSON(CLOSURE_KEY, null); 37 this.dayClosureHistory = this._loadJSON(CLOSURE_HISTORY_KEY, []); 38 } 39 40 _loadJSON(key, defaultVal) { 41 try { 42 const raw = localStorage.getItem(key); 43 return raw ? JSON.parse(raw) : defaultVal; 44 } catch (e) { 45 console.error(`PomodoroStore load failed for ${key}`, e); 46 return defaultVal; 47 } 48 } 49 50 _saveJSON(key, value) { 51 try { 52 if (value === null) { 53 localStorage.removeItem(key); 54 } else { 55 localStorage.setItem(key, JSON.stringify(value)); 56 } 57 } catch (e) { 58 console.error(`PomodoroStore save failed for ${key}`, e); 59 } 60 } 61 62 setBus(bus) { 63 this.bus = bus; 64 } 65 66 /* History */ 67 getHistory() { 68 return this.history; 69 } 70 71 addSession(session) { 72 this.history.push(session);
73 this._saveJSON(HISTORY_KEY, this.history); 74 if (this.bus) this.bus.emit('history:changed', { history: this.history }); 75 } 76 77 updateHistory(newHistory) { 78 this.history = newHistory; 79 this._saveJSON(HISTORY_KEY, this.history); 80 if (this.bus) this.bus.emit('history:changed', { history: this.history }); 81 } 82 83 /* Settings */ 84 getSettings() { 85 return this.settings; 86 } 87 88 updateSettings(newSettings) { 89 this.settings = { ...this.settings, ...newSettings }; 90 this._saveJSON(SETTINGS_KEY, this.settings); 91 if (this.bus) this.bus.emit('settings:changed', { settings: this.settings }); 92 } 93 94 /* Day Closure */ 95 getDayClosure() { 96 this.checkAndClearOldClosure(); 97 return this.dayClosure; 98 } 99 100 isDayClosed() { 101 this.checkAndClearOldClosure(); 102 return this.dayClosure !== null; 103 } 104 105 checkAndClearOldClosure() { 106 if (shouldClearDayClosure(this.dayClosure)) { 107 this.clearDayClosure(); 108 } 109 } 110 111 setDayClosure(closureData) { 112 const result = recordProtectedDayClosure({ 113 closure: closureData, 114 closureHistory: this.dayClosureHistory, 115 focusHistory: this.history 116 }); 117 this.dayClosure = result.closure; 118 this.dayClosureHistory = result.closureHistory; 119 this._saveJSON(CLOSURE_KEY, this.dayClosure); 120 this._saveJSON(CLOSURE_HISTORY_KEY, this.dayClosureHistory); 121 if (this.bus) { 122 this.bus.emit('closure:changed', { 123 closure: this.dayClosure, 124 protectedStreak: this.getProtectedStreak(), 125 streakAdded: result.streakAdded 126 }); 127 } 128 return result; 129 } 130 131 getDayClosureHistory() { 132 return Array.isArray(this.dayClosureHistory) ? [...this.dayClosureHistory] : []; 133 } 134 135 getProtectedStreak() { 136 return getProtectedStreak(this.dayClosureHistory); 137 } 138 139 startNewDay() { 140 this.clearDayClosure(); 141 } 142 143 clearDayClosure() { 144 this.dayClosure = null; 145 this._saveJSON(CLOSURE_KEY, null); 146 if (this.bus) this.bus.emit('closure:changed', { closure: null }); 147 } 148} 149 150export const pomodoroStore = new PomodoroStore();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.