fix: final review — cached links keep their expiry; history removal sticks; saves flushed

- A link reused from the rehost cache keeps the expiry it was uploaded with, and
  links near expiry are uploaded again (a dead litterbox link could be filed).
- History saving moves into HistoryKeeper (tested): removing an entry or clearing
  the history cancels a pending save and stops tracking the series on screen;
  a pending edit is saved before a new search, before opening another entry and
  when the page is hidden; opening an entry no longer re-saves the current one.
- The "couldn't be saved" line sits outside the History list, so it shows even
  when the list is empty; a refused IndexedDB says history is off.
- Recently searched bands wrap their meta line instead of cutting the count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas WintherandClaude Opus 5.5 committed 2026-10-15 11:45:48 +02:00
1 parent 1cf31b26cf
commit 0d4a1a61fa
8 files changed
+362 -68

No files matched your search

+42 -1
View File
@@ -1,4 +1,4 @@
import { describe, expect, test } from "bun:test";
import { describe, expect, setSystemTime, test } from "bun:test";
import { HostError } from "../src/core/hosting/host";
import { MangaBakaError } from "../src/core/mangabaka/client";
import type { SubmissionImage } from "../src/core/mangabaka/types";
@@ -186,3 +186,44 @@ describe("hosting is remembered on the cards", () => {
expect(second.cards[0]!.hostedUrl).toBe("https://litter.catbox.moe/a.jpg");
});
});
describe("hosted links that expire", () => {
const T = Date.UTC(2026, 9, 15, 10);
const MIN = 60_000;
test("a link reused from the rehost cache keeps its original expiry", async () => {
const fake = new FakeTransport().on(LITTERBOX, () => text("https://litter.catbox.moe/a.jpg"));
const s = new Session(deps(fake));
s.load(result());
try {
setSystemTime(new Date(T));
await s.rehost("litterbox", { litterboxTime: "1h" });
setSystemTime(new Date(T + 20 * MIN));
await s.rehost("litterbox", { litterboxTime: "1h" });
expect(s.cards[0]!.hosting!.until).toBe(T + 60 * MIN);
} finally {
setSystemTime();
}
});
test("a restored link that has expired since is uploaded again, not reused", async () => {
let n = 0;
const fake = new FakeTransport().on(LITTERBOX, () => text(`https://litter.catbox.moe/${++n}.jpg`));
const r = result();
const first = new Session(deps(fake));
first.load(r);
try {
setSystemTime(new Date(T));
await first.rehost("litterbox", { litterboxTime: "1h" });
setSystemTime(new Date(T + 55 * MIN));
const second = new Session(deps(fake));
second.restore(r, first.cards.map((c) => ({ ...c })), true, null);
setSystemTime(new Date(T + 90 * MIN));
await second.rehost("litterbox", { litterboxTime: "1h" });
expect(second.cards[0]!.hostedUrl).toBe("https://litter.catbox.moe/2.jpg");
expect(second.cards[0]!.hosting!.until).toBe(T + 150 * MIN);
} finally {
setSystemTime();
}
});
});