Skip to content

Commit 8427e8c

Browse files
authored
Merge pull request #30 from murdercode/timer-improvements
Timer improvements
2 parents cf3abc9 + 3a3b9f2 commit 8427e8c

File tree

7 files changed

+250
-9
lines changed

7 files changed

+250
-9
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,5 @@ dist-ssr
3030
.env.development.local
3131
.env.test.local
3232
.env.production.local
33+
34+
claude.md

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ tauri-plugin-oauth = { git = "https://github.com/FabianLars/tauri-plugin-oauth",
4343
tauri-plugin-aptabase = "1.0.0"
4444
serde = { version = "1", features = ["derive"], default-features = false }
4545
serde_json = { version = "1", default-features = false }
46-
chrono = { version = "0.4", features = ["serde"], default-features = false }
46+
chrono = { version = "0.4", features = ["serde", "clock"], default-features = false }
4747
dotenv = "0.15"
4848
base64 = "0.21"
4949

src-tauri/src/lib.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -416,10 +416,26 @@ async fn load_session_data(app: AppHandle) -> Result<Option<PomodoroSession>, St
416416
}
417417

418418
let content =
419-
fs::read_to_string(file_path).map_err(|e| format!("Failed to read session file: {}", e))?;
420-
let session: PomodoroSession =
419+
fs::read_to_string(&file_path).map_err(|e| format!("Failed to read session file: {}", e))?;
420+
let mut session: PomodoroSession =
421421
serde_json::from_str(&content).map_err(|e| format!("Failed to parse session: {}", e))?;
422422

423+
// Get today's date string
424+
let today = chrono::Local::now().format("%a %b %d %Y").to_string();
425+
426+
// If the saved session is not from today, reset the counters but keep the date updated
427+
if session.date != today {
428+
session.completed_pomodoros = 0;
429+
session.total_focus_time = 0;
430+
session.current_session = 1;
431+
session.date = today;
432+
433+
// Save the reset session back to file
434+
let json = serde_json::to_string_pretty(&session)
435+
.map_err(|e| format!("Failed to serialize reset session: {}", e))?;
436+
fs::write(file_path, json).map_err(|e| format!("Failed to write reset session file: {}", e))?;
437+
}
438+
423439
Ok(Some(session))
424440
}
425441

src/core/pomodoro-timer.js

Lines changed: 200 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export class PomodoroTimer {
3030
this.currentSessionElapsedTime = 0; // Actual elapsed time for current session (in seconds)
3131
this.lastCompletedSessionTime = 0; // Time of the last completed session for undo functionality
3232
this.sessionCompletedButNotSaved = false; // Flag to track if session completed but not saved yet
33+
this.maxSessionTime = 120 * 60 * 1000; // Default 2 hours in milliseconds
34+
this.maxSessionTimeReached = false; // Flag to track if max session time was reached
3335

3436
// Timer accuracy tracking (for background throttling fix)
3537
this.timerStartTime = null; // When the timer was started (Date.now())
@@ -86,6 +88,10 @@ export class PomodoroTimer {
8688
skip: "CommandOrControl+Alt+S" // Save Session
8789
};
8890

91+
// Midnight monitoring for daily reset
92+
this.midnightMonitorInterval = null;
93+
this.currentDateString = new Date().toDateString();
94+
8995
this.init();
9096
}
9197

@@ -180,6 +186,9 @@ export class PomodoroTimer {
180186

181187
// Initialize tray menu state
182188
this.updateTrayMenu();
189+
190+
// Start midnight monitoring for daily reset
191+
this.startMidnightMonitoring();
183192
}
184193

185194
setupEventListeners() {
@@ -667,6 +676,9 @@ export class PomodoroTimer {
667676
}
668677
}
669678

679+
// Check if max session time has been reached
680+
this.checkMaxSessionTime();
681+
670682
this.updateDisplay();
671683

672684
// Warning when less than 2 minutes remaining
@@ -696,6 +708,35 @@ export class PomodoroTimer {
696708
}
697709
}
698710

