From 936dc5db12e47781c69e446c942e3b8c8a1a0c2b Mon Sep 17 00:00:00 2001 From: Jaime Idolpx Date: Mon, 15 Jun 2026 15:56:03 -0400 Subject: [PATCH] feat(SearchCommoServe): refactor search logic and enhance array extraction functionality --- src/app/components/SearchCommoServe.tsx | 103 ++++++++---------------- 1 file changed, 35 insertions(+), 68 deletions(-) diff --git a/src/app/components/SearchCommoServe.tsx b/src/app/components/SearchCommoServe.tsx index a865ab7..c278c0e 100644 --- a/src/app/components/SearchCommoServe.tsx +++ b/src/app/components/SearchCommoServe.tsx @@ -4,6 +4,7 @@ import { toast } from 'sonner'; import { humanFileSize, basename, joinPath, putFileContents } from '../webdav'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from './ui/dialog'; import { MarqueeText } from './ui/marquee-text'; +import { EntryIcon } from './MediaEntry'; // ─── API ────────────────────────────────────────────────────────────────────── @@ -16,7 +17,6 @@ function leetFetch(path: string, query?: Record) { if (query) Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v)); return fetch(url.toString(), { headers: { - 'User-Agent': 'Assembly Query', 'Client-Id': 'Commodore', }, }); @@ -29,6 +29,17 @@ async function leetJson(path: string, query?: Record): Promis return data as T; } +// CommoServe may wrap arrays in an object — unwrap the first array value found. +function extractArray(data: unknown): T[] { + if (Array.isArray(data)) return data as T[]; + if (data && typeof data === 'object') { + for (const v of Object.values(data as object)) { + if (Array.isArray(v) && v.length > 0) return v as T[]; + } + } + return []; +} + // ─── Types ──────────────────────────────────────────────────────────────────── interface ContentItem { @@ -55,13 +66,6 @@ interface ContentEntry { date: number; } -interface CategoryMapping { - id: number; - name?: string; - title?: string; - [k: string]: unknown; -} - interface PresetValue { aqlKey: string; name?: string; @@ -166,9 +170,6 @@ export default function SearchCommoServe({ config, setConfig, onClose }: SearchC const [isLoadingMore, setIsLoadingMore] = useState(false); const [searchError, setSearchError] = useState(null); const [corsBlocked, setCorsBlocked] = useState(false); - const [categoryFilter, setCategoryFilter] = useState(() => _store.categoryFilter); - - const [categories, setCategories] = useState([]); const [presets, setPresets] = useState([]); const [activePreset, setActivePreset] = useState(null); @@ -186,17 +187,16 @@ export default function SearchCommoServe({ config, setConfig, onClose }: SearchC const [sortDir, setSortDir] = useState(() => _store.sortDir); useEffect(() => { - _store.query = query; - _store.results = results; - _store.offset = offset; - _store.hasMore = hasMore; - _store.hasSearched = hasSearched; - _store.categoryFilter = categoryFilter; - _store.showFilter = showFilter; - _store.filterText = filterText; - _store.sortField = sortField; - _store.sortDir = sortDir; - }, [query, results, offset, hasMore, hasSearched, categoryFilter, showFilter, filterText, sortField, sortDir]); + _store.query = query; + _store.results = results; + _store.offset = offset; + _store.hasMore = hasMore; + _store.hasSearched = hasSearched; + _store.showFilter = showFilter; + _store.filterText = filterText; + _store.sortField = sortField; + _store.sortDir = sortDir; + }, [query, results, offset, hasMore, hasSearched, showFilter, filterText, sortField, sortDir]); useEffect(() => { if (_store.scrollTop > 0 && scrollRef.current) @@ -204,30 +204,22 @@ export default function SearchCommoServe({ config, setConfig, onClose }: SearchC }, []); useEffect(() => { - const isCors = (e: any) => /failed to fetch|networkerror/i.test(e?.message ?? ''); - leetJson('/search/categories').then(setCategories).catch(e => { if (isCors(e)) setCorsBlocked(true); }); - leetJson('/search/aql/presets').then(setPresets).catch(e => { if (isCors(e)) setCorsBlocked(true); }); + leetJson('/search/aql/presets').then(d => setPresets(extractArray(d))).catch(() => {}); }, []); - const categoryName = useMemo(() => { - const map: Record = {}; - for (const c of categories) map[c.id] = (c.name ?? c.title ?? String(c.id)) as string; - return map; - }, [categories]); - // ── Search ────────────────────────────────────────────────────────────────── - const doSearch = async (q: string, cat: number | null, fromOffset: number, append = false) => { + const doSearch = async (q: string, fromOffset: number, append = false) => { if (!append) setIsSearching(true); else setIsLoadingMore(true); setSearchError(null); try { - let aql = q.trim(); - if (cat !== null) aql = aql ? `${aql} category:${cat}` : `category:${cat}`; - const data = await leetJson( + const aql = q.trim(); + const raw = await leetJson( `/search/aql/${fromOffset}/${PAGE_SIZE}`, aql ? { query: aql } : undefined, ); + const data = extractArray(raw); if (append) setResults(prev => [...prev, ...data]); else setResults(data); setOffset(fromOffset + data.length); @@ -242,8 +234,8 @@ export default function SearchCommoServe({ config, setConfig, onClose }: SearchC } }; - const handleSearch = () => { setFilterText(''); doSearch(query, categoryFilter, 0); }; - const handleLoadMore = () => doSearch(query, categoryFilter, offset, true); + const handleSearch = () => { setFilterText(''); doSearch(query, 0); }; + const handleLoadMore = () => doSearch(query, offset, true); const applyPreset = (group: PresetGroup, value: PresetValue) => { const prefix = PRESET_PREFIX[group.type]; @@ -253,7 +245,7 @@ export default function SearchCommoServe({ config, setConfig, onClose }: SearchC const trimmed = query.trim(); const next = trimmed ? `${trimmed} ${token}` : token; setQuery(next); - doSearch(next, categoryFilter, 0); + doSearch(next, 0); }; // ── Filter / sort ──────────────────────────────────────────────────────────── @@ -309,8 +301,7 @@ export default function SearchCommoServe({ config, setConfig, onClose }: SearchC const downloadToSd = async (item: ContentItem, entry: ContentEntry) => { setDownloading(entry.id); try { - const url = downloadUrl(item, entry); - const res = await fetch(url); + const res = await fetch(downloadUrl(item, entry)); if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = await res.arrayBuffer(); const dest = joinPath(DOWNLOAD_DIR, entryFilename(entry)); @@ -350,6 +341,7 @@ export default function SearchCommoServe({ config, setConfig, onClose }: SearchC return s; }, [config]); + // ── Render ──────────────────────────────────────────────────────────────────── return ( @@ -432,28 +424,6 @@ export default function SearchCommoServe({ config, setConfig, onClose }: SearchC )} - {categories.length > 0 && ( -
- - {categories.map(c => { - const name = (c.name ?? c.title ?? String(c.id)) as string; - return ( - - ); - })} -
- )} {/* Filter + sort bar */} @@ -528,9 +498,7 @@ export default function SearchCommoServe({ config, setConfig, onClose }: SearchC {isSearching && } {visibleResults.length}{results.length !== visibleResults.length ? ` of ${results.length}` : ''} result{visibleResults.length !== 1 ? 's' : ''}{hasMore ? '+' : ''}

- {visibleResults.map(item => { - const catLabel = categoryName[item.category] ?? `Cat ${item.category}`; - return ( + {visibleResults.map(item => (
{item.name} - {catLabel} {item.place === 1 && }
@@ -580,8 +547,7 @@ export default function SearchCommoServe({ config, setConfig, onClose }: SearchC
- ); - })} + ))} {hasMore && (
@@ -681,6 +647,7 @@ export default function SearchCommoServe({ config, setConfig, onClose }: SearchC key={entry.id} className={`px-4 py-3 rounded-lg border flex items-center gap-3 ${isMounted ? 'border-blue-300 bg-blue-50' : 'border-neutral-200'}`} > +
{fname}
{humanFileSize(entry.size)}