diff --git a/js/desktop/src/features/compose/recording-overlay.tsx b/js/desktop/src/features/compose/recording-overlay.tsx index aa9f15b..597e2df 100644 --- a/js/desktop/src/features/compose/recording-overlay.tsx +++ b/js/desktop/src/features/compose/recording-overlay.tsx @@ -7,6 +7,10 @@ import { useObjectUrl } from '@/hooks/use-object-url'; import { AttachmentStrip } from '@/features/compose/attachment-strip'; import type { PendingAttachment } from '@/features/compose/attachment-strip'; import { cn } from '@/lib/utils'; +import { + RECORDING_MAX_DURATION_SECONDS, + RECORDING_WARNING_SECONDS, +} from '@/lib/constants'; import { Button } from '@/components/ui/button'; import { KeyHint } from '@/components/key-hint'; import { useComposeIntentStore } from '@/stores/compose-intent-store'; @@ -34,16 +38,51 @@ interface RecordingOverlayProps { objectFit?: 'cover' | 'contain'; } -function RecordingTimer() { - const [elapsed, setElapsed] = useState(0); +const WARNING_AT_SECONDS = + RECORDING_MAX_DURATION_SECONDS - RECORDING_WARNING_SECONDS; + +/** + * Tracks elapsed recording time and drives the time-limit UI. Keeps the + * recorder itself unaware of limits: when the cap is reached it dispatches the + * standard `stop` intent (the same path as releasing the ` key), which finishes + * the recording into the review step. + */ +function useRecordingCountdown(active: boolean) { + const [{ elapsed, isWarning }, setState] = useState({ + elapsed: 0, + isWarning: false, + }); + const requestIntent = useComposeIntentStore((s) => s.request); useEffect(() => { - const interval = setInterval(() => { - setElapsed((prev) => prev + 1); - }, 1000); - return () => clearInterval(interval); - }, []); + if (!active) return; + const start = Date.now(); + let stopped = false; + const interval = setInterval(() => { + const seconds = Math.floor((Date.now() - start) / 1000); + setState({ elapsed: seconds, isWarning: seconds >= WARNING_AT_SECONDS }); + if (seconds >= RECORDING_MAX_DURATION_SECONDS && !stopped) { + stopped = true; + requestIntent('stop'); + } + }, 250); + return () => { + clearInterval(interval); + setState({ elapsed: 0, isWarning: false }); + }; + }, [active, requestIntent]); + + return { elapsed, isWarning }; +} + +function RecordingTimer({ + elapsed, + isWarning, +}: { + elapsed: number; + isWarning: boolean; +}) { const minutes = Math.floor(elapsed / 60); const seconds = elapsed % 60; const display = `${minutes}:${seconds.toString().padStart(2, '0')}`; @@ -51,7 +90,14 @@ function RecordingTimer() { return (