mirror of
https://github.com/priyanshujain/margin.git
synced 2026-10-02 11:07:04 +00:00
feat(library): show last edited time on book cards
This commit is contained in:
1 parent
742e42d65b
commit
efd45cf61e
6 files changed
+75
-22
No files matched your search
@@ -10,10 +10,12 @@ import { RowMenu } from "./RowMenu";
|
||||
import { BackupButton } from "./BackupButton";
|
||||
import { useBackup } from "../store/useBackup";
|
||||
import { useBook } from "../store/useBook";
|
||||
import { relativeTime } from "../time";
|
||||
|
||||
export function Library({ onOpen }: { onOpen: (book: Book) => void }) {
|
||||
const [books, setBooks] = useState<BookSummary[]>([]);
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const [now, setNow] = useState(() => Date.now());
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [pendingDelete, setPendingDelete] = useState<BookSummary | null>(null);
|
||||
const notice = useBook((s) => s.notice);
|
||||
@@ -30,6 +32,10 @@ export function Library({ onOpen }: { onOpen: (book: Book) => void }) {
|
||||
useEffect(() => {
|
||||
refresh();
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => setNow(Date.now()), 60_000);
|
||||
return () => clearInterval(timer);
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
if (restoreNonce) refresh();
|
||||
}, [restoreNonce]);
|
||||
@@ -125,6 +131,7 @@ export function Library({ onOpen }: { onOpen: (book: Book) => void }) {
|
||||
>
|
||||
<span className="card-title">{b.title || "Untitled"}</span>
|
||||
{b.author && <span className="card-author">{b.author}</span>}
|
||||
{b.updatedAt > 0 && <span className="card-meta">Edited {relativeTime(b.updatedAt, now)}</span>}
|
||||
<RowMenu label="Book options" className="card-menu" onDelete={() => setPendingDelete(b)} />
|
||||
</div>
|
||||
),
|
||||
|
||||
@@ -5,6 +5,7 @@ import { AddPageMenu } from "./AddPageMenu";
|
||||
import { ConfirmDialog } from "./ConfirmDialog";
|
||||
import { Icon } from "./Icon";
|
||||
import { RowMenu } from "./RowMenu";
|
||||
import { relativeTime } from "../time";
|
||||
|
||||
interface Row {
|
||||
chapter: Chapter;
|
||||
@@ -19,26 +20,6 @@ interface DropTarget {
|
||||
index: number;
|
||||
}
|
||||
|
||||
function formatEdited(ts: number, now: number): string {
|
||||
const diff = Math.max(0, now - ts);
|
||||
const min = 60_000;
|
||||
const hour = 60 * min;
|
||||
const day = 24 * hour;
|
||||
if (diff < min) return "just now";
|
||||
if (diff < hour) {
|
||||
const m = Math.floor(diff / min);
|
||||
return `${m} minute${m === 1 ? "" : "s"} ago`;
|
||||
}
|
||||
if (diff < day) {
|
||||
const h = Math.floor(diff / hour);
|
||||
return `${h} hour${h === 1 ? "" : "s"} ago`;
|
||||
}
|
||||
if (diff < 2 * day) return "yesterday";
|
||||
const d = Math.floor(diff / day);
|
||||
if (d < 7) return `${d} days ago`;
|
||||
return new Date(ts).toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric" });
|
||||
}
|
||||
|
||||
export function Sidebar({ onNavigate }: { onNavigate?: () => void }) {
|
||||
const chapters = useBook((s) => s.book?.chapters ?? []);
|
||||
const activeChapterId = useBook((s) => s.activeChapterId);
|
||||
@@ -254,7 +235,7 @@ export function Sidebar({ onNavigate }: { onNavigate?: () => void }) {
|
||||
</span>
|
||||
)}
|
||||
{row.chapter.id === activeChapterId && (
|
||||
<span className="meta">Edited {formatEdited(row.chapter.updatedAt, now)}</span>
|
||||
<span className="meta">Edited {relativeTime(row.chapter.updatedAt, now)}</span>
|
||||
)}
|
||||
</span>
|
||||
<RowMenu
|
||||
|
||||
@@ -6,6 +6,7 @@ export interface BookSummary {
|
||||
id: string;
|
||||
title: string;
|
||||
author: string;
|
||||
updatedAt: number;
|
||||
corrupt?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -1566,6 +1566,19 @@ body.resizing .sidebar {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.card-meta {
|
||||
position: absolute;
|
||||
left: 14px;
|
||||
right: 14px;
|
||||
bottom: 14px;
|
||||
font-family: var(--font-ui);
|
||||
font-size: var(--t-1);
|
||||
color: var(--ink-faint);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.card-action {
|
||||
background: transparent;
|
||||
border: 1px dashed var(--line-strong);
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
export function relativeTime(ts: number, now: number): string {
|
||||
const diff = Math.max(0, now - ts);
|
||||
const min = 60_000;
|
||||
const hour = 60 * min;
|
||||
const day = 24 * hour;
|
||||
if (diff < min) return "just now";
|
||||
if (diff < hour) {
|
||||
const m = Math.floor(diff / min);
|
||||
return `${m} minute${m === 1 ? "" : "s"} ago`;
|
||||
}
|
||||
if (diff < day) {
|
||||
const h = Math.floor(diff / hour);
|
||||
return `${h} hour${h === 1 ? "" : "s"} ago`;
|
||||
}
|
||||
if (diff < 2 * day) return "yesterday";
|
||||
const d = Math.floor(diff / day);
|
||||
if (d < 7) return `${d} days ago`;
|
||||
return new Date(ts).toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric" });
|
||||
}
|
||||
Reference in new issue
Block a user