import { useEffect, useRef } from 'react' import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { Link, useLocation, useParams, useSearchParams } from 'react-router-dom' import { getFeed, getSavedCounts, getSyncStatus, listCategories, listChannels, syncVideos } from '../api/client' import Icon from '../components/Icon' import VideoCard from '../components/VideoCard' function VideoSkeleton() { return
} function Feed() { const location = useLocation() const { categoryId } = useParams<{ categoryId: string }>() const [searchParams] = useSearchParams() const search = location.pathname === '/search' ? searchParams.get('q')?.trim() ?? '' : '' const categoryNumber = categoryId && /^\d+$/.test(categoryId) ? Number(categoryId) : undefined const isLocal = location.pathname === '/local' const isUncategorized = location.pathname === '/uncategorized' const localCategoryRaw = isLocal ? searchParams.get('category') : null const localCategory = localCategoryRaw && /^\d+$/.test(localCategoryRaw) ? Number(localCategoryRaw) : undefined const localUncategorized = isLocal && searchParams.get('uncategorized') === 'true' const queryClient = useQueryClient() const categoriesQuery = useQuery({ queryKey: ['categories'], queryFn: listCategories }) const category = categoriesQuery.data?.find((item) => item.id === categoryNumber) const savedCountsQuery = useQuery({ queryKey: ['feed', 'saved-counts'], queryFn: getSavedCounts, enabled: isLocal }) const countQuery = useQuery({ queryKey: ['channels', isUncategorized ? 'uncategorized-count' : 'all-for-counts'], queryFn: () => listChannels({ subscribed: true, uncategorized: isUncategorized }), enabled: isUncategorized || (!categoryNumber && !isLocal && !search), }) const feedQuery = useInfiniteQuery({ queryKey: ['feed', location.pathname, search, localCategory, localUncategorized], queryFn: ({ pageParam }) => getFeed({ categoryId: isLocal ? localCategory : categoryNumber, uncategorized: isUncategorized || localUncategorized, downloaded: isLocal, search, cursor: pageParam, }), initialPageParam: undefined as string | undefined, getNextPageParam: (lastPage) => lastPage.next_cursor ?? undefined, enabled: !categoryId || categoryNumber !== undefined, }) const syncStatusQuery = useQuery({ queryKey: ['sync-status'], queryFn: getSyncStatus, refetchInterval: (query) => query.state.data?.videos.running ? 2000 : 60_000, }) const finishedAt = syncStatusQuery.data?.videos.finished_at const previousFinished = useRef(finishedAt) useEffect(() => { if (finishedAt && previousFinished.current !== finishedAt) queryClient.invalidateQueries({ queryKey: ['feed'] }) previousFinished.current = finishedAt }, [finishedAt, queryClient]) const subscriptionsFinished = syncStatusQuery.data?.subscriptions.finished_at const previousSubscriptionsFinished = useRef(subscriptionsFinished) useEffect(() => { if (subscriptionsFinished && previousSubscriptionsFinished.current !== subscriptionsFinished) queryClient.invalidateQueries({ queryKey: ['channels'] }) previousSubscriptionsFinished.current = subscriptionsFinished }, [subscriptionsFinished, queryClient]) const syncMutation = useMutation({ mutationFn: syncVideos, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['sync-status'] }); queryClient.invalidateQueries({ queryKey: ['feed'] }) }, }) const title = search ? `Поиск: ${search}` : isLocal ? 'На сервере' : isUncategorized ? 'Без категории' : categoryId ? category?.name ?? 'Категория' : 'Все видео' const subtitle = search ? 'Результаты в вашей ленте' : isLocal ? 'Видео, которые вы сохранили для просмотра' : isUncategorized ? `${countQuery.data?.length ?? '…'} каналов пока без категории` : category ? `${category.channel_count} каналов в категории` : `${countQuery.data?.length ?? '…'} подписок в вашей ленте` const items = feedQuery.data?.pages.flatMap((page) => page.items) ?? [] useEffect(() => { if (feedQuery.isLoading) return const key = `feed-scroll:${location.pathname + location.search}` const saved = sessionStorage.getItem(key) if (saved) { sessionStorage.removeItem(key) requestAnimationFrame(() => window.scrollTo(0, Number(saved))) } }, [feedQuery.isLoading, location.pathname, location.search]) const hasInvalidCategory = Boolean(categoryId && !categoryNumber) returnМоя лента
{subtitle}
Проверь соединение и попробуй ещё раз.
{syncStatusQuery.data?.subscriptions.running || syncStatusQuery.data?.videos.running ? 'Подписки и лента обновляются. Видео появятся здесь автоматически.' : search ? 'Попробуй другой запрос.' : isLocal ? 'Нажми «Скачать» у понравившегося ролика, и он появится здесь.' : 'Добавь каналы в категорию или обнови ленту.'}
{!search && !isLocal && !syncStatusQuery.data?.subscriptions.running && !syncStatusQuery.data?.videos.running && Перейти к каналам}