feat(SearchCommoServe): add CORS handling and loading state for search errors
This commit is contained in:
parent
c3af4406bf
commit
8957254471
|
|
@ -231,6 +231,7 @@ export default function SearchAssembly64({ config, setConfig, onClose }: SearchA
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSearch = () => doSearch(query, categoryFilter, 0);
|
const handleSearch = () => doSearch(query, categoryFilter, 0);
|
||||||
|
const handleLoadMore = () => doSearch(query, categoryFilter, offset, true);
|
||||||
|
|
||||||
// Build an AQL token for a preset value and append/replace it in the query.
|
// Build an AQL token for a preset value and append/replace it in the query.
|
||||||
// Tokens that already contain a colon (e.g. 'subcat:c64comdemos') are
|
// Tokens that already contain a colon (e.g. 'subcat:c64comdemos') are
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,7 @@ export default function SearchCommoServe({ config, setConfig, onClose }: SearchC
|
||||||
const [isSearching, setIsSearching] = useState(false);
|
const [isSearching, setIsSearching] = useState(false);
|
||||||
const [isLoadingMore, setIsLoadingMore] = useState(false);
|
const [isLoadingMore, setIsLoadingMore] = useState(false);
|
||||||
const [searchError, setSearchError] = useState<string | null>(null);
|
const [searchError, setSearchError] = useState<string | null>(null);
|
||||||
|
const [corsBlocked, setCorsBlocked] = useState(false);
|
||||||
const [categoryFilter, setCategoryFilter] = useState<number | null>(() => _store.categoryFilter);
|
const [categoryFilter, setCategoryFilter] = useState<number | null>(() => _store.categoryFilter);
|
||||||
|
|
||||||
const [categories, setCategories] = useState<CategoryMapping[]>([]);
|
const [categories, setCategories] = useState<CategoryMapping[]>([]);
|
||||||
|
|
@ -186,8 +187,9 @@ export default function SearchCommoServe({ config, setConfig, onClose }: SearchC
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
leetJson<CategoryMapping[]>('/search/categories').then(setCategories).catch(() => {});
|
const isCors = (e: any) => /failed to fetch|networkerror/i.test(e?.message ?? '');
|
||||||
leetJson<PresetGroup[]>('/search/aql/presets').then(setPresets).catch(() => {});
|
leetJson<CategoryMapping[]>('/search/categories').then(setCategories).catch(e => { if (isCors(e)) setCorsBlocked(true); });
|
||||||
|
leetJson<PresetGroup[]>('/search/aql/presets').then(setPresets).catch(e => { if (isCors(e)) setCorsBlocked(true); });
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const categoryName = useMemo(() => {
|
const categoryName = useMemo(() => {
|
||||||
|
|
@ -215,7 +217,8 @@ export default function SearchCommoServe({ config, setConfig, onClose }: SearchC
|
||||||
setHasMore(data.length === PAGE_SIZE);
|
setHasMore(data.length === PAGE_SIZE);
|
||||||
setHasSearched(true);
|
setHasSearched(true);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
setSearchError(e?.message ?? 'Search failed');
|
if (/failed to fetch|networkerror/i.test(e?.message ?? '')) setCorsBlocked(true);
|
||||||
|
else setSearchError(e?.message ?? 'Search failed');
|
||||||
} finally {
|
} finally {
|
||||||
setIsSearching(false);
|
setIsSearching(false);
|
||||||
setIsLoadingMore(false);
|
setIsLoadingMore(false);
|
||||||
|
|
@ -397,14 +400,25 @@ export default function SearchCommoServe({ config, setConfig, onClose }: SearchC
|
||||||
className="flex-1 overflow-y-auto"
|
className="flex-1 overflow-y-auto"
|
||||||
onScroll={e => { _store.scrollTop = (e.currentTarget as HTMLDivElement).scrollTop; }}
|
onScroll={e => { _store.scrollTop = (e.currentTarget as HTMLDivElement).scrollTop; }}
|
||||||
>
|
>
|
||||||
{isSearching && !hasSearched && (
|
{corsBlocked && (
|
||||||
|
<div className="flex flex-col items-center justify-center py-16 px-8 gap-3 text-center">
|
||||||
|
<span className="text-3xl">🚫</span>
|
||||||
|
<p className="text-sm font-medium text-neutral-700">CommoServe is not accessible</p>
|
||||||
|
<p className="text-xs text-neutral-400 leading-relaxed">
|
||||||
|
The CommoServe server does not allow requests from this browser origin (CORS policy).
|
||||||
|
This service may only be reachable directly from your Meatloaf device.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!corsBlocked && isSearching && !hasSearched && (
|
||||||
<div className="flex flex-col items-center justify-center py-16 gap-3">
|
<div className="flex flex-col items-center justify-center py-16 gap-3">
|
||||||
<Loader2 className="w-8 h-8 text-blue-500 animate-spin" />
|
<Loader2 className="w-8 h-8 text-blue-500 animate-spin" />
|
||||||
<p className="text-sm text-neutral-500">Searching…</p>
|
<p className="text-sm text-neutral-500">Searching…</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{searchError && (
|
{!corsBlocked && searchError && (
|
||||||
<div className="p-6 text-center">
|
<div className="p-6 text-center">
|
||||||
<p className="text-sm text-red-500">{searchError}</p>
|
<p className="text-sm text-red-500">{searchError}</p>
|
||||||
<button onClick={handleSearch} className="mt-2 inline-flex items-center gap-1 text-xs text-blue-600">
|
<button onClick={handleSearch} className="mt-2 inline-flex items-center gap-1 text-xs text-blue-600">
|
||||||
|
|
@ -413,7 +427,7 @@ export default function SearchCommoServe({ config, setConfig, onClose }: SearchC
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!searchError && hasSearched && (
|
{!corsBlocked && !searchError && hasSearched && (
|
||||||
<>
|
<>
|
||||||
{results.length > 0 ? (
|
{results.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
|
|
@ -487,7 +501,7 @@ export default function SearchCommoServe({ config, setConfig, onClose }: SearchC
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!hasSearched && !isSearching && (
|
{!corsBlocked && !hasSearched && !isSearching && (
|
||||||
<div className="py-16 text-center px-6">
|
<div className="py-16 text-center px-6">
|
||||||
<Search className="w-10 h-10 mx-auto mb-3 text-neutral-300" />
|
<Search className="w-10 h-10 mx-auto mb-3 text-neutral-300" />
|
||||||
<p className="text-sm font-medium text-neutral-600 mb-1">Search the CommoServe database</p>
|
<p className="text-sm font-medium text-neutral-600 mb-1">Search the CommoServe database</p>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user