diff --git a/public/sounds/alarm.mp3 b/public/sounds/alarm.mp3 new file mode 100644 index 0000000..29cff71 Binary files /dev/null and b/public/sounds/alarm.mp3 differ diff --git a/src/components/toolbox/pomodoro/pomodoro.tsx b/src/components/toolbox/pomodoro/pomodoro.tsx index 6fdeed9..aad94e5 100644 --- a/src/components/toolbox/pomodoro/pomodoro.tsx +++ b/src/components/toolbox/pomodoro/pomodoro.tsx @@ -9,6 +9,7 @@ import { Button } from './button'; import { Setting } from './setting'; import { useLocalStorage } from '@/hooks/use-local-storage'; +import { useSoundEffect } from '@/hooks/use-sound-effect'; import { usePomodoroStore } from '@/store'; import styles from './pomodoro.module.css'; @@ -29,6 +30,8 @@ export function Pomodoro({ onClose, show }: PomodoroProps) { const [timer, setTimer] = useState(0); const interval = useRef | null>(null); + const alarm = useSoundEffect('/sounds/alarm.mp3'); + const defaultTimes = useMemo( () => ({ long: 15 * 60, @@ -74,13 +77,15 @@ export function Pomodoro({ onClose, show }: PomodoroProps) { if (timer <= 0 && running) { if (interval.current) clearInterval(interval.current); + alarm.play(); + setRunning(false); setCompletions(prev => ({ ...prev, [selectedTab]: prev[selectedTab] + 1, })); } - }, [timer, selectedTab, running, setRunning]); + }, [timer, selectedTab, running, setRunning, alarm]); useEffect(() => { const time = times[selectedTab] || 10; diff --git a/src/hooks/use-sound-effect.ts b/src/hooks/use-sound-effect.ts new file mode 100644 index 0000000..eb47509 --- /dev/null +++ b/src/hooks/use-sound-effect.ts @@ -0,0 +1,45 @@ +import { useMemo, useEffect, useCallback } from 'react'; +import { Howl } from 'howler'; + +import { useSSR } from './use-ssr'; + +export function useSoundEffect(src: string, volume: number = 1) { + const { isBrowser } = useSSR(); + + const sound = useMemo(() => { + let sound: Howl | null = null; + + if (isBrowser) { + sound = new Howl({ + html5: true, + src: src, + }); + } + + return sound; + }, [src, isBrowser]); + + useEffect(() => { + if (sound) sound.volume(typeof volume === 'number' ? volume : 1); + }, [sound, volume]); + + const play = useCallback(() => { + if (sound) { + if (!sound.playing()) { + sound.play(); + } + } + }, [sound]); + + const stop = useCallback(() => { + if (sound) sound.stop(); + }, [sound]); + + const pause = useCallback(() => { + if (sound) sound.pause(); + }, [sound]); + + const control = useMemo(() => ({ pause, play, stop }), [play, stop, pause]); + + return control; +}