feat: Implement flexible media sorting by standard columns and metadata tags by refactoring backend query building and updating frontend API parameters.

This commit is contained in:
2025-12-03 22:53:27 +01:00
parent c8403d70da
commit 15177f218b
7 changed files with 249 additions and 52 deletions

View File

@@ -3,16 +3,26 @@ import { createFileRoute } from "@tanstack/react-router";
import { Button } from "@/components/ui/button";
import { AuthenticatedImage } from "@/components/media/authenticated-image";
import type { Media } from "@/domain/types";
import { useMemo, useState } from "react"; // Import useMemo
import { useMemo, useState } from "react";
import { MediaViewer } from "@/components/media/media-viewer";
import { groupMediaByDate } from "@/lib/date-utils"; // Import our new helper
import { parseISO } from "date-fns";
import { groupMediaByDate } from "@/lib/date-utils";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
export const Route = createFileRoute("/media/")({
component: MediaPage,
});
function MediaPage() {
const [sortBy, setSortBy] = useState<string>("created_at");
const [sortOrder, setSortOrder] = useState<"asc" | "desc">("desc");
const [mimeType, setMimeType] = useState<string | undefined>(undefined);
const {
data,
isLoading,
@@ -20,20 +30,16 @@ function MediaPage() {
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useGetMediaList();
} = useGetMediaList({
sort_by: sortBy,
order: sortOrder,
mime_type: mimeType === "all" ? undefined : mimeType,
});
const [selectedMedia, setSelectedMedia] = useState<Media | null>(null);
const allMedia = useMemo(
() =>
data?.pages
.flatMap((page) => page.data)
.sort((a, b) => {
// Sort by date (newest first)
const dateA = a.date_taken ?? a.created_at;
const dateB = b.date_taken ?? b.created_at;
return parseISO(dateB).getTime() - parseISO(dateA).getTime();
}) ?? [],
() => data?.pages.flatMap((page) => page.data) ?? [],
[data]
);
@@ -46,8 +52,65 @@ function MediaPage() {
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
<h1 className="text-3xl font-bold">All Photos</h1>
<div className="flex flex-wrap items-center gap-2">
<Select value={sortBy} onValueChange={setSortBy}>
<SelectTrigger className="w-[140px]">
<SelectValue placeholder="Sort by" />
</SelectTrigger>
<SelectContent>
<SelectItem value="created_at">Date Created</SelectItem>
<SelectItem value="date_taken">Date Taken</SelectItem>
<SelectItem value="original_filename">Filename</SelectItem>
<SelectItem value="custom">Custom Tag...</SelectItem>
</SelectContent>
</Select>
{sortBy === "custom" && (
<input
type="text"
placeholder="Enter tag name"
className="border rounded px-2 py-1 text-sm w-[140px]"
onBlur={(e) => {
if (e.target.value) setSortBy(e.target.value);
}}
onKeyDown={(e) => {
if (e.key === 'Enter') {
setSortBy(e.currentTarget.value);
}
}}
/>
)}
<Select
value={sortOrder}
onValueChange={(val) => setSortOrder(val as "asc" | "desc")}
>
<SelectTrigger className="w-[140px]">
<SelectValue placeholder="Order" />
</SelectTrigger>
<SelectContent>
<SelectItem value="desc">Newest First</SelectItem>
<SelectItem value="asc">Oldest First</SelectItem>
</SelectContent>
</Select>
<Select
value={mimeType ?? "all"}
onValueChange={(val) => setMimeType(val === "all" ? undefined : val)}
>
<SelectTrigger className="w-[140px]">
<SelectValue placeholder="Filter by Type" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Types</SelectItem>
<SelectItem value="image/jpeg">JPEG</SelectItem>
<SelectItem value="image/png">PNG</SelectItem>
<SelectItem value="video/mp4">MP4</SelectItem>
</SelectContent>
</Select>
</div>
</div>
{isLoading && <p>Loading photos...</p>}