import { useState, useRef, useEffect } from 'react'; import { FiMic, FiSquare, FiPlay, FiPause, FiTrash2 } from 'react-icons/fi'; export interface AudioRecorderProps { onRecordingComplete: (audioBlob: Blob) => void; onError?: (error: string) => void; disabled?: boolean; className?: string; } export function AudioRecorder({ onRecordingComplete, onError, disabled = false, className = '' }: AudioRecorderProps) { const [isRecording, setIsRecording] = useState(false); const [isPaused, setIsPaused] = useState(false); const [recordingTime, setRecordingTime] = useState(0); const [audioUrl, setAudioUrl] = useState(null); const [isPlaying, setIsPlaying] = useState(false); const mediaRecorderRef = useRef(null); const audioChunksRef = useRef([]); const streamRef = useRef(null); const timerRef = useRef(null); const audioRef = useRef(null); useEffect(() => { return () => { if (timerRef.current) { clearInterval(timerRef.current); } if (streamRef.current) { streamRef.current.getTracks().forEach(track => track.stop()); } if (audioUrl) { URL.revokeObjectURL(audioUrl); } }; }, [audioUrl]); const formatTime = (seconds: number): string => { const mins = Math.floor(seconds / 60); const secs = seconds % 60; return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; }; const startRecording = async () => { try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); streamRef.current = stream; const mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm', }); mediaRecorderRef.current = mediaRecorder; audioChunksRef.current = []; mediaRecorder.ondataavailable = event => { if (event.data.size > 0) { audioChunksRef.current.push(event.data); } }; mediaRecorder.onstop = () => { const audioBlob = new Blob(audioChunksRef.current, { type: 'audio/webm' }); const url = URL.createObjectURL(audioBlob); setAudioUrl(url); onRecordingComplete(audioBlob); }; mediaRecorder.start(); setIsRecording(true); setIsPaused(false); setRecordingTime(0); timerRef.current = setInterval(() => { setRecordingTime(prev => prev + 1); }, 1000); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Failed to access microphone'; onError?.(errorMessage); console.error('Error starting recording:', error); } }; const stopRecording = () => { if (mediaRecorderRef.current && isRecording) { mediaRecorderRef.current.stop(); if (streamRef.current) { streamRef.current.getTracks().forEach(track => track.stop()); } setIsRecording(false); setIsPaused(false); if (timerRef.current) { clearInterval(timerRef.current); timerRef.current = null; } } }; const pauseRecording = () => { if (mediaRecorderRef.current && isRecording && !isPaused) { mediaRecorderRef.current.pause(); setIsPaused(true); if (timerRef.current) { clearInterval(timerRef.current); timerRef.current = null; } } }; const resumeRecording = () => { if (mediaRecorderRef.current && isRecording && isPaused) { mediaRecorderRef.current.resume(); setIsPaused(false); timerRef.current = setInterval(() => { setRecordingTime(prev => prev + 1); }, 1000); } }; const playRecording = () => { if (audioUrl && audioRef.current) { audioRef.current.play(); setIsPlaying(true); } }; const pausePlayback = () => { if (audioRef.current) { audioRef.current.pause(); setIsPlaying(false); } }; const handleAudioEnded = () => { setIsPlaying(false); }; const clearRecording = () => { if (audioUrl) { URL.revokeObjectURL(audioUrl); setAudioUrl(null); } setRecordingTime(0); setIsPlaying(false); if (audioRef.current) { audioRef.current.pause(); audioRef.current.currentTime = 0; } }; return (
{!isRecording && !audioUrl && ( )} {isRecording && ( <>
{formatTime(recordingTime)}
)} {audioUrl && !isRecording && ( <> )}
{audioUrl && (
)}
); }