711+
checkMaxSessionTime() {
712+
// Only check during focus sessions and if a session is active
713+
if (this.currentMode !== 'focus' || !this.sessionStartTime || this.maxSessionTimeReached) {
714+
return;
715+
}
716+
717+
const now = Date.now();
718+
const sessionElapsed = now - this.sessionStartTime;
719+
720+
// Check if session has exceeded max time
721+
if (sessionElapsed >= this.maxSessionTime) {
722+
this.maxSessionTimeReached = true;
723+
this.pauseTimer();
724+
725+
// Show notification
726+
const maxTimeInMinutes = Math.floor(this.maxSessionTime / (60 * 1000));
727+
NotificationUtils.showNotificationPing(
728+
`Session automatically paused after ${maxTimeInMinutes} minutes. Take a break! 🛑`,
729+
'warning'
730+
);
731+
732+
// Show desktop notification if enabled
733+
NotificationUtils.showDesktopNotification(
734+
'Session Time Limit Reached',
735+
`Your session has been automatically paused after ${maxTimeInMinutes} minutes. Consider taking a break!`
736+
);
737+
}
738+
}
739+
699740
pauseTimer() {
700741
if (this.isRunning) {
701742
this.isRunning = false;
@@ -748,6 +789,7 @@ export class PomodoroTimer {
748789
this.sessionStartTime = null;
749790
this.currentSessionElapsedTime = 0;
750791
this.sessionCompletedButNotSaved = false; // Reset flag
792+
this.maxSessionTimeReached = false; // Reset max session time flag
751793

752794
// Reset timer accuracy tracking
753795
this.timerStartTime = null;
@@ -801,6 +843,82 @@ export class PomodoroTimer {
801843
);
802844
}
803845

846+
// Midnight monitoring methods for daily reset
847+
startMidnightMonitoring() {
848+
// Clear any existing monitoring
849+
this.stopMidnightMonitoring();
850+
851+
// Check for date change every minute
852+
this.midnightMonitorInterval = setInterval(() => {
853+
this.checkForMidnightReset();
854+
}, 60000); // Check every minute
855+
856+
console.log('🌙 Midnight monitoring started');
857+
}
858+
859+
stopMidnightMonitoring() {
860+
if (this.midnightMonitorInterval) {
861+
clearInterval(this.midnightMonitorInterval);
862+
this.midnightMonitorInterval = null;
863+
console.log('🌙 Midnight monitoring stopped');
864+
}
865+
}
866+
867+
checkForMidnightReset() {
868+
const newDateString = new Date().toDateString();
869+
870+
if (newDateString !== this.currentDateString) {
871+
console.log('🌙 Date change detected:', this.currentDateString, '→', newDateString);
872+
this.currentDateString = newDateString;
873+
this.performMidnightReset();
874+
}
875+
}
876+
877+
async performMidnightReset() {
878+
console.log('🌅 Performing midnight reset...');
879+
880+
// Store previous session count for notification
881+
const previousPomodoros = this.completedPomodoros;
882+
883+
// Use enhanced loadSessionData with force reset to ensure clean state
884+
await this.loadSessionData(true);
885+
886+
// Update display to show the reset state
887+
this.updateDisplay();
888+
889+
// Save the reset state
890+
await this.saveSessionData();
891+
892+
// Show user notification about the reset
893+
if (previousPomodoros > 0) {
894+
NotificationUtils.showNotificationPing(
895+
`New day! Yesterday's ${previousPomodoros} sessions have been saved. Fresh start! 🌅`,
896+
'info'
897+
);
898+
} else {
899+
NotificationUtils.showNotificationPing(
900+
'Good morning! Ready for a productive new day? 🌅',
901+
'info'
902+
);
903+
}
904+
905+
// Update all visual elements
906+
this.updateTrayIcon();
907+
908+
// Refresh navigation charts if available
909+
if (window.navigationManager) {
910+
try {
911+
await window.navigationManager.updateDailyChart();
912+
await window.navigationManager.updateFocusSummary();
913+
await window.navigationManager.updateWeeklySessionsChart();
914+
} catch (error) {
915+
console.error('Failed to update navigation charts after midnight reset:', error);
916+
}
917+
}
918+
919+
console.log('✅ Midnight reset completed successfully');
920+
}
921+
804922
async skipSession() {
805923
this.isRunning = false;
806924
this.isPaused = false;
@@ -835,8 +953,24 @@ export class PomodoroTimer {
835953
} else {
836954
this.currentMode = 'break';
837955
}
956+
957+
// Reset display state tracking when switching to break modes
958+
this._lastLoggedState = null;
959+
this._lastAutoPausedLogged = false;
960+
this._lastPausedLogged = false;
838961
} else {
839962
this.currentMode = 'focus';
963+
964+
// Reset display state tracking when switching to focus mode
965+
this._lastLoggedState = null;
966+
this._lastAutoPausedLogged = false;
967+
this._lastPausedLogged = false;
968+
969+
// Restore TagManager display when returning to focus mode
970+
if (window.tagManager) {
971+
window.tagManager.updateStatusDisplay();
972+
}
973+
840974
if (this.completedPomodoros < this.totalSessions) {
841975
this.currentSession = this.completedPomodoros + 1;
842976
}
@@ -884,8 +1018,18 @@ export class PomodoroTimer {
8841018
} else {
8851019
this.currentMode = 'break';
8861020
}
1021+
1022+
// Reset display state tracking when switching to break modes
1023+
this._lastLoggedState = null;
1024+
this._lastAutoPausedLogged = false;
1025+
this._lastPausedLogged = false;
8871026
} else {
8881027
this.currentMode = 'focus';
1028+
1029+
// Reset display state tracking when switching to focus mode
1030+
this._lastLoggedState = null;
1031+
this._lastAutoPausedLogged = false;
1032+
this._lastPausedLogged = false;
8891033

8901034
// Restore TagManager display when returning to focus mode
8911035
if (window.tagManager) {
@@ -962,6 +1106,11 @@ export class PomodoroTimer {
9621106
} else {
9631107
this.currentMode = 'break';
9641108
}
1109+
1110+
// Reset display state tracking when switching to break modes
1111+
this._lastLoggedState = null;
1112+
this._lastAutoPausedLogged = false;
1113+
this._lastPausedLogged = false;
9651114
}
9661115
} else {
9671116
// Break completed
@@ -971,6 +1120,11 @@ export class PomodoroTimer {
9711120
} else {
9721121
// Traditional behavior - go back to focus
9731122
this.currentMode = 'focus';
1123+
1124+
// Reset display state tracking when switching to focus mode
1125+
this._lastLoggedState = null;
1126+
this._lastAutoPausedLogged = false;
1127+
this._lastPausedLogged = false;
9741128

9751129
// Restore TagManager display when returning to focus mode
9761130
if (window.tagManager) {
@@ -986,6 +1140,7 @@ export class PomodoroTimer {
9861140
this.sessionStartTime = null;
9871141
this.currentSessionElapsedTime = 0;
9881142
this.sessionCompletedButNotSaved = false; // Reset flag
1143+
this.maxSessionTimeReached = false; // Reset max session time flag
9891144

9901145
// Reset timer accuracy tracking
9911146
this.timerStartTime = null;
@@ -1581,6 +1736,7 @@ export class PomodoroTimer {
15811736
this.currentSessionElapsedTime = 0;
15821737
this.lastCompletedSessionTime = 0;
15831738
this.sessionCompletedButNotSaved = false; // Reset flag
1739+
this.maxSessionTimeReached = false; // Reset max session time flag
15841740

15851741
// Reset timer accuracy tracking
15861742
this.timerStartTime = null;
@@ -1932,26 +2088,57 @@ export class PomodoroTimer {
19322088
}
19332089
}
19342090

1935-
async loadSessionData() {
2091+
async loadSessionData(forceReset = false) {
2092+
const today = new Date().toDateString();
2093+
2094+
// Update current date string for midnight monitoring
2095+
this.currentDateString = today;
2096+
19362097
try {
19372098
const data = await invoke('load_session_data');
1938-
if (data && data.date === new Date().toDateString()) {
2099+
if (data && data.date === today && !forceReset) {
2100+
// Load today's session data
19392101
this.completedPomodoros = data.completed_pomodoros || 0;
19402102
this.totalFocusTime = data.total_focus_time || 0;
19412103
this.currentSession = data.current_session || 1;
19422104
this.updateProgressDots();
2105+
console.log('📊 Loaded existing session data for today');
2106+
} else {
2107+
// Reset to default values for new day, no data, or forced reset
2108+
this.completedPomodoros = 0;
2109+
this.totalFocusTime = 0;
2110+
this.currentSession = 1;
2111+
this.updateProgressDots();
2112+
console.log('🌅 Reset session data for new day or forced reset');
19432113
}
19442114
} catch (error) {
19452115
console.error('Failed to load session data from Tauri, using localStorage:', error);
19462116
const saved = localStorage.getItem('pomodoro-session');
2117+
19472118
if (saved) {
19482119
const data = JSON.parse(saved);
1949-
if (data.date === new Date().toDateString()) {
2120+
if (data.date === today && !forceReset) {
2121+
// Load today's session data from localStorage
19502122
this.completedPomodoros = data.completedPomodoros || 0;
19512123
this.totalFocusTime = data.totalFocusTime || 0;
19522124
this.currentSession = data.currentSession || 1;
19532125
this.updateProgressDots();
2126+
console.log('📊 Loaded existing session data from localStorage');
2127+
} else {
2128+
// Reset to default values for new day, no data, or forced reset
2129+
this.completedPomodoros = 0;
2130+
this.totalFocusTime = 0;
2131+
this.currentSession = 1;
2132+
this.updateProgressDots();
2133+
console.log('🌅 Reset session data from localStorage for new day or forced reset');
19542134
}
2135+
} else {
2136+
// No saved data at all, reset to defaults
2137+
this.completedPomodoros = 0;
2138+
this.totalFocusTime = 0;
2139+
this.currentSession = 1;
2140+
this.updateProgressDots();
2141+
console.log('🌅 No saved data found, using defaults');
19552142
}
19562143
}
19572144
}
@@ -2089,6 +2276,9 @@ export class PomodoroTimer {
20892276
}
20902277

20912278
this.totalSessions = settings.timer.total_sessions;
2279+
2280+
// Update max session time (convert from minutes to milliseconds)
2281+
this.maxSessionTime = (settings.timer.max_session_time || 120) * 60 * 1000;
20922282

20932283
// If timer is not running, update current time remaining
20942284
if (!this.isRunning) {
@@ -2160,6 +2350,9 @@ export class PomodoroTimer {
21602350
this.smartPauseEnabled = false;
21612351
this.inactivityThreshold = 30000; // Reset to default 30 seconds
21622352

2353+
// Clear midnight monitoring
2354+
this.stopMidnightMonitoring();
2355+
21632356
// Reset all counters and state
21642357
this.completedPomodoros = 0;
21652358
this.generateProgressDots();
@@ -2172,6 +2365,7 @@ export class PomodoroTimer {
21722365
this.sessionStartTime = null;
21732366
this.currentSessionElapsedTime = 0;
21742367
this.lastCompletedSessionTime = 0;
2368+
this.maxSessionTimeReached = false; // Reset max session time flag
21752369

21762370
// Reset timer accuracy tracking
21772371
this.timerStartTime = null;
@@ -2207,6 +2401,9 @@ export class PomodoroTimer {
22072401
this.updateTrayIcon();
22082402
this.updateSettingIndicators();
22092403

2404+
// Restart midnight monitoring
2405+
this.startMidnightMonitoring();
2406+
22102407
console.log('Timer reset to initial state');
22112408
}
22122409
}

0 commit comments

Comments
 (0)