import { useState, useEffect, useMemo, useCallback } from 'react'; import { motion } from 'framer-motion'; import { padNumber } from '@/helpers/number'; import styles from './exercise.module.css'; type Exercise = 'Box Breathing' | 'Resonant Breathing' | '4-7-8 Breathing'; type Phase = 'inhale' | 'exhale' | 'holdInhale' | 'holdExhale'; const EXERCISE_PHASES: Record = { '4-7-8 Breathing': ['inhale', 'holdInhale', 'exhale'], 'Box Breathing': ['inhale', 'holdInhale', 'exhale', 'holdExhale'], 'Resonant Breathing': ['inhale', 'exhale'], }; const EXERCISE_DURATIONS: Record>> = { '4-7-8 Breathing': { exhale: 8, holdInhale: 7, inhale: 4 }, 'Box Breathing': { exhale: 4, holdExhale: 4, holdInhale: 4, inhale: 4 }, 'Resonant Breathing': { exhale: 5, inhale: 5 }, // No holdExhale }; const PHASE_LABELS: Record = { exhale: 'Exhale', holdExhale: 'Hold', holdInhale: 'Hold', inhale: 'Inhale', }; export function Exercise() { const [selectedExercise, setSelectedExercise] = useState('4-7-8 Breathing'); const [phaseIndex, setPhaseIndex] = useState(0); const phases = useMemo( () => EXERCISE_PHASES[selectedExercise], [selectedExercise], ); const durations = useMemo( () => EXERCISE_DURATIONS[selectedExercise], [selectedExercise], ); const currentPhase = phases[phaseIndex]; const animationVariants = useMemo( () => ({ exhale: { transform: 'translate(-50%, -50%) scale(1)', transition: { duration: durations.exhale }, }, holdExhale: { transform: 'translate(-50%, -50%) scale(1)', transition: { duration: durations.holdExhale }, }, holdInhale: { transform: 'translate(-50%, -50%) scale(1.5)', transition: { duration: durations.holdInhale }, }, inhale: { transform: 'translate(-50%, -50%) scale(1.5)', transition: { duration: durations.inhale }, }, }), [durations], ); const resetExercise = useCallback(() => { setPhaseIndex(0); }, []); const updatePhase = useCallback(() => { setPhaseIndex(prevIndex => (prevIndex + 1) % phases.length); }, [phases.length]); useEffect(() => { resetExercise(); }, [selectedExercise, resetExercise]); useEffect(() => { const intervalDuration = (durations[currentPhase] || 4) * 1000; const interval = setInterval(updatePhase, intervalDuration); return () => clearInterval(interval); }, [currentPhase, durations, updatePhase]); const [timer, setTimer] = useState(0); useEffect(() => { const interval = setInterval(() => setTimer(prev => prev + 1), 1000); return () => clearInterval(interval); }, []); return ( <>
{padNumber(Math.floor(timer / 60))}:{padNumber(timer % 60)}

{PHASE_LABELS[currentPhase]}

); }