Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | 1x 1x 1x 76x 76x 76x 76x 76x 76x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 76x 76x 5x 5x 3x 3x 3x 3x 2x 1x 2x 2x 76x 76x 1x 1x 1x 76x 76x 76x 76x 76x 76x 76x 76x 76x 76x | import { newsletterSourceService } from '@common/services/newsletterSource/NewsletterSourceService';
import type { NewsletterSource } from '@common/types';
import type { NewsletterSourceQueryParams } from '@common/types/api';
import { useCallback, useState } from 'react';
interface UseSourceSearchOptions {
debounceMs?: number;
minQueryLength?: number;
}
interface UseSourceSearchReturn {
searchResults: NewsletterSource[];
isSearching: boolean;
searchQuery: string;
setSearchQuery: (query: string) => void;
clearSearch: () => void;
searchError: string | null;
}
export const useSourceSearch = (options: UseSourceSearchOptions = {}): UseSourceSearchReturn => {
const { debounceMs = 300, minQueryLength = 2 } = options;
const [searchResults, setSearchResults] = useState<NewsletterSource[]>([]);
const [isSearching, setIsSearching] = useState(false);
const [searchQuery, setSearchQueryState] = useState('');
const [searchError, setSearchError] = useState<string | null>(null);
// Debounced search function
const performSearch = useCallback(async (query: string) => {
if (query.length < minQueryLength) {
setSearchResults([]);
setSearchError(null);
return;
}
setIsSearching(true);
setSearchError(null);
try {
const searchParams: NewsletterSourceQueryParams = {
search: query.trim(),
excludeArchived: false,
limit: 50, // Limit search results for performance
orderBy: 'name',
orderDirection: 'asc',
includeCount: false, // No need for counts in search results
};
const result = await newsletterSourceService.getSources(searchParams);
setSearchResults(result.data);
} catch (error) {
console.error('Source search error:', error);
setSearchError(error instanceof Error ? error.message : 'Search failed');
setSearchResults([]);
} finally {
setIsSearching(false);
}
}, [minQueryLength]);
// Debounced search
const setSearchQuery = useCallback((query: string) => {
setSearchQueryState(query);
// Clear results immediately if query is too short
if (query.length < minQueryLength) {
setSearchResults([]);
setSearchError(null);
return;
}
// Debounce the actual search
const timeoutId = setTimeout(() => {
performSearch(query);
}, debounceMs);
return () => clearTimeout(timeoutId);
}, [performSearch, debounceMs, minQueryLength]);
const clearSearch = useCallback(() => {
setSearchQueryState('');
setSearchResults([]);
setSearchError(null);
}, []);
return {
searchResults,
isSearching,
searchQuery,
setSearchQuery,
clearSearch,
searchError,
};
};
|