import { useEffect, useRef } from 'react'; import { listen } from '@tauri-apps/api/event'; import { invoke } from '@tauri-apps/api/core'; import { useTimerStore, TimerTickPayload } from '../store/timerStore'; import { useTaskStore } from '../store/taskStore'; interface PhaseChangedPayload { phase: TimerTickPayload['phase']; session_count: number; } interface TimerStatus { phase: 'work' | 'short_break' | 'long_break'; remaining_secs: number; total_secs: number; running: boolean; session_count: number; current_task_id: string | null; } interface CompletedPayload { task_id: string | null; } export function useTimerEvents( onCompleted: (taskId: string | null) => void, ) { const setTimerTick = useTimerStore((s) => s.setTimerTick); const setRunning = useTimerStore((s) => s.setRunning); const fetchTasks = useTaskStore((s) => s.fetchTasks); const onCompletedRef = useRef(onCompleted); useEffect(() => { onCompletedRef.current = onCompleted; }, [onCompleted]); useEffect(() => { let cancelled = false; let unlisteners: Array<() => void> = []; async function setup() { // Bootstrap initial state from backend try { const status = await invoke('get_timer_status'); if (cancelled) return; setTimerTick({ phase: status.phase, remaining_secs: status.remaining_secs, total_secs: status.total_secs, session_count: status.session_count, current_task_id: status.current_task_id, }); setRunning(status.running); } catch (e) { console.error('Failed to get timer status:', e); } if (cancelled) return; // Register all listeners atomically try { const [unlistenTick, unlistenCompleted, unlistenPhaseChanged] = await Promise.all([ listen('timer-tick', (event) => { setTimerTick(event.payload); setRunning(true); }), listen('timer-completed', async (event) => { setRunning(false); onCompletedRef.current(event.payload.task_id ?? null); await fetchTasks(); }), listen('timer-phase-changed', async (_event) => { try { const status = await invoke('get_timer_status'); setTimerTick({ phase: status.phase, remaining_secs: status.remaining_secs, total_secs: status.total_secs, session_count: status.session_count, current_task_id: status.current_task_id, }); } catch (e) { console.error('Failed to re-sync timer status:', e); } }), ]); if (cancelled) { unlistenTick(); unlistenCompleted(); unlistenPhaseChanged(); return; } unlisteners = [unlistenTick, unlistenCompleted, unlistenPhaseChanged]; } catch (e) { console.error('Failed to register timer listeners:', e); } } setup(); return () => { cancelled = true; unlisteners.forEach((fn) => fn()); }; }, [setTimerTick, setRunning, fetchTasks]); // onCompleted excluded — updated via ref }