From 1eff318034fb2e91074e79215f7818e7ddc0334d Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 29 Nov 2025 07:54:56 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20permettre=20au=20timer=20Pomodoro=20de?= =?UTF-8?q?=20continuer=20en=20arri=C3=A8re-plan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/components/PomodoroTimer.tsx | 51 ++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/src/components/PomodoroTimer.tsx b/src/components/PomodoroTimer.tsx index 9f829f5..7d4ec9e 100644 --- a/src/components/PomodoroTimer.tsx +++ b/src/components/PomodoroTimer.tsx @@ -37,6 +37,10 @@ export function PomodoroTimer({ onStateChange }: PomodoroTimerProps = {}) { const saved = localStorage.getItem('pomodoroSessionsCompleted'); return saved ? parseInt(saved) : 0; }); + const [endTime, setEndTime] = useState(() => { + const saved = localStorage.getItem('pomodoroEndTime'); + return saved ? parseInt(saved) : null; + }); const intervalRef = useRef(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 => {