Files
Gabriel Kaszewski 0077caa743 feat: safe deletion, album/asset delete, trash, README update
- volume-aware deletion: read-only volumes remove DB only, writable
  volumes soft-delete to trash with configurable grace period
- trash page with restore, worker purge sweep (TRASH_RETENTION_DAYS)
- album delete endpoint + sidebar trash icon
- asset delete from timeline selection toolbar
- all listing queries exclude trashed assets (deleted_at IS NULL)
- timeline ordered by EXIF capture date, date-summary endpoint
- README rewritten with features, setup, full env var table
2026-06-01 01:57:53 +02:00

56 lines
1.6 KiB
TypeScript

"use client"
import { useMemo } from "react"
import { useQueryClient } from "@tanstack/react-query"
import { useTimeline, useDateSummary } from "@/hooks/use-timeline"
import { groupByDate } from "@/lib/timeline"
import { PhotoGrid } from "@/components/photo-grid"
import { DateScrubber } from "@/components/date-scrubber"
import api from "@/lib/api"
import { toast } from "sonner"
export default function TimelinePage() {
const qc = useQueryClient()
const { assets, isLoading, hasMore, loadMore, total } = useTimeline()
const { data: dateSummary } = useDateSummary()
const groups = useMemo(() => groupByDate(assets), [assets])
const handleDeleteAssets = async (ids: string[]) => {
let deleted = 0
for (const id of ids) {
try {
await api.delete(`/assets/${id}`)
deleted++
} catch { /* skip */ }
}
if (deleted > 0) {
toast.success(`Deleted ${deleted} photo(s)`)
qc.invalidateQueries({ queryKey: ["timeline"] })
qc.invalidateQueries({ queryKey: ["date-summary"] })
}
}
return (
<div className="flex flex-col gap-4">
<div className="flex items-center justify-between">
<h1 className="text-lg font-semibold">Timeline</h1>
{total > 0 && (
<span className="text-sm text-muted-foreground">
{total} photos
</span>
)}
</div>
<div className="flex gap-1">
<PhotoGrid
groups={groups}
isLoading={isLoading}
hasMore={hasMore}
onLoadMore={() => loadMore()}
onDeleteAssets={handleDeleteAssets}
/>
<DateScrubber dates={dateSummary ?? []} />
</div>
</div>
)
}