Keep the UI attached to background jobs across navigation
Jobs already run on the server — leaving the page or closing the tab does not stop them — but the SPA lost its link to them because the task id lived in component state. The online detail page now looks up the newest task for the post: an active one resumes the progress bar and cancel button, and a finished one shows "your last download for this post finished — J-xx". The downloads list accepts a post_id filter for that lookup. The footer's Active Workers count is also a link to the staff stats dashboard, which is the global view of running jobs.
This commit is contained in:
@@ -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<string | null>(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<Paginated<DownloadTask>>(
|
||||
`/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<DownloadTask>(`/api/online/downloads/${downloadTaskId}/`),
|
||||
enabled: Boolean(downloadTaskId),
|
||||
api<DownloadTask>(`/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 }) {
|
||||
<p className="rounded-md border border-ctp-red/30 bg-ctp-red/10 px-3 py-2 text-sm text-ctp-red">
|
||||
{downloadTask.error || "Download failed."}
|
||||
</p>
|
||||
) : remoteFinishedTask && !downloadMutation.isPending ? (
|
||||
<p className="flex items-center gap-2 text-xs text-ctp-overlay0">
|
||||
<CheckCircle2 className="h-3.5 w-3.5 shrink-0 text-ctp-green" />
|
||||
Your last download for this post finished
|
||||
{remoteFinishedTask.library_j_id ? (
|
||||
<>
|
||||
{" — "}
|
||||
<Link
|
||||
to={`/detail/${remoteFinishedTask.library_j_id}`}
|
||||
className="font-mono text-ctp-blue hover:underline"
|
||||
>
|
||||
{remoteFinishedTask.library_j_id}
|
||||
</Link>
|
||||
</>
|
||||
) : null}
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
{favoriteMutation.isError ? (
|
||||
|
||||
Reference in New Issue
Block a user