diff --git a/frontend/src/features/detail/DetailPage.tsx b/frontend/src/features/detail/DetailPage.tsx index ba269ea..fc772b9 100644 --- a/frontend/src/features/detail/DetailPage.tsx +++ b/frontend/src/features/detail/DetailPage.tsx @@ -14,11 +14,14 @@ export default function DetailPage() { if (J_ID_RE.test(itemId)) { if (!hasBackend()) return ; const normalized = `J-${itemId.slice(2)}`; - return ; + // Key per item: remount so local state (delete confirm, optimizer, task + // view) cannot leak from the previously viewed item. + return ; } if (/^\d+$/.test(itemId)) { - return ; + // Key per post: the download task view must not survive a post change. + return ; } return ( diff --git a/frontend/src/features/online/OnlineDetail.tsx b/frontend/src/features/online/OnlineDetail.tsx index 44c52dc..de164b5 100644 --- a/frontend/src/features/online/OnlineDetail.tsx +++ b/frontend/src/features/online/OnlineDetail.tsx @@ -121,6 +121,10 @@ export function OnlineDetail({ postId }: { postId: number }) { }); const [downloadTaskId, setDownloadTaskId] = useState(null); + // True once this visit has seen the download running. Only then is "your + // last download finished" worth showing; a revisit stays quiet (the "In + // library" button already says where the file is). + const [watchedDownload, setWatchedDownload] = useState(false); // Server-side tasks outlive this page; re-attach to the newest one for this // post so navigating away and back keeps the progress view. @@ -186,6 +190,19 @@ export function OnlineDetail({ postId }: { postId: number }) { const downloadStatus = downloadTask?.status; const downloadActive = downloadStatus === "pending" || downloadStatus === "downloading"; + // Never render another post's task — this covers the single frame before the + // remount (DetailPage keys this component per post). A null post_id (older or + // hand-made tasks) stays visible rather than hiding a real download. + const downloadTaskMatchesPost = + !downloadTask || + downloadTask.post_id === null || + downloadTask.post_id === post?.id; + + // Derived state (safe to set during render): remember that this visit saw + // the download running. + if (downloadActive && !watchedDownload) { + setWatchedDownload(true); + } useEffect(() => { if (!downloadStatus) return; @@ -340,7 +357,8 @@ export function OnlineDetail({ postId }: { postId: number }) { - {downloadTask && + {downloadTaskMatchesPost && + downloadTask && (downloadTask.status === "pending" || downloadTask.status === "downloading") ? (
@@ -372,7 +390,7 @@ export function OnlineDetail({ postId }: { postId: number }) { cancel
- ) : downloadTask?.status === "complete" ? ( + ) : downloadTaskMatchesPost && downloadTask?.status === "complete" ? (

Downloaded to the library @@ -385,13 +403,13 @@ export function OnlineDetail({ postId }: { postId: number }) { ) : null}

- ) : downloadTask?.status === "cancelled" ? ( + ) : downloadTaskMatchesPost && downloadTask?.status === "cancelled" ? (

Download cancelled.

- ) : downloadTask?.status === "error" ? ( + ) : downloadTaskMatchesPost && downloadTask?.status === "error" ? (

{downloadTask.error || "Download failed."}

- ) : remoteFinishedTask && !downloadMutation.isPending ? ( + ) : remoteFinishedTask && watchedDownload && !downloadMutation.isPending ? (

Your last download for this post finished