'use client'; import { useEffect, useState, useRef, useCallback } from 'react'; import { useSearchParams } from 'next/navigation'; import { searchVideosClient } from '../clientActions'; import { VideoData } from '../constants'; import VideoCard from '../components/VideoCard'; import LoadingSpinner from '../components/LoadingSpinner'; function SearchSkeleton() { return (
{[...Array(12)].map((_, i) => (
))}
); } export default function ClientSearchPage() { const searchParams = useSearchParams(); const query = searchParams.get('q') || ''; const [videos, setVideos] = useState([]); const [loading, setLoading] = useState(true); const [loadingMore, setLoadingMore] = useState(false); const [searchPage, setSearchPage] = useState(0); const [hasMore, setHasMore] = useState(true); const observerTarget = useRef(null); const loadingMoreRef = useRef(false); const hasMoreRef = useRef(true); const searchPageRef = useRef(0); useEffect(() => { loadingMoreRef.current = loadingMore; }, [loadingMore]); useEffect(() => { hasMoreRef.current = hasMore; }, [hasMore]); useEffect(() => { searchPageRef.current = searchPage; }, [searchPage]); useEffect(() => { if (query) { performSearch(query); } }, [query]); const performSearch = async (q: string) => { try { setLoading(true); setSearchPage(0); searchPageRef.current = 0; setHasMore(true); hasMoreRef.current = true; const results = await searchVideosClient(q, 50); const uniqueResults = results.filter((video, index, self) => index === self.findIndex(v => v.id === video.id) ); setVideos(uniqueResults); setHasMore(uniqueResults.length >= 40); hasMoreRef.current = uniqueResults.length >= 40; } catch (error) { console.error('Search failed:', error); } finally { setLoading(false); } }; const loadMore = useCallback(async () => { if (loadingMoreRef.current || !hasMoreRef.current || !query) return; setLoadingMore(true); const nextPage = searchPageRef.current + 1; try { // Use different search variations to get more results const variations = [ `${query}`, `${query} official`, `${query} video`, `${query} review`, `${query} tutorial`, `${query} 2026`, `${query} new`, `${query} best`, ]; const searchVariation = variations[nextPage % variations.length]; const results = await searchVideosClient(searchVariation, 50); setVideos(prev => { const existingIds = new Set(prev.map(v => v.id)); const uniqueNewVideos = results.filter(v => !existingIds.has(v.id)); // Stop loading if we get very few new videos if (uniqueNewVideos.length < 3) { setHasMore(false); hasMoreRef.current = false; } return [...prev, ...uniqueNewVideos]; }); setSearchPage(nextPage); searchPageRef.current = nextPage; } catch (error) { console.error('Failed to load more:', error); } finally { setLoadingMore(false); } }, [query]); // Infinite scroll observer useEffect(() => { const observer = new IntersectionObserver( (entries) => { if (entries[0].isIntersecting && !loadingMoreRef.current && hasMoreRef.current) { loadMore(); } }, { rootMargin: '500px', threshold: 0.1 } ); const timer = setTimeout(() => { if (observerTarget.current) { observer.observe(observerTarget.current); } }, 100); return () => { clearTimeout(timer); observer.disconnect(); }; }, [loadMore]); return (
{/* Results Header */} {query && !loading && (
{videos.length > 0 ? `${videos.length} results for "${query}"` : `No results for "${query}"`}
)} {/* Results Grid */} {loading ? ( ) : videos.length === 0 ? (

No results found

Try different keywords or check your spelling

) : ( <>
{videos.map((video) => ( ))}
{/* Infinite scroll sentinel */}
{loadingMore && }
{/* End of results */} {!hasMore && videos.length > 0 && (
End of results
)} )}
); }