diff --git a/frontend/src/components/DownloadButton.tsx b/frontend/src/components/DownloadButton.tsx index 7505738..964ca1a 100644 --- a/frontend/src/components/DownloadButton.tsx +++ b/frontend/src/components/DownloadButton.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import type { FeedVideoDto } from '../api/client' import { downloadVideo, getDownloadStatus } from '../api/client' @@ -16,43 +16,43 @@ interface Props { function DownloadButton({ video }: Props) { const queryClient = useQueryClient() - const [polling, setPolling] = useState(ACTIVE_STATUSES.includes(video.local.status)) + // Once we start tracking a download (it was already active on load, or the + // user just clicked "Скачать"), the query stays enabled for good -- + // refetchInterval alone stops the ticking once the status is terminal. + // Disabling the query on completion used to make the button fall back to + // the *stale* `video` prop instead of the query's own fresh cache, which + // showed "Скачать" again until the page was manually reloaded. + const [tracking, setTracking] = useState(ACTIVE_STATUSES.includes(video.local.status)) const statusQuery = useQuery({ queryKey: ['download-status', video.youtube_video_id], queryFn: () => getDownloadStatus(video.youtube_video_id), - enabled: polling, - refetchInterval: (query) => (query.state.data && ACTIVE_STATUSES.includes(query.state.data.status) ? 2000 : false), - initialData: polling - ? { - status: video.local.status, - progress_percent: video.local.progress_percent, - media_url: video.local.media_url, - error_message: null, - } - : undefined, + enabled: tracking, + refetchInterval: (query) => + query.state.data && ACTIVE_STATUSES.includes(query.state.data.status) ? 2000 : false, }) const downloadMutation = useMutation({ mutationFn: () => downloadVideo(video.youtube_video_id), onSuccess: (data) => { queryClient.setQueryData(['download-status', video.youtube_video_id], data) - setPolling(true) + setTracking(true) }, }) - const current = polling ? statusQuery.data : undefined - const status = current?.status ?? video.local.status - const percent = current?.progress_percent ?? video.local.progress_percent + const status = statusQuery.data?.status ?? video.local.status + const percent = statusQuery.data?.progress_percent ?? video.local.progress_percent + const prevStatus = useRef(status) useEffect(() => { - if (current && !ACTIVE_STATUSES.includes(current.status)) { - setPolling(false) - if (current.status === 'completed' || current.status === 'failed') { + if (prevStatus.current !== status) { + prevStatus.current = status + if (status === 'completed' || status === 'failed') { queryClient.invalidateQueries({ queryKey: ['feed'] }) + queryClient.invalidateQueries({ queryKey: ['video', video.youtube_video_id] }) } } - }, [current?.status, queryClient]) + }, [status, queryClient, video.youtube_video_id]) if (status === 'completed') { return ✓ На сервере