/** Ring countdown to a server deadline (docs/07 §4). */ import { useEffect, useRef, useState } from 'react'; import { socket } from '../net/socket'; import { haptic, sounds } from '../lib/feedback'; export function useServerNow(intervalMs = 250): number { const [now, setNow] = useState(() => socket.now()); useEffect(() => { const t = setInterval(() => setNow(socket.now()), intervalMs); return () => clearInterval(t); }, [intervalMs]); return now; } export function TurnTimer({ deadline, totalMs, mine, size = 44 }: { deadline: number | null; totalMs: number; mine: boolean; size?: number }) { const now = useServerNow(); const warned = useRef(null); const left = deadline ? Math.max(0, deadline - now) : null; const sec = left === null ? null : Math.ceil(left / 1000); useEffect(() => { if (!mine || sec === null) return; if ((sec === 10 || sec === 5) && warned.current !== sec) { warned.current = sec; sounds.warn(); if (sec === 5) haptic([60, 40, 60]); } }, [sec, mine]); if (left === null) return null; const frac = Math.min(1, left / Math.max(1, totalMs)); const color = sec! <= 5 ? 'var(--danger)' : sec! <= 10 ? 'var(--warning)' : 'var(--success)'; const r = size / 2 - 4; const c = 2 * Math.PI * r; return ( 40 ? '0.95rem' : '0.8rem', color }}> {sec! >= 600 ? `${Math.floor(sec! / 60)}분` : sec! >= 100 ? `${Math.floor(sec! / 60)}:${String(sec! % 60).padStart(2, '0')}` : sec} ); }