Files
KVideo/lib/hooks/useHomePage.ts
T

145 lines
4.8 KiB
TypeScript

import { useState, useRef, useEffect, useCallback } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import { useSearchCache } from '@/lib/hooks/useSearchCache';
import { useParallelSearch } from '@/lib/hooks/useParallelSearch';
import { useSubscriptionSync } from '@/lib/hooks/useSubscriptionSync';
import { settingsStore, type SortOption } from '@/lib/store/settings-store';
export function useHomePage() {
useSubscriptionSync();
const router = useRouter();
const searchParams = useSearchParams();
const { loadFromCache, saveToCache } = useSearchCache();
const hasLoadedCache = useRef(false);
const hasSearchedWithSourcesRef = useRef(false);
const [query, setQuery] = useState('');
const [hasSearched, setHasSearched] = useState(false);
const [currentSortBy, setCurrentSortBy] = useState<SortOption>('default');
const onUrlUpdate = useCallback((q: string) => {
router.replace(`/?q=${encodeURIComponent(q)}`, { scroll: false });
}, [router]);
// Search stream hook
const {
loading,
results,
availableSources,
completedSources,
totalSources,
performSearch,
resetSearch,
loadCachedResults,
applySorting,
} = useParallelSearch(
saveToCache,
onUrlUpdate
);
// Core search execution function - extracted to eliminate duplication
const executeSearch = useCallback((searchQuery: string) => {
if (!searchQuery.trim()) return false;
const settings = settingsStore.getSettings();
const enabledSources = settings.sources.filter(s => s.enabled);
if (enabledSources.length === 0) {
return false;
}
performSearch(searchQuery, enabledSources, settings.sortBy);
hasSearchedWithSourcesRef.current = true;
return true;
}, [performSearch]);
// Re-sort results when sort preference changes
useEffect(() => {
if (hasSearched && results.length > 0) {
applySorting(currentSortBy);
}
}, [currentSortBy, applySorting, hasSearched, results.length]);
// Load sort preference on mount and subscribe to changes
useEffect(() => {
const updateSettings = () => {
const settings = settingsStore.getSettings();
// Update sort preference
if (settings.sortBy !== currentSortBy) {
setCurrentSortBy(settings.sortBy);
}
// Check if we need to re-trigger search due to new sources being loaded
// This fixes the issue where initial visit has 0 sources, then sources are loaded async
// but the search (or lack thereof) is already stuck with empty sources.
const enabledSources = settings.sources.filter(s => s.enabled);
const hasSources = enabledSources.length > 0;
// If we have a query, and we haven't searched with sources yet,
// and we suddenly have sources, trigger the search.
if (query && hasSources && !hasSearchedWithSourcesRef.current && !loading) {
if (executeSearch(query)) {
setHasSearched(true);
}
}
};
// Initial load
updateSettings();
// Subscribe to changes
const unsubscribe = settingsStore.subscribe(updateSettings);
return () => unsubscribe();
}, [query, loading, executeSearch, currentSortBy]);
const handleSearch = useCallback((searchQuery: string) => {
if (!searchQuery.trim()) return;
setQuery(searchQuery);
setHasSearched(true);
executeSearch(searchQuery);
}, [executeSearch]);
// Load cached results on mount
useEffect(() => {
if (hasLoadedCache.current) return;
hasLoadedCache.current = true;
const urlQuery = searchParams.get('q');
const cached = loadFromCache();
if (urlQuery) {
setQuery(urlQuery);
if (cached && cached.query === urlQuery && cached.results.length > 0) {
setHasSearched(true);
loadCachedResults(cached.results, cached.availableSources);
hasSearchedWithSourcesRef.current = true;
} else {
handleSearch(urlQuery);
}
}
}, [searchParams, loadFromCache, loadCachedResults, handleSearch]);
const handleReset = useCallback(() => {
setHasSearched(false);
setQuery('');
hasSearchedWithSourcesRef.current = false;
resetSearch();
router.replace('/', { scroll: false });
}, [resetSearch, router]);
return {
query,
hasSearched,
loading,
results,
availableSources,
completedSources,
totalSources,
handleSearch,
handleReset,
};
}