fix: permettre au timer Pomodoro de continuer en arrière-plan

Correction du problème où le timer Pomodoro se mettait en pause lors du
changement d'onglet. Le timer utilise maintenant des timestamps réels au
lieu de setInterval pour calculer le temps restant, ce qui permet de
continuer à fonctionner même quand l'onglet n'est pas actif.

Changements:
- Ajout d'un état endTime pour stocker le timestamp de fin
- Modification de la logique du timer pour calculer le temps restant basé sur endTime
- Le timer vérifie maintenant toutes les 100ms pour plus de précision
- Persistance de endTime dans localStorage pour la synchronisation
This commit is contained in:
Claude committed 2025-11-29 07:54:56 +00:00
1 parent bd8013b4fc
commit 1eff318034
1 file changed
+39 -12
+39 -12
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) {
handleTimerComplete();
return 0;
}
return prev - 1;
});
}, 1000);
const now = Date.now();
const remaining = Math.max(0, Math.ceil((endTime - now) / 1000));
setTimeLeft(remaining);
if (remaining <= 0) {
handleTimerComplete();
}
}, 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 => {