"use client" import { useState, useRef, useEffect } from 'react' import { Button } from '@/components/ui/button' import { Mic, MicOff, Square, Send, Trash2 } from 'lucide-react' import { useAuth } from '@/hooks/useAuth' interface VoiceRecorderProps { onSendVoice: (audioUrl: string, duration: number) => void } export function VoiceRecorder({ onSendVoice }: VoiceRecorderProps) { const { user } = useAuth() const [isRecording, setIsRecording] = useState(false) const [recordedAudio, setRecordedAudio] = useState(null) const [duration, setDuration] = useState(0) const [isPlaying, setIsPlaying] = useState(false) const [permission, setPermission] = useState<'granted' | 'denied' | 'prompt'>('prompt') const mediaRecorderRef = useRef(null) const audioChunksRef = useRef([]) const audioRef = useRef(null) const intervalRef = useRef(null) useEffect(() => { // Проверяем доступность микрофона if (typeof navigator !== 'undefined' && navigator.mediaDevices) { navigator.permissions.query({ name: 'microphone' as PermissionName }).then((result) => { setPermission(result.state as 'granted' | 'denied' | 'prompt') }).catch(() => { // Если permissions API недоступен, оставляем prompt }) } }, []) const startRecording = async () => { try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }) setPermission('granted') const mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/webm;codecs=opus' }) 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 audioUrl = URL.createObjectURL(audioBlob) setRecordedAudio(audioUrl) // Останавливаем все треки для освобождения микрофона stream.getTracks().forEach(track => track.stop()) // Останавливаем таймер if (intervalRef.current) { clearInterval(intervalRef.current) intervalRef.current = null } } mediaRecorder.start() setIsRecording(true) setDuration(0) // Запускаем таймер записи intervalRef.current = setInterval(() => { setDuration(prev => prev + 1) }, 1000) } catch (error) { console.error('Error accessing microphone:', error) setPermission('denied') } } const stopRecording = () => { if (mediaRecorderRef.current && isRecording) { mediaRecorderRef.current.stop() setIsRecording(false) } } const cancelRecording = () => { if (mediaRecorderRef.current && isRecording) { mediaRecorderRef.current.stop() setIsRecording(false) } setRecordedAudio(null) setDuration(0) if (intervalRef.current) { clearInterval(intervalRef.current) intervalRef.current = null } } const playRecording = () => { if (recordedAudio) { if (audioRef.current) { audioRef.current.pause() audioRef.current.currentTime = 0 } audioRef.current = new Audio(recordedAudio) audioRef.current.play() setIsPlaying(true) audioRef.current.onended = () => { setIsPlaying(false) } } } const sendVoiceMessage = async () => { if (!recordedAudio || !user?.id) return try { // Конвертируем Blob в File для загрузки const response = await fetch(recordedAudio) const blob = await response.blob() const file = new File([blob], `voice-${Date.now()}.webm`, { type: 'audio/webm' }) // Загружаем в S3 const formData = new FormData() formData.append('file', file) formData.append('userId', user.id) const uploadResponse = await fetch('/api/upload-voice', { method: 'POST', body: formData }) if (!uploadResponse.ok) { throw new Error('Failed to upload voice message') } const result = await uploadResponse.json() // Отправляем голосовое сообщение onSendVoice(result.url, duration) // Очищаем состояние setRecordedAudio(null) setDuration(0) } catch (error) { console.error('Error sending voice message:', error) } } const formatDuration = (seconds: number) => { const mins = Math.floor(seconds / 60) const secs = seconds % 60 return `${mins}:${secs.toString().padStart(2, '0')}` } if (permission === 'denied') { return (
Доступ к микрофону запрещен
) } return (
{!recordedAudio ? ( // Состояние записи <> {!isRecording ? ( ) : ( <>
{formatDuration(duration)}
)} ) : ( // Состояние воспроизведения и отправки
{formatDuration(duration)}
)}
) }