From 3e89d2998588f8e35c524e523ba2ee853f0f6475 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Mon, 20 Apr 2026 11:31:17 +0200 Subject: refactor: restructure files to their domains, new preview overlay system, and dep graph --- apps/web/src/sounds/use-sound-search.ts | 154 ++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 apps/web/src/sounds/use-sound-search.ts (limited to 'apps/web/src/sounds/use-sound-search.ts') diff --git a/apps/web/src/sounds/use-sound-search.ts b/apps/web/src/sounds/use-sound-search.ts new file mode 100644 index 00000000..79bc3110 --- /dev/null +++ b/apps/web/src/sounds/use-sound-search.ts @@ -0,0 +1,154 @@ +import { useEffect } from "react"; +import { useSoundsStore } from "@/sounds/sounds-store"; + +export function useSoundSearch({ + query, + commercialOnly, +}: { + query: string; + commercialOnly: boolean; +}) { + const { + searchResults, + isSearching, + searchError, + lastSearchQuery, + currentPage, + hasNextPage, + isLoadingMore, + totalCount, + setSearchResults, + setSearching, + setSearchError, + setLastSearchQuery, + setCurrentPage, + setHasNextPage, + setTotalCount, + setLoadingMore, + appendSearchResults, + appendTopSounds, + resetPagination, + } = useSoundsStore(); + + const loadMore = async () => { + if (isLoadingMore || !hasNextPage) return; + + try { + setLoadingMore({ loading: true }); + const nextPage = currentPage + 1; + + const searchParams = new URLSearchParams({ + page: nextPage.toString(), + type: "effects", + }); + + if (query.trim()) { + searchParams.set("q", query); + } + + searchParams.set("commercial_only", commercialOnly.toString()); + const response = await fetch( + `/api/sounds/search?${searchParams.toString()}`, + ); + + if (response.ok) { + const data = await response.json(); + + if (query.trim()) { + appendSearchResults(data.results); + } else { + appendTopSounds(data.results); + } + + setCurrentPage({ page: nextPage }); + setHasNextPage({ hasNext: !!data.next }); + setTotalCount(data.count); + } else { + setSearchError({ error: `Load more failed: ${response.status}` }); + } + } catch (err) { + setSearchError({ + error: err instanceof Error ? err.message : "Load more failed", + }); + } finally { + setLoadingMore({ loading: false }); + } + }; + + useEffect(() => { + if (!query.trim()) { + setSearchResults({ results: [] }); + setSearchError({ error: null }); + setLastSearchQuery({ query: "" }); + return; + } + + if (query === lastSearchQuery && searchResults.length > 0) { + return; + } + + let ignore = false; + + const timeoutId = setTimeout(async () => { + try { + setSearching({ searching: true }); + setSearchError({ error: null }); + resetPagination(); + + const response = await fetch( + `/api/sounds/search?q=${encodeURIComponent(query)}&type=effects&page=1`, + ); + + if (!ignore) { + if (response.ok) { + const data = await response.json(); + setSearchResults({ results: data.results }); + setLastSearchQuery({ query: query }); + setHasNextPage({ hasNext: !!data.next }); + setTotalCount({ count: data.count }); + setCurrentPage({ page: 1 }); + } else { + setSearchError({ error: `Search failed: ${response.status}` }); + } + } + } catch (err) { + if (!ignore) { + setSearchError({ + error: err instanceof Error ? err.message : "Search failed", + }); + } + } finally { + if (!ignore) { + setSearching({ searching: false }); + } + } + }, 300); + + return () => { + clearTimeout(timeoutId); + ignore = true; + }; + }, [ + query, + lastSearchQuery, + searchResults.length, + setSearchResults, + setSearching, + setSearchError, + setLastSearchQuery, + setCurrentPage, + setHasNextPage, + setTotalCount, + resetPagination, + ]); + + return { + results: searchResults, + isLoading: isSearching, + error: searchError, + loadMore, + hasNextPage, + isLoadingMore, + totalCount, + }; +} -- cgit v1.3.1