"use client" import { useState, useRef, useCallback, useEffect, useMemo } from "react" import { cn } from "@/lib/utils" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar" import { ScrollArea } from "@/components/ui/scroll-area" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { Search, Send, Phone, MoreHorizontal, Paperclip, Smile, Flag, Ban, Trash2, Image, FileIcon, X, Mic, Square, Play, Pause, Check, CheckCheck, CornerDownRight, Forward, Pencil, Download, Undo2, } from "lucide-react" import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, DialogClose, } from "@/components/ui/dialog" import { Label } from "@/components/ui/label" import { Textarea } from "@/components/ui/textarea" import { useTheme } from "next-themes" import { useUser } from "@/providers/user-provider" import { toast } from "sonner" import { io, Socket } from "socket.io-client" import VoiceCallModal from "@/components/chats/voice-call-modal" import MediaPicker from "@/components/chats/media-picker" const activeAudioRef: { current: HTMLAudioElement | null } = { current: null } const previewAudioRef: { current: HTMLAudioElement | null } = { current: null } const previewAnimRef: { current: number } = { current: 0 } function VoiceMessagePlayer({ src, initialDuration, isOwn }: { src: string; initialDuration: number; isOwn?: boolean }) { const [playing, setPlaying] = useState(false) const [current, setCurrent] = useState(0) const [duration, setDuration] = useState(initialDuration) const audioRef = useRef(null) const animRef = useRef(0) const progRef = useRef(null) useEffect(() => { const audio = audioRef.current if (!audio) return const onLoaded = () => { if (isFinite(audio.duration)) setDuration(audio.duration) } const onEnded = () => { setPlaying(false); setCurrent(0) } audio.addEventListener("loadedmetadata", onLoaded) audio.addEventListener("ended", onEnded) return () => { audio.removeEventListener("loadedmetadata", onLoaded); audio.removeEventListener("ended", onEnded) } }, [src]) useEffect(() => { if (!playing) { cancelAnimationFrame(animRef.current); return } const tick = () => { if (audioRef.current) setCurrent(audioRef.current.currentTime); animRef.current = requestAnimationFrame(tick) } animRef.current = requestAnimationFrame(tick) return () => cancelAnimationFrame(animRef.current) }, [playing]) const toggle = () => { if (!audioRef.current) return if (playing) { audioRef.current.pause(); setPlaying(false) } else { if (activeAudioRef.current && activeAudioRef.current !== audioRef.current) { activeAudioRef.current.pause() } activeAudioRef.current = audioRef.current audioRef.current.play(); setPlaying(true) } } const seek = (e: React.ChangeEvent) => { const val = parseFloat(e.target.value) if (audioRef.current) { audioRef.current.currentTime = val; setCurrent(val) } } const replay = () => { if (!audioRef.current) return audioRef.current.currentTime = 0; setCurrent(0) if (!playing) { audioRef.current.play(); setPlaying(true) } } const displayDuration = isFinite(duration) && duration > 0 ? duration : initialDuration const pct = displayDuration > 0 ? (current / displayDuration) * 100 : 0 const fmt = (s: number) => { const secs = isFinite(s) ? Math.max(0, Math.floor(s)) : 0; return `${Math.floor(secs / 60)}:${(secs % 60).toString().padStart(2, "0")}` } const barCount = 28 const waveform = Array.from({ length: barCount }, (_, i) => { const peak = 0.15 + Math.sin(i * 1.1) * 0.35 + Math.sin(i * 2.3) * 0.2 + Math.sin(i * 0.7) * 0.3 return Math.max(0.15, Math.min(1, peak)) }) return (