diff --git a/backend/apps/library/views.py b/backend/apps/library/views.py index 46aa83a..ee5e3cc 100644 --- a/backend/apps/library/views.py +++ b/backend/apps/library/views.py @@ -420,6 +420,9 @@ class DownloadTaskViewSet( def get_queryset(self): reap_stale_downloads() queryset = DownloadTask.objects.select_related("library_item") + post_id = self.request.query_params.get("post_id", "").strip() + if post_id.isdigit(): + queryset = queryset.filter(post_id=int(post_id)) user = self.request.user if not (user.is_staff or user.is_superuser): queryset = queryset.filter(user=user) diff --git a/frontend/src/components/AppShell.tsx b/frontend/src/components/AppShell.tsx index 3f3d6f2..9a9de70 100644 --- a/frontend/src/components/AppShell.tsx +++ b/frontend/src/components/AppShell.tsx @@ -175,7 +175,12 @@ export function AppShell() { - + setPaletteOpen(false)} /> ); diff --git a/frontend/src/components/StatusFooter.tsx b/frontend/src/components/StatusFooter.tsx index 6f4f0ba..94d6b26 100644 --- a/frontend/src/components/StatusFooter.tsx +++ b/frontend/src/components/StatusFooter.tsx @@ -1,8 +1,27 @@ import { Folder, Zap } from "lucide-react"; +import { Link } from "react-router-dom"; import type { SystemStatus } from "@/lib/types"; -export function StatusFooter({ status }: { status?: SystemStatus }) { +export function StatusFooter({ + status, + staff, +}: { + status?: SystemStatus; + staff?: boolean; +}) { + const workers = ( + <> + + + Active Workers:{" "} + {status + ? `${status.workers.active} running | ${status.workers.queued} pending` + : "…"} + + + ); + return (
@@ -15,18 +34,22 @@ export function StatusFooter({ status }: { status?: SystemStatus }) { {status?.watched_folder ?? "…"} - - - - Active Workers:{" "} - {status - ? `${status.workers.active} running | ${status.workers.queued} pending` - : "…"} + {staff ? ( + + {workers} + + ) : ( + + {workers} - + )}
); diff --git a/frontend/src/features/online/OnlineDetail.tsx b/frontend/src/features/online/OnlineDetail.tsx index cd9989c..8f31eed 100644 --- a/frontend/src/features/online/OnlineDetail.tsx +++ b/frontend/src/features/online/OnlineDetail.tsx @@ -28,7 +28,7 @@ import { TAG_CATEGORY_ORDER, } from "@/lib/e621"; import { formatBytes, formatDate } from "@/lib/format"; -import type { DownloadTask } from "@/lib/types"; +import type { DownloadTask, Paginated } from "@/lib/types"; import { useAuth } from "@/store/auth"; import { useE621 } from "@/store/e621"; @@ -119,6 +119,25 @@ export function OnlineDetail({ postId }: { postId: number }) { const [downloadTaskId, setDownloadTaskId] = useState(null); + // Server-side tasks outlive this page; re-attach to the newest one for this + // post so navigating away and back keeps the progress view. + const tasksQuery = useQuery({ + queryKey: ["download-tasks", postId], + queryFn: () => + api>( + `/api/online/downloads/?post_id=${postId}`, + ), + enabled: Boolean(user?.can_upload), + staleTime: 10_000, + }); + const remoteActiveTask = tasksQuery.data?.results.find( + (task) => task.status === "pending" || task.status === "downloading", + ); + const remoteFinishedTask = tasksQuery.data?.results.find( + (task) => task.status === "complete", + ); + const effectiveTaskId = downloadTaskId ?? remoteActiveTask?.task_id ?? null; + useEffect(() => { function handleKeyDown(event: KeyboardEvent) { if (event.ctrlKey || event.metaKey || event.altKey) return; @@ -150,10 +169,10 @@ export function OnlineDetail({ postId }: { postId: number }) { }); const taskQuery = useQuery({ - queryKey: ["download-task", downloadTaskId], + queryKey: ["download-task", effectiveTaskId], queryFn: () => - api(`/api/online/downloads/${downloadTaskId}/`), - enabled: Boolean(downloadTaskId), + api(`/api/online/downloads/${effectiveTaskId}/`), + enabled: Boolean(effectiveTaskId), refetchInterval: (query) => { const status = query.state.data?.status; return status === "pending" || status === "downloading" ? 700 : false; @@ -176,15 +195,17 @@ export function OnlineDetail({ postId }: { postId: number }) { if (downloadStatus === "complete") { void queryClient.invalidateQueries({ queryKey: ["library-lookup"] }); void queryClient.invalidateQueries({ queryKey: ["files"] }); + void queryClient.invalidateQueries({ queryKey: ["download-tasks"] }); } }, [downloadStatus, queryClient]); function cancelDownload() { - if (!downloadTaskId) return; - void api(`/api/online/downloads/${downloadTaskId}/cancel/`, { + if (!effectiveTaskId) return; + void api(`/api/online/downloads/${effectiveTaskId}/cancel/`, { method: "POST", }).then(() => { void queryClient.invalidateQueries({ queryKey: ["status"] }); + void queryClient.invalidateQueries({ queryKey: ["download-tasks"] }); }); } @@ -362,6 +383,22 @@ export function OnlineDetail({ postId }: { postId: number }) {

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

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

+ + Your last download for this post finished + {remoteFinishedTask.library_j_id ? ( + <> + {" — "} + + {remoteFinishedTask.library_j_id} + + + ) : null} +

) : null} {favoriteMutation.isError ? (