fix(files): a file is added when it is done, not when it was asked for
A download that failed for days and then went through sat days down the library, under its request date, and read as missing. A job now records when it became done (`ready_at`), stamped where the expiry already is (`folders.stamp_done`, `finalize_if_running`) and on an insert that is already done; a check constraint holds every path to done to it. The library sorts and dates its "Added" by it; the queue keeps the request order. The migration dates a file from before by its "ready to watch" notice where the inbox still holds one, else by its request date, where it already sat.
This commit is contained in:
@@ -11,6 +11,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { act, cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { api, type FileFolder, type LibraryFile } from "../lib/api";
|
||||
import { formatDateTime } from "../lib/format";
|
||||
import {
|
||||
ALL_FILES,
|
||||
FilesActionsContext,
|
||||
@@ -496,6 +497,32 @@ describe("Files · the view", () => {
|
||||
expect(screen.queryByText("Más")).not.toBeNull();
|
||||
expect(screen.queryByText("Harmadik")).toBeNull();
|
||||
});
|
||||
|
||||
it("dates and sorts a file by when it was done, not when it was asked for", () => {
|
||||
// Done in another order than asked: a download that failed for days and then went through is new.
|
||||
mocks.files = [
|
||||
file(2, { created_at: "2026-10-01T10:00:00Z", ready_at: "2026-10-05T10:00:00Z" }),
|
||||
file(3, { created_at: "2026-10-02T10:00:00Z", ready_at: "2026-10-12T10:00:00Z" }),
|
||||
file(4, { created_at: "2026-10-03T10:00:00Z", ready_at: "2026-10-03T10:00:00Z" }),
|
||||
];
|
||||
mocks.folders = [YT];
|
||||
mount();
|
||||
const order = () =>
|
||||
[...document.querySelectorAll<HTMLElement>("[data-nav-index]")].map(
|
||||
(r) => r.textContent?.match(/Clip \d/)?.[0],
|
||||
);
|
||||
const byDone = [
|
||||
["Clip 4", "Clip 2", "Clip 3"],
|
||||
["Clip 3", "Clip 2", "Clip 4"],
|
||||
];
|
||||
fireEvent.click(screen.getByRole("button", { name: /files\.columns\.added/ }));
|
||||
expect(byDone).toContainEqual(order());
|
||||
fireEvent.click(screen.getByRole("button", { name: /files\.columns\.added/ }));
|
||||
expect(byDone).toContainEqual(order());
|
||||
expect(
|
||||
rowOf("Clip 3").querySelector(`[title="${formatDateTime("2026-10-12T10:00:00Z", "en")}"]`),
|
||||
).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Files · the grid", () => {
|
||||
|
||||
@@ -402,9 +402,9 @@ export default function Files() {
|
||||
align: "right",
|
||||
nowrap: true,
|
||||
sortable: true,
|
||||
sortValue: (f) => f.shared_at ?? f.created_at ?? "",
|
||||
sortValue: (f) => f.shared_at ?? f.ready_at ?? f.created_at ?? "",
|
||||
render: (f) => {
|
||||
const at = f.shared_at ?? f.created_at;
|
||||
const at = f.shared_at ?? f.ready_at ?? f.created_at;
|
||||
return (
|
||||
<span className="text-xs text-muted" title={formatDateTime(at, lang)}>
|
||||
{relativeTime(at, lang, t)}
|
||||
|
||||
@@ -94,7 +94,8 @@ export interface DownloadJob {
|
||||
error_code: string | null; // failure category, see backend downloads.errors.classify
|
||||
error_detail: string | null; // host-specific failure-catalog entry, see downloads.errors.diagnose
|
||||
queue_pos: number;
|
||||
created_at: string | null;
|
||||
created_at: string | null; // when it was asked for
|
||||
ready_at?: string | null; // when it became done — the library's "Added"
|
||||
source_kind: string;
|
||||
source_ref: string;
|
||||
source_url: string | null; // clean "downloaded from" URL (null for edit clips)
|
||||
|
||||
Reference in New Issue
Block a user