import { useEffect, useRef, useState } from "react"; import { deleteVoice, listVoices, setFavorite, type VoiceRecord } from "@/lib/idb"; import { cn } from "@/lib/utils"; type Props = { selectedId?: number; onSelect: (v: VoiceRecord) => void; refreshKey?: number; }; export default function VoiceLibrary({ selectedId, onSelect, refreshKey }: Props) { const [voices, setVoices] = useState([]); const [playingId, setPlayingId] = useState(null); const audioRef = useRef(null); const urlRef = useRef(null); useEffect(() => { listVoices().then(setVoices); }, [refreshKey]); useEffect(() => { return () => { audioRef.current?.pause(); if (urlRef.current) URL.revokeObjectURL(urlRef.current); }; }, []); function stop() { audioRef.current?.pause(); audioRef.current = null; if (urlRef.current) { URL.revokeObjectURL(urlRef.current); urlRef.current = null; } setPlayingId(null); } function play(v: VoiceRecord) { stop(); const url = URL.createObjectURL(v.blob); const audio = new Audio(url); audio.onended = () => stop(); audio.onerror = () => stop(); audioRef.current = audio; urlRef.current = url; setPlayingId(v.id ?? null); audio.play().catch(() => stop()); } function toggle(v: VoiceRecord) { if (playingId === v.id) stop(); else play(v); } if (voices.length === 0) { return (

Voices will appear here once you upload or record one.

); } return (
    {voices.map((v, i) => { const isPlaying = playingId === v.id; return (
  • {String(i + 1).padStart(2, "0")}
  • ); })}
); }