- 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
76 lines
2.1 KiB
TypeScript
76 lines
2.1 KiB
TypeScript
"use client"
|
|
|
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
|
|
import api from "@/lib/api"
|
|
import type {
|
|
AlbumResponse,
|
|
CreateAlbumRequest,
|
|
UpdateAlbumRequest,
|
|
} from "@/lib/types"
|
|
|
|
export function useAlbums() {
|
|
const qc = useQueryClient()
|
|
|
|
const query = useQuery({
|
|
queryKey: ["albums"],
|
|
queryFn: async () => {
|
|
const { data } = await api.get<AlbumResponse[]>("/albums")
|
|
return data
|
|
},
|
|
})
|
|
|
|
const create = useMutation({
|
|
mutationFn: async (title: string) => {
|
|
const body: CreateAlbumRequest = { title }
|
|
const { data } = await api.post<AlbumResponse>("/albums", body)
|
|
return data
|
|
},
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["albums"] }),
|
|
})
|
|
|
|
const update = useMutation({
|
|
mutationFn: async ({ id, ...updates }: UpdateAlbumRequest & { id: string }) => {
|
|
const { data } = await api.put<AlbumResponse>(`/albums/${id}`, updates)
|
|
return data
|
|
},
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["albums"] }),
|
|
})
|
|
|
|
const deleteAlbum = useMutation({
|
|
mutationFn: async (id: string) => {
|
|
await api.delete(`/albums/${id}`)
|
|
},
|
|
onSuccess: () => qc.invalidateQueries({ queryKey: ["albums"] }),
|
|
})
|
|
|
|
const addEntry = useMutation({
|
|
mutationFn: async ({ albumId, assetId }: { albumId: string; assetId: string }) => {
|
|
await api.post(`/albums/${albumId}/entries`, { asset_id: assetId })
|
|
},
|
|
onSuccess: () => {
|
|
qc.invalidateQueries({ queryKey: ["albums"] })
|
|
qc.invalidateQueries({ queryKey: ["album"] })
|
|
},
|
|
})
|
|
|
|
const removeEntry = useMutation({
|
|
mutationFn: async ({ albumId, assetId }: { albumId: string; assetId: string }) => {
|
|
await api.delete(`/albums/${albumId}/entries/${assetId}`)
|
|
},
|
|
onSuccess: () => {
|
|
qc.invalidateQueries({ queryKey: ["albums"] })
|
|
qc.invalidateQueries({ queryKey: ["album"] })
|
|
},
|
|
})
|
|
|
|
return {
|
|
albums: query.data ?? [],
|
|
isLoading: query.isLoading,
|
|
createAlbum: create.mutateAsync,
|
|
deleteAlbum: deleteAlbum.mutateAsync,
|
|
updateAlbum: update.mutateAsync,
|
|
addEntry: addEntry.mutateAsync,
|
|
removeEntry: removeEntry.mutateAsync,
|
|
}
|
|
}
|