feat(web): history view helpers
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2293d2810f
commit
01c2253051
2 files changed
+47
No files matched your search
@@ -3,6 +3,7 @@ import type { HostId, HostOptions } from "../core/hosting/host";
|
||||
import { HOSTS } from "../core/hosting/registry";
|
||||
import type { Granularity, InspectedCover } from "../core/model";
|
||||
import type { Stage } from "../core/pipeline";
|
||||
import type { Marks, SearchSummary } from "../core/saved";
|
||||
import type { ReviewCard } from "../core/session";
|
||||
|
||||
export interface ServerConfig {
|
||||
@@ -120,3 +121,28 @@ export function timelineGroups(cards: readonly ReviewCard[], now = new Date()):
|
||||
}
|
||||
return [...groups].map(([year, list]) => ({ year, cards: list }));
|
||||
}
|
||||
|
||||
/** History list times: recent ones relative, older ones as a date. */
|
||||
export function relativeTime(iso: string, now = new Date()): string {
|
||||
const ms = now.getTime() - new Date(iso).getTime();
|
||||
const min = Math.floor(ms / 60_000);
|
||||
if (min < 1) return "just now";
|
||||
if (min < 60) return `${min} min ago`;
|
||||
const h = Math.floor(min / 60);
|
||||
if (h < 24) return `${h} h ago`;
|
||||
const days = Math.floor(h / 24);
|
||||
if (days === 1) return "yesterday";
|
||||
if (days < 7) return `${days} days ago`;
|
||||
return iso.slice(0, 10);
|
||||
}
|
||||
|
||||
export function markText(m: Marks): string[] {
|
||||
return [
|
||||
...(m.downloaded ? ["downloaded"] : []),
|
||||
...(m.rehosted ? [`rehosted on ${m.rehosted.host}`] : []),
|
||||
...(m.filed ? [`filed #${m.filed.submissionId}`] : []),
|
||||
];
|
||||
}
|
||||
|
||||
export const summaryLine = (s: Pick<SearchSummary, "fresh" | "present">): string =>
|
||||
`${s.fresh} new${s.present ? ` · ${s.present} on MangaBaka` : ""}`;
|
||||
@@ -71,3 +71,24 @@ describe("seen ranges and the timeline", () => {
|
||||
expect(groups[1]!.cards).toEqual([mid]);
|
||||
});
|
||||
});
|
||||
|
||||
test("relative time for the history list", async () => {
|
||||
const { relativeTime } = await import("../src/web/view-model");
|
||||
const now = new Date("2026-10-15T12:00:00Z");
|
||||
const ago = (ms: number) => new Date(now.getTime() - ms).toISOString();
|
||||
expect(relativeTime(ago(20_000), now)).toBe("just now");
|
||||
expect(relativeTime(ago(5 * 60_000), now)).toBe("5 min ago");
|
||||
expect(relativeTime(ago(3 * 3_600_000), now)).toBe("3 h ago");
|
||||
expect(relativeTime(ago(30 * 3_600_000), now)).toBe("yesterday");
|
||||
expect(relativeTime(ago(4 * 86_400_000), now)).toBe("4 days ago");
|
||||
expect(relativeTime(ago(40 * 86_400_000), now)).toBe("2026-09-05");
|
||||
});
|
||||
|
||||
test("history marks and summary line", async () => {
|
||||
const { markText, summaryLine } = await import("../src/web/view-model");
|
||||
expect(markText({})).toEqual([]);
|
||||
expect(markText({ downloaded: "x", rehosted: { at: "x", host: "litterbox" }, filed: { at: "x", submissionId: 77 } }))
|
||||
.toEqual(["downloaded", "rehosted on litterbox", "filed #77"]);
|
||||
expect(summaryLine({ fresh: 14, present: 3 })).toBe("14 new · 3 on MangaBaka");
|
||||
expect(summaryLine({ fresh: 1, present: 0 })).toBe("1 new");
|
||||
});
|
||||
Reference in new issue
Block a user