diff --git a/packages/common/src/api/tan-query/tracks/useDownloadTrackStems.ts b/packages/common/src/api/tan-query/tracks/useDownloadTrackStems.ts index ea3d4424e70..09627688c84 100644 --- a/packages/common/src/api/tan-query/tracks/useDownloadTrackStems.ts +++ b/packages/common/src/api/tan-query/tracks/useDownloadTrackStems.ts @@ -1,3 +1,5 @@ +import { useEffect, useRef } from 'react' + import { Id } from '@audius/sdk' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' @@ -10,6 +12,10 @@ import { useCurrentUserId } from '../users/account/useCurrentUserId' import { useTrack } from './useTrack' +// Stop polling the archive job after this long even if it never transitions +// out of `active`, so the UI can surface an error instead of spinning forever. +const STEMS_ARCHIVE_POLL_TIMEOUT_MS = 300_000 // 5 minutes + type GetStemsArchiveJobStatusResponse = { id: string state: @@ -109,6 +115,13 @@ export const useGetStemsArchiveJobStatus = ( ) => { const { audiusSdk } = useQueryContext() + // Track when polling for this job began so we can enforce a hard timeout even + // if the job never transitions out of `active`. + const jobStartTimeRef = useRef(null) + useEffect(() => { + jobStartTimeRef.current = jobId ? Date.now() : null + }, [jobId]) + return useQuery({ queryKey: getStemsArchiveJobQueryKey(jobId), queryFn: async () => { @@ -124,6 +137,15 @@ export const useGetStemsArchiveJobStatus = ( }, // refetch once per second until the job is completed or failed refetchInterval: (query) => { + // Hard stop: give up polling after the timeout even if the job is still + // reported as active, so we don't spin forever on a stalled job. + const jobStartTime = jobStartTimeRef.current + if ( + jobStartTime !== null && + Date.now() - jobStartTime > STEMS_ARCHIVE_POLL_TIMEOUT_MS + ) { + return false + } if (!query.state.data) { return 1000 } diff --git a/packages/mobile/src/screens/contest-screen/ContestScreen.tsx b/packages/mobile/src/screens/contest-screen/ContestScreen.tsx index 737d8e98b48..91b4805b4b4 100644 --- a/packages/mobile/src/screens/contest-screen/ContestScreen.tsx +++ b/packages/mobile/src/screens/contest-screen/ContestScreen.tsx @@ -220,7 +220,7 @@ export const ContestScreen = () => { source: ShareSource.PAGE }) ) - }, [dispatch, trackId, contest?.permalink]) + }, [dispatch, trackId]) // Pull-to-refresh: invalidate the contest's event + comment queries so all // tabs (details, updates, submissions, comments) refetch the next time diff --git a/packages/web/src/components/download-track-archive-modal/DownloadTrackArchiveModal.tsx b/packages/web/src/components/download-track-archive-modal/DownloadTrackArchiveModal.tsx index d256411235c..7ee712b057d 100644 --- a/packages/web/src/components/download-track-archive-modal/DownloadTrackArchiveModal.tsx +++ b/packages/web/src/components/download-track-archive-modal/DownloadTrackArchiveModal.tsx @@ -73,13 +73,16 @@ const DownloadTrackArchiveModalContent = ({ const { mutate: cancelStemsArchiveJob } = useCancelStemsArchiveJob() - const { data: jobStatus } = useGetStemsArchiveJobStatus({ - jobId - }) + const { data: jobStatus, isError: isJobStatusError } = + useGetStemsArchiveJobStatus({ + jobId + }) const hasError = !isStartingDownload && - (initiateDownloadFailed || jobStatus?.state === 'failed') + (initiateDownloadFailed || + jobStatus?.state === 'failed' || + (!!jobId && isJobStatusError)) useEffect(() => { if (hasError) {