mirror of
https://github.com/R0m1k3/noteflow.git
synced 2026-10-11 17:29:37 +02:00
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:
1 file changed
+39
-12
@@ -37,6 +37,10 @@ export function PomodoroTimer({ onStateChange }: PomodoroTimerProps = {}) {
|
|||||||
const saved = localStorage.getItem('pomodoroSessionsCompleted');
|
const saved = localStorage.getItem('pomodoroSessionsCompleted');
|
||||||
return saved ? parseInt(saved) : 0;
|
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);
|
const intervalRef = useRef<NodeJS.Timeout | null>(null);
|
||||||
|
|
||||||
// Save state to localStorage whenever it changes and dispatch custom event
|
// 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'));
|
window.dispatchEvent(new CustomEvent('pomodoroStateChange'));
|
||||||
}, [sessionsCompleted]);
|
}, [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
|
// Listen for localStorage changes from other instances
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleStorageChange = () => {
|
const handleStorageChange = () => {
|
||||||
@@ -67,6 +80,7 @@ export function PomodoroTimer({ onStateChange }: PomodoroTimerProps = {}) {
|
|||||||
const savedTimeLeft = localStorage.getItem('pomodoroTimeLeft');
|
const savedTimeLeft = localStorage.getItem('pomodoroTimeLeft');
|
||||||
const savedIsRunning = localStorage.getItem('pomodoroIsRunning');
|
const savedIsRunning = localStorage.getItem('pomodoroIsRunning');
|
||||||
const savedSessions = localStorage.getItem('pomodoroSessionsCompleted');
|
const savedSessions = localStorage.getItem('pomodoroSessionsCompleted');
|
||||||
|
const savedEndTime = localStorage.getItem('pomodoroEndTime');
|
||||||
|
|
||||||
if (savedMode && savedMode !== mode) {
|
if (savedMode && savedMode !== mode) {
|
||||||
setMode(savedMode);
|
setMode(savedMode);
|
||||||
@@ -80,11 +94,15 @@ export function PomodoroTimer({ onStateChange }: PomodoroTimerProps = {}) {
|
|||||||
if (savedSessions && parseInt(savedSessions) !== sessionsCompleted) {
|
if (savedSessions && parseInt(savedSessions) !== sessionsCompleted) {
|
||||||
setSessionsCompleted(parseInt(savedSessions));
|
setSessionsCompleted(parseInt(savedSessions));
|
||||||
}
|
}
|
||||||
|
const newEndTime = savedEndTime ? parseInt(savedEndTime) : null;
|
||||||
|
if (newEndTime !== endTime) {
|
||||||
|
setEndTime(newEndTime);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('pomodoroStateChange', handleStorageChange);
|
window.addEventListener('pomodoroStateChange', handleStorageChange);
|
||||||
return () => window.removeEventListener('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
|
// Function to play notification sound using Web Audio API
|
||||||
const playNotificationSound = () => {
|
const playNotificationSound = () => {
|
||||||
@@ -137,6 +155,7 @@ export function PomodoroTimer({ onStateChange }: PomodoroTimerProps = {}) {
|
|||||||
|
|
||||||
const handleTimerComplete = useCallback(() => {
|
const handleTimerComplete = useCallback(() => {
|
||||||
setIsRunning(false);
|
setIsRunning(false);
|
||||||
|
setEndTime(null);
|
||||||
|
|
||||||
// Play notification sound
|
// Play notification sound
|
||||||
playNotificationSound();
|
playNotificationSound();
|
||||||
@@ -163,18 +182,19 @@ export function PomodoroTimer({ onStateChange }: PomodoroTimerProps = {}) {
|
|||||||
}
|
}
|
||||||
}, [mode, sessionsCompleted]);
|
}, [mode, sessionsCompleted]);
|
||||||
|
|
||||||
// Manage timer interval
|
// Manage timer interval - using timestamps for accuracy across tab switches
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isRunning) {
|
if (isRunning && endTime) {
|
||||||
intervalRef.current = setInterval(() => {
|
intervalRef.current = setInterval(() => {
|
||||||
setTimeLeft(prev => {
|
const now = Date.now();
|
||||||
if (prev <= 1) {
|
const remaining = Math.max(0, Math.ceil((endTime - now) / 1000));
|
||||||
handleTimerComplete();
|
|
||||||
return 0;
|
setTimeLeft(remaining);
|
||||||
}
|
|
||||||
return prev - 1;
|
if (remaining <= 0) {
|
||||||
});
|
handleTimerComplete();
|
||||||
}, 1000);
|
}
|
||||||
|
}, 100); // Check every 100ms for better accuracy
|
||||||
} else {
|
} else {
|
||||||
if (intervalRef.current) {
|
if (intervalRef.current) {
|
||||||
clearInterval(intervalRef.current);
|
clearInterval(intervalRef.current);
|
||||||
@@ -188,18 +208,24 @@ export function PomodoroTimer({ onStateChange }: PomodoroTimerProps = {}) {
|
|||||||
intervalRef.current = null;
|
intervalRef.current = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [isRunning, handleTimerComplete]);
|
}, [isRunning, endTime, handleTimerComplete]);
|
||||||
|
|
||||||
const handleStart = () => {
|
const handleStart = () => {
|
||||||
|
const now = Date.now();
|
||||||
|
const newEndTime = now + (timeLeft * 1000);
|
||||||
|
setEndTime(newEndTime);
|
||||||
setIsRunning(true);
|
setIsRunning(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePause = () => {
|
const handlePause = () => {
|
||||||
setIsRunning(false);
|
setIsRunning(false);
|
||||||
|
setEndTime(null);
|
||||||
|
// timeLeft is already updated by the interval
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
setIsRunning(false);
|
setIsRunning(false);
|
||||||
|
setEndTime(null);
|
||||||
setTimeLeft(TIMER_DURATIONS[mode]);
|
setTimeLeft(TIMER_DURATIONS[mode]);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -207,6 +233,7 @@ export function PomodoroTimer({ onStateChange }: PomodoroTimerProps = {}) {
|
|||||||
setMode(newMode);
|
setMode(newMode);
|
||||||
setTimeLeft(TIMER_DURATIONS[newMode]);
|
setTimeLeft(TIMER_DURATIONS[newMode]);
|
||||||
setIsRunning(false);
|
setIsRunning(false);
|
||||||
|
setEndTime(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatTime = (seconds: number): string => {
|
const formatTime = (seconds: number): string => {
|
||||||
|
|||||||
Reference in new issue
Block a user