87 lines
3.3 KiB
TypeScript
87 lines
3.3 KiB
TypeScript
import { useLocation, useSearch } from "wouter";
|
|
import { Button } from "@/components/ui/button.tsx";
|
|
import { GalleryCard } from "@/components/gallery/GalleryCard.tsx";
|
|
import TagBadge from "@/components/gallery/TagBadge.tsx";
|
|
import { useSearchGalleryInfinite } from "../hook/useSearchGallery.ts";
|
|
import { Spinner } from "../components/Spinner.tsx";
|
|
import TagInput from "@/components/gallery/TagInput.tsx";
|
|
import { useState } from "react";
|
|
|
|
export default function Gallery() {
|
|
const search = useSearch();
|
|
const searchParams = new URLSearchParams(search);
|
|
const word = searchParams.get("word") ?? undefined;
|
|
const tags = searchParams.get("allow_tag") ?? undefined;
|
|
const limit = searchParams.get("limit");
|
|
const cursor = searchParams.get("cursor");
|
|
const { data, error, isLoading, size, setSize } = useSearchGalleryInfinite({
|
|
word, tags,
|
|
limit: limit ? Number.parseInt(limit) : undefined,
|
|
cursor: cursor ? Number.parseInt(cursor) : undefined
|
|
});
|
|
|
|
if (isLoading) {
|
|
return <div className="p-4">Loading...</div>
|
|
}
|
|
if (error) {
|
|
return <div className="p-4">Error: {String(error)}</div>
|
|
}
|
|
|
|
const isLoadingMore = data && size > 0 && (data[size - 1] === undefined);
|
|
const isReachingEnd = data && data[size - 1]?.hasMore === false;
|
|
|
|
return (<div className="p-4 grid gap-2 overflow-auto h-screen items-start content-start">
|
|
<Search />
|
|
{(word || tags) &&
|
|
<div className="bg-primary rounded-full p-1 sticky top-0 shadow-md">
|
|
{word && <span className="text-primary-foreground ml-4">Search: {word}</span>}
|
|
{tags && <span className="text-primary-foreground ml-4">Tags: <ul className="inline-flex">{tags.split(",").map(x => <TagBadge tagname={x} key={x} />)}</ul></span>}
|
|
</div>
|
|
}
|
|
{
|
|
data?.length === 0 && <div className="p-4 text-3xl">No results</div>
|
|
}
|
|
{
|
|
// TODO: date based grouping
|
|
data?.map((docs) => {
|
|
return docs?.data?.map((x) => {
|
|
return (
|
|
<GalleryCard doc={x} key={x.id} />
|
|
);
|
|
});
|
|
})
|
|
}
|
|
{
|
|
<Button className="w-full" onClick={() => setSize(size + 1)}
|
|
disabled={isReachingEnd || isLoadingMore}
|
|
> {isLoadingMore && <Spinner className="mr-1" />}{size + 1} Load more</Button>
|
|
}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function Search() {
|
|
const search = useSearch();
|
|
const [, navigate] = useLocation();
|
|
const searchParams = new URLSearchParams(search);
|
|
const [tags, setTags] = useState(searchParams.get("allow_tag")?.split(",") ?? []);
|
|
const [word, setWord] = useState(searchParams.get("word") ?? "");
|
|
return <div className="flex space-x-2">
|
|
<TagInput className="flex-1" input={word} onInputChange={setWord}
|
|
tags={tags} onTagsChange={setTags}
|
|
/>
|
|
<Button className="flex-none" onClick={()=>{
|
|
const params = new URLSearchParams();
|
|
if (tags.length > 0) {
|
|
for (const tag of tags) {
|
|
params.append("allow_tag", tag);
|
|
}
|
|
}
|
|
if (word) {
|
|
params.set("word", word);
|
|
}
|
|
navigate(`/search?${params.toString()}`);
|
|
}}>Search</Button>
|
|
</div>;
|
|
}
|
|
|