Merge pull request #150 from R0m1k3/claude/fix-pomodoro-tab-switch-01WFQEsfPro5ynAgg4VAU3EV

fix: permettre au timer Pomodoro de continuer en arrière-plan
This commit is contained in:
LogiFlow authored and GitHub committed 2025-11-29 09:01:55 +01:00
commit 4be735a46b
1 file changed
+37 -10
+37 -10
View File
@@ -37,6 +37,10 @@ export function PomodoroTimer({ onStateChange }: PomodoroTimerProps = {}) {
const saved = localStorage.getItem('pomodoroSessionsCompleted');
return saved ? parseInt(saved) : 0;
});
const [endTime, setEndTime] = useState<number | null>(() => {
const saved = localStorage.getItem('pomodoroEndTime');
return saved ? parseInt(saved) : null;
});
const intervalRef = useRef<NodeJS.Timeout | null>(null);
// Save state to localStorage whenever it changes and dispatch custom event
@@ -60,6 +64,15 @@ export function PomodoroTimer({ onStateChange }: PomodoroTimerProps = {}) {
window.dispatchEvent(new CustomEvent('pomodoroStateChange'));
}, [sessionsCompleted]);
useEffect(() => {
if (endTime !== null) {
localStorage.setItem('pomodoroEndTime', endTime.toString());
} else {
localStorage.removeItem('pomodoroEndTime');
}
window.dispatchEvent(new CustomEvent('pomodoroStateChange'));
}, [endTime]);
// Listen for localStorage changes from other instances
useEffect(() => {
const handleStorageChange = () => {
@@ -67,6 +80,7 @@ export function PomodoroTimer({ onStateChange }: PomodoroTimerProps = {}) {
const savedTimeLeft = localStorage.getItem('pomodoroTimeLeft');
const savedIsRunning = localStorage.getItem('pomodoroIsRunning');
const savedSessions = localStorage.getItem('pomodoroSessionsCompleted');
const savedEndTime = localStorage.getItem('pomodoroEndTime');
if (savedMode && savedMode !== mode) {
setMode(savedMode);
@@ -80,11 +94,15 @@ export function PomodoroTimer({ onStateChange }: PomodoroTimerProps = {}) {
if (savedSessions && parseInt(savedSessions) !== sessionsCompleted) {
setSessionsCompleted(parseInt(savedSessions));
}
const newEndTime = savedEndTime ? parseInt(savedEndTime) : null;
if (newEndTime !== endTime) {
setEndTime(newEndTime);
}
};
window.addEventListener('pomodoroStateChange', handleStorageChange);
return () => window.removeEventListener('pomodoroStateChange', handleStorageChange);
}, [mode, timeLeft, isRunning, sessionsCompleted]);
}, [mode, timeLeft, isRunning, sessionsCompleted, endTime]);
// Function to play notification sound using Web Audio API
const playNotificationSound = () => {
@@ -137,6 +155,7 @@ export function PomodoroTimer({ onStateChange }: PomodoroTimerProps = {}) {
const handleTimerComplete = useCallback(() => {
setIsRunning(false);
setEndTime(null);
// Play notification sound
playNotificationSound();
@@ -163,18 +182,19 @@ export function PomodoroTimer({ onStateChange }: PomodoroTimerProps = {}) {
}
}, [mode, sessionsCompleted]);
// Manage timer interval
// Manage timer interval - using timestamps for accuracy across tab switches
useEffect(() => {
if (isRunning) {
if (isRunning && endTime) {
intervalRef.current = setInterval(() => {
setTimeLeft(prev => {
if (prev <= 1) {
const now = Date.now();
const remaining = Math.max(0, Math.ceil((endTime - now) / 1000));
setTimeLeft(remaining);
if (remaining <= 0) {
handleTimerComplete();
return 0;
}
return prev - 1;
});
}, 1000);
}, 100); // Check every 100ms for better accuracy
} else {
if (intervalRef.current) {
clearInterval(intervalRef.current);
@@ -188,18 +208,24 @@ export function PomodoroTimer({ onStateChange }: PomodoroTimerProps = {}) {
intervalRef.current = null;
}
};
}, [isRunning, handleTimerComplete]);
}, [isRunning, endTime, handleTimerComplete]);
const handleStart = () => {
const now = Date.now();
const newEndTime = now + (timeLeft * 1000);
setEndTime(newEndTime);
setIsRunning(true);
};
const handlePause = () => {
setIsRunning(false);
setEndTime(null);
// timeLeft is already updated by the interval
};
const handleReset = () => {
setIsRunning(false);
setEndTime(null);
setTimeLeft(TIMER_DURATIONS[mode]);
};
@@ -207,6 +233,7 @@ export function PomodoroTimer({ onStateChange }: PomodoroTimerProps = {}) {
setMode(newMode);
setTimeLeft(TIMER_DURATIONS[newMode]);
setIsRunning(false);
setEndTime(null);
};
const formatTime = (seconds: number): string => {