"use client"; import { useEffect, useState } from "react"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Input } from "@/components/ui/input"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Separator } from "@/components/ui/separator"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { useInfiniteScroll } from "@/hooks/use-infinite-scroll"; import { useSoundSearch } from "@/sounds/use-sound-search"; import { useSoundsStore } from "@/sounds/sounds-store"; import type { SavedSound, SoundEffect } from "@/sounds/types"; import { cn } from "@/utils/ui"; import { FavouriteIcon, FilterMailIcon, PauseIcon, PlayIcon, PlusSignIcon, } from "@hugeicons/core-free-icons"; import { HugeiconsIcon } from "@hugeicons/react"; export function SoundsView() { return (
Sound effects Saved
); } function SoundEffectsView() { const { topSoundEffects, isLoading, searchQuery, setSearchQuery, scrollPosition, setScrollPosition, loadSavedSounds, showCommercialOnly, toggleCommercialFilter, hasLoaded, setTopSoundEffects, setLoading, setError, setHasLoaded, setCurrentPage, setHasNextPage, setTotalCount, } = useSoundsStore(); const { results: searchResults, isLoading: isSearching, loadMore, hasNextPage, isLoadingMore, } = useSoundSearch({ query: searchQuery, commercialOnly: showCommercialOnly, }); const [playingId, setPlayingId] = useState(null); const [audioElement, setAudioElement] = useState( null, ); const { scrollAreaRef, handleScroll } = useInfiniteScroll({ onLoadMore: loadMore, hasMore: hasNextPage, isLoading: isLoadingMore || isSearching, }); useEffect(() => { loadSavedSounds(); }, [loadSavedSounds]); useEffect(() => { if (hasLoaded) { return; } let shouldIgnore = false; const fetchTopSounds = async () => { try { if (!shouldIgnore) { setLoading({ loading: true }); setError({ error: null }); } const response = await fetch( "/api/sounds/search?page_size=50&sort=downloads", ); if (!shouldIgnore) { 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 (!shouldIgnore) { console.error("Failed to fetch top sounds:", error); setError({ error: error instanceof Error ? error.message : "Failed to load sounds", }); } } finally { if (!shouldIgnore) { setLoading({ loading: false }); } } }; const timeoutId = setTimeout(fetchTopSounds, 100, {}); return () => { shouldIgnore = true; clearTimeout(timeoutId); }; }, [ hasLoaded, setTopSoundEffects, setLoading, setError, setHasLoaded, setCurrentPage, setHasNextPage, setTotalCount, ]); useEffect(() => { if (!scrollAreaRef.current || scrollPosition <= 0) { return; } const restoreScrollPosition = () => { scrollAreaRef.current?.scrollTo({ top: scrollPosition }); }; const timeoutId = setTimeout(restoreScrollPosition, 100, {}); return () => clearTimeout(timeoutId); }, [scrollPosition, scrollAreaRef]); const handleScrollWithPosition = ({ currentTarget, }: React.UIEvent) => { const { scrollTop } = currentTarget; setScrollPosition({ position: scrollTop }); handleScroll({ currentTarget } as React.UIEvent); }; const displayedSounds = searchQuery ? searchResults : topSoundEffects; const playSound = ({ sound }: { sound: SoundEffect }) => { if (playingId === sound.id) { audioElement?.pause(); setPlayingId(null); return; } audioElement?.pause(); if (sound.previewUrl) { const audio = new Audio(sound.previewUrl); audio.addEventListener("ended", () => { setPlayingId(null); }); audio.addEventListener("error", () => { setPlayingId(null); }); audio.play().catch((error) => { console.error("Failed to play sound preview:", error); setPlayingId(null); }); setAudioElement(audio); setPlayingId(sound.id); } }; return (
setSearchQuery({ query: currentTarget.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) => ( ))} {!isLoading && !isSearching && displayedSounds.length === 0 && (
{searchQuery ? "No sounds found" : "No sounds available"}
)} {isLoadingMore && (
Loading more sounds...
)}
); } function SavedSoundsView() { const { savedSounds, isLoadingSavedSounds, savedSoundsError, loadSavedSounds, clearSavedSounds, } = useSoundsStore(); const [playingId, setPlayingId] = useState(null); const [audioElement, setAudioElement] = useState( null, ); const [showClearDialog, setShowClearDialog] = useState(false); useEffect(() => { loadSavedSounds(); }, [loadSavedSounds]); const playSound = ({ sound }: { sound: SoundEffect }) => { if (playingId === sound.id) { audioElement?.pause(); setPlayingId(null); return; } audioElement?.pause(); if (sound.previewUrl) { const audio = new Audio(sound.previewUrl); audio.addEventListener("ended", () => { setPlayingId(null); }); audio.addEventListener("error", () => { setPlayingId(null); }); audio.play().catch((error) => { console.error("Failed to play sound preview:", error); setPlayingId(null); }); setAudioElement(audio); setPlayingId(sound.id); } }; const convertToSoundEffect = ({ savedSound, }: { 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) => ( ))}
); } interface AudioItemProps { sound: SoundEffect; isPlaying: boolean; onPlay: ({ sound }: { sound: SoundEffect }) => void; } function AudioItem({ sound, isPlaying, onPlay }: AudioItemProps) { const { addSoundToTimeline, isSoundSaved, toggleSavedSound } = useSoundsStore(); const isSaved = isSoundSaved({ soundId: sound.id }); const handleClick = () => { onPlay({ sound }); }; const handleSaveClick = ({ stopPropagation, }: React.MouseEvent) => { stopPropagation(); toggleSavedSound({ soundEffect: sound }); }; const handleAddToTimeline = async ({ stopPropagation, }: React.MouseEvent) => { stopPropagation(); await addSoundToTimeline({ sound }); }; return (
); }