import { useEffect, useState } from 'react'; import { useRoute, useNavigation } from '@react-navigation/native'; import { View } from '@/components/shared/Themed'; import styled from '@emotion/native'; import TimerContent from '@/components/useCases/timer/view/TimerContent'; import FinishContent from '@/components/useCases/timer/view/FinishContent'; import { TimerBgColor } from '@/components/useCases/timer/business/type'; import { Audio } from 'expo-av'; import { Sound } from 'expo-av/build/Audio'; import { activateKeepAwakeAsync, deactivateKeepAwake } from 'expo-keep-awake'; import { loadUserSettings } from '@/components/shared/business/AsyncStorage'; import BackgroundTimer from 'react-native-background-timer'; interface TimerProps { reps: number; restTime: number; workTime: number; } export default function Timer() { const navigation = useNavigation(); const route = useRoute(); const { reps, restTime, workTime } = route.params as TimerProps; const [currentRep, setCurrentRep] = useState(0); const [timeLeft, setTimeLeft] = useState(0); const [isWorkPhase, setIsWorkPhase] = useState(true); const [isRunning, setIsRunning] = useState(false); const [isFinish, setIsFinish] = useState(false); const [sound, setSound] = useState(); const [soundEnabled, setSoundEnabled] = useState(true); useEffect(() => { const init = async () => { try { const soundEnabledLocal = await loadUserSettings('soundEnabled'); setSoundEnabled(Boolean(Number(soundEnabledLocal))); if (soundEnabled) await configureAudio(); handleStart(); await activateKeepAwakeAsync(); } catch (error) { throw new Error('Erreur lors du chargement des paramètres utilisateur'); } }; init(); }, []); // when the user exits the screen, desactivate the keepAwake useEffect(() => { const unsubscribe = navigation.addListener('beforeRemove', (_) => { deactivateKeepAwake(); }); return unsubscribe; }, [navigation]); useEffect(() => { let timerId: number | null = null; if (isRunning && timeLeft > 0) { // Utilisation de BackgroundTimer pour gérer le timer même en arrière-plan timerId = BackgroundTimer.setInterval(() => { setTimeLeft((prevTime) => prevTime - 1); }, 1000); // Timer d'une seconde } else if (isRunning && timeLeft === 0) { nextRep(); // Passe à la répétition suivante } return () => { // Nettoyage du timer lorsqu'il est arrêté ou que le composant est démonté if (timerId !== null) { BackgroundTimer.clearInterval(timerId); } }; }, [isRunning, timeLeft]); const handleStart = () => { setCurrentRep(1); setIsWorkPhase(true); setTimeLeft(workTime); setIsRunning(true); setIsFinish(false); }; async function configureAudio() { await Audio.setAudioModeAsync({ allowsRecordingIOS: false, staysActiveInBackground: false, playsInSilentModeIOS: true, shouldDuckAndroid: true, playThroughEarpieceAndroid: false, }); } async function playSound() { if (!soundEnabled) return; const { sound } = await Audio.Sound.createAsync(require('../assets/audios/boxingBell.mp3')); setSound(sound); await sound.playAsync(); } useEffect(() => { return sound ? () => { sound.unloadAsync(); } : undefined; }, [sound]); const nextRep = () => { if (currentRep < reps) { if (isWorkPhase) { playSound(); setIsWorkPhase(false); setTimeLeft(restTime); } else { playSound(); setIsWorkPhase(true); setTimeLeft(workTime); setCurrentRep((prevRep) => prevRep + 1); } } else { playSound(); setIsFinish(true); setIsRunning(false); } }; const previousRep = () => { if (isWorkPhase) { if (currentRep > 1) { setIsWorkPhase(false); setTimeLeft(restTime); setCurrentRep((prevRep) => prevRep - 1); } } else { setIsWorkPhase(true); setTimeLeft(workTime); } }; const handleContine = () => { setIsRunning(true); }; const handleStop = () => { setIsRunning(false); }; const renderBgColor: () => TimerBgColor = () => { if (isFinish) return 'black'; if (isWorkPhase) return 'red'; return 'green'; } return ( {isFinish && ( )} {!isFinish && ( )} ); } const Container = styled(View)<{ bgColor: TimerBgColor }>(({ theme, bgColor }) => ({ flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: theme.colors[bgColor] }));