"use client"; import { Input } from "@/components/ui/input"; import { useState, useMemo, useEffect } from "react"; import { Separator } from "@/components/ui/separator"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { PlayIcon, PauseIcon, HeartIcon, PlusIcon, ListFilter, } from "lucide-react"; import { useSoundsStore } from "@/stores/sounds-store"; import { useSoundSearch } from "@/hooks/use-sound-search"; import type { SoundEffect, SavedSound } from "@/types/sounds"; import { Button } from "@/components/ui/button"; import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuCheckboxItem, } from "@/components/ui/dropdown-menu"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { cn } from "@/utils/ui"; import { useInfiniteScroll } from "@/hooks/use-infinite-scroll"; export function SoundsView() { return (
Sound effects Songs Saved
); } function SoundEffectsView() { const { topSoundEffects, isLoading, searchQuery, setSearchQuery, scrollPosition, setScrollPosition, loadSavedSounds, isSoundSaved, toggleSavedSound, showCommercialOnly, toggleCommercialFilter, hasLoaded, setTopSoundEffects, setLoading, setError, setHasLoaded, setCurrentPage, setHasNextPage, setTotalCount, } = useSoundsStore(); const { results: searchResults, isLoading: isSearching, loadMore, hasNextPage, isLoadingMore, } = useSoundSearch({ query: searchQuery, commercialOnly: showCommercialOnly }); // Audio playback state const [playingId, setPlayingId] = useState(null); const [audioElement, setAudioElement] = useState( null ); const { scrollAreaRef, handleScroll } = useInfiniteScroll({ onLoadMore: loadMore, hasMore: hasNextPage, isLoading: isLoadingMore || isSearching, }); useEffect(() => { loadSavedSounds(); if (!hasLoaded) { let ignore = false; const fetchTopSounds = async () => { try { if (!ignore) { setLoading({ loading: true }); setError({ error: null }); } const response = await fetch( "/api/sounds/search?page_size=50&sort=downloads" ); if (!ignore) { if (!response.ok) { throw new Error(`Failed to fetch: ${response.status}`); } const data = await response.json(); setTopSoundEffects({ sounds: data.results }); setHasLoaded({ loaded: true }); setCurrentPage({ page: 1 }); setHasNextPage({ hasNext: !!data.next }); setTotalCount({ count: data.count }); } } catch (error) { if (!ignore) { console.error("Failed to fetch top sounds:", error); setError({ error: error instanceof Error ? error.message : "Failed to load sounds", }); } } finally { if (!ignore) { setLoading({ loading: false }); } } }; const timeoutId = setTimeout(fetchTopSounds, 100); return () => { clearTimeout(timeoutId); ignore = true; }; } if (scrollAreaRef.current && scrollPosition > 0) { const timeoutId = setTimeout(() => { scrollAreaRef.current?.scrollTo({ top: scrollPosition }); }, 100); return () => clearTimeout(timeoutId); } }, [ hasLoaded, setTopSoundEffects, setLoading, setError, setHasLoaded, setCurrentPage, setHasNextPage, setTotalCount, ]); const handleScrollWithPosition = (event: React.UIEvent) => { const { scrollTop } = event.currentTarget; setScrollPosition({ position: scrollTop }); handleScroll(event); }; const displayedSounds = useMemo(() => { const sounds = searchQuery ? searchResults : topSoundEffects; return sounds; }, [searchQuery, searchResults, topSoundEffects]); const playSound = (sound: SoundEffect) => { if (playingId === sound.id) { audioElement?.pause(); setPlayingId(null); return; } // Stop previous sound audioElement?.pause(); if (sound.previewUrl) { const audio = new Audio(sound.previewUrl); audio.addEventListener("ended", () => { setPlayingId(null); }); audio.addEventListener("error", (e) => { setPlayingId(null); }); audio.play().catch((error) => { setPlayingId(null); }); setAudioElement(audio); setPlayingId(sound.id); } }; return (
setSearchQuery({ query: e.target.value })} showClearIcon onClear={() => setSearchQuery({ query: "" })} /> toggleCommercialFilter()} > Show only commercially licensed
{showCommercialOnly ? "Only showing sounds licensed for commercial use" : "Showing all sounds regardless of license"}
{isLoading && !searchQuery && (
Loading sounds...
)} {isSearching && searchQuery && (
Searching...
)} {displayedSounds.map((sound) => ( playSound(sound)} isSaved={isSoundSaved({ soundId: sound.id })} onToggleSaved={() => toggleSavedSound({ soundEffect: sound })} /> ))} {!isLoading && !isSearching && displayedSounds.length === 0 && (
{searchQuery ? "No sounds found" : "No sounds available"}
)} {isLoadingMore && (
Loading more sounds...
)}
); } function SavedSoundsView() { const { savedSounds, isLoadingSavedSounds, savedSoundsError, loadSavedSounds, isSoundSaved, toggleSavedSound, clearSavedSounds, } = useSoundsStore(); // Audio playback state const [playingId, setPlayingId] = useState(null); const [audioElement, setAudioElement] = useState( null ); // Clear confirmation dialog state const [showClearDialog, setShowClearDialog] = useState(false); // Load saved sounds when tab becomes active useEffect(() => { loadSavedSounds(); }, [loadSavedSounds]); const playSound = (sound: SavedSound) => { if (playingId === sound.id) { audioElement?.pause(); setPlayingId(null); return; } // Stop previous sound audioElement?.pause(); if (sound.previewUrl) { const audio = new Audio(sound.previewUrl); audio.addEventListener("ended", () => { setPlayingId(null); }); audio.addEventListener("error", (e) => { setPlayingId(null); }); audio.play().catch((error) => { setPlayingId(null); }); setAudioElement(audio); setPlayingId(sound.id); } }; // Convert SavedSound to SoundEffect for compatibility with AudioItem const convertToSoundEffect = (savedSound: SavedSound): SoundEffect => ({ id: savedSound.id, name: savedSound.name, description: "", url: "", previewUrl: savedSound.previewUrl, downloadUrl: savedSound.downloadUrl, duration: savedSound.duration, filesize: 0, type: "audio", channels: 0, bitrate: 0, bitdepth: 0, samplerate: 0, username: savedSound.username, tags: savedSound.tags, license: savedSound.license, created: savedSound.savedAt, downloads: 0, rating: 0, ratingCount: 0, }); if (isLoadingSavedSounds) { return (
Loading saved sounds...
); } if (savedSoundsError) { return (
Error: {savedSoundsError}
); } if (savedSounds.length === 0) { return (

No saved sounds

Click the heart icon on any sound to save it here

); } return (

{savedSounds.length} saved{" "} {savedSounds.length === 1 ? "sound" : "sounds"}

Clear all saved sounds? This will permanently remove all {savedSounds.length} saved sounds from your collection. This action cannot be undone.
{savedSounds.map((sound) => ( playSound(sound)} isSaved={isSoundSaved({ soundId: sound.id })} onToggleSaved={() => toggleSavedSound({ soundEffect: convertToSoundEffect(sound) }) } /> ))}
); } function SongsView() { return
Songs
; } interface AudioItemProps { sound: SoundEffect; isPlaying: boolean; onPlay: () => void; isSaved: boolean; onToggleSaved: () => void; } function AudioItem({ sound, isPlaying, onPlay, isSaved, onToggleSaved, }: AudioItemProps) { const { addSoundToTimeline } = useSoundsStore(); const handleClick = () => { onPlay(); }; const handleSaveClick = (e: React.MouseEvent) => { e.stopPropagation(); onToggleSaved(); }; const handleAddToTimeline = async (e: React.MouseEvent) => { e.stopPropagation(); await addSoundToTimeline({ sound }); }; return (
{isPlaying ? ( ) : ( )}

{sound.name}

{sound.username}
); }