import { useState, useRef } from "react"; import { ThumbsUp, ThumbsDown, Copy, Check, Loader2, Volume2, VolumeX } from "lucide-react"; import { replayAudio } from "../../../audio/AudioPlayer"; import { textToSpeech } from "../../../services/voiceApi"; interface FeedbackWidgetProps { messageId: string; content: string; audioText?: string; audioChunks?: ArrayBuffer[]; audioSampleRate?: number; } export default function FeedbackWidget({ content, audioText, audioChunks, audioSampleRate, }: FeedbackWidgetProps) { const [thumbsUp, setThumbsUp] = useState(false); const [thumbsDown, setThumbsDown] = useState(false); const [copyState, setCopyState] = useState<"idle" | "loading" | "success">("idle"); const [speakerState, setSpeakerState] = useState<"idle" | "loading" | "playing">("idle"); const cancelPlayRef = useRef<(() => void) | null>(null); const handleThumbsUp = () => { setThumbsUp((v) => !v); if (thumbsDown) setThumbsDown(false); }; const handleThumbsDown = () => { setThumbsDown((v) => !v); if (thumbsUp) setThumbsUp(false); }; const handleCopy = async () => { if (copyState !== "idle") return; setCopyState("loading"); try { await navigator.clipboard.writeText(content); setCopyState("success"); setTimeout(() => setCopyState("idle"), 2000); } catch { setCopyState("idle"); } }; const handleSpeaker = async () => { if (speakerState === "playing") { cancelPlayRef.current?.(); cancelPlayRef.current = null; setSpeakerState("idle"); return; } if (speakerState === "loading") return; if (audioChunks && audioChunks.length > 0) { // Voice mode: replay pre-stored audio immediately setSpeakerState("playing"); const cancel = replayAudio(audioChunks, audioSampleRate ?? 24000); cancelPlayRef.current = cancel; // Calculate approx duration to reset state const totalSamples = audioChunks.reduce((acc, c) => acc + c.byteLength / 2, 0); const duration = (totalSamples / (audioSampleRate ?? 24000)) * 1000; setTimeout(() => { cancelPlayRef.current = null; setSpeakerState("idle"); }, duration + 200); } else { // Text mode: request TTS now setSpeakerState("loading"); try { const { pcm, sampleRate } = await textToSpeech(audioText || content); setSpeakerState("playing"); const cancel = replayAudio([pcm], sampleRate); cancelPlayRef.current = cancel; const duration = (pcm.byteLength / 2 / sampleRate) * 1000; setTimeout(() => { cancelPlayRef.current = null; setSpeakerState("idle"); }, duration + 200); } catch { setSpeakerState("idle"); } } }; return (