Perplexica/ui/components/SearchImages.tsx

129 lines
4.9 KiB
TypeScript

import { ImagesIcon, PlusIcon } from "lucide-react";
import { useState } from "react";
import Lightbox from "yet-another-react-lightbox";
import "yet-another-react-lightbox/styles.css";
import { Message } from "./ChatWindow";
type Image = {
url: string;
img_src: string;
title: string;
};
const SearchImages = ({ query, chat_history }: { query: string; chat_history: Message[] }) => {
const [images, setImages] = useState<Image[] | null>(null);
const [loading, setLoading] = useState(false);
const [open, setOpen] = useState(false);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const [slides, setSlides] = useState<any[]>([]);
return (
<>
{!loading && images === null && (
<button
onClick={async () => {
setLoading(true);
const chatModelProvider = localStorage.getItem("chatModelProvider");
const chatModel = localStorage.getItem("chatModel");
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/images`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
query: query,
chat_history: chat_history,
chat_model_provider: chatModelProvider,
chat_model: chatModel,
}),
});
const data = await res.json();
const images = data.images;
setImages(images);
setSlides(
images.map((image: Image) => {
return {
src: image.img_src,
};
}),
);
setLoading(false);
}}
className="border border-dashed border-light-200 dark:border-dark-200 hover:bg-light-200 dark:hover:bg-dark-200 active:scale-95 duration-200 transition px-4 py-2 flex flex-row items-center justify-between rounded-lg dark:text-white text-sm w-full"
>
<div className="flex flex-row items-center space-x-2">
<ImagesIcon size={17} />
<p>Search images</p>
</div>
<PlusIcon className="text-[#24A0ED]" size={17} />
</button>
)}
{loading && (
<div className="grid grid-cols-2 gap-2">
{Array.from({ length: 4 }).map((_, index) => (
<div
key={index}
className="bg-light-secondary dark:bg-dark-secondary h-32 w-full rounded-lg animate-pulse aspect-video object-cover"
/>
))}
</div>
)}
{images !== null && images.length > 0 && (
<>
<div className="grid grid-cols-2 gap-2">
{images.length > 4
? images.slice(0, 3).map((image, index) => (
<img
onClick={() => {
setOpen(true);
setSlides([slides[index], ...slides.slice(0, index), ...slides.slice(index + 1)]);
}}
key={index}
src={image.img_src}
alt={image.title}
className="h-full w-full aspect-video object-cover rounded-lg transition duration-200 active:scale-95 hover:scale-[1.02] cursor-zoom-in"
/>
))
: images.map((image, index) => (
<img
onClick={() => {
setOpen(true);
setSlides([slides[index], ...slides.slice(0, index), ...slides.slice(index + 1)]);
}}
key={index}
src={image.img_src}
alt={image.title}
className="h-full w-full aspect-video object-cover rounded-lg transition duration-200 active:scale-95 hover:scale-[1.02] cursor-zoom-in"
/>
))}
{images.length > 4 && (
<button
onClick={() => setOpen(true)}
className="bg-light-100 hover:bg-light-200 dark:bg-dark-100 dark:hover:bg-dark-200 transition duration-200 active:scale-95 hover:scale-[1.02] h-auto w-full rounded-lg flex flex-col justify-between text-white p-2"
>
<div className="flex flex-row items-center space-x-1">
{images.slice(3, 6).map((image, index) => (
<img
key={index}
src={image.img_src}
alt={image.title}
className="h-6 w-12 rounded-md lg:h-3 lg:w-6 lg:rounded-sm aspect-video object-cover"
/>
))}
</div>
<p className="text-black/70 dark:text-white/70 text-xs">View {images.length - 3} more</p>
</button>
)}
</div>
<Lightbox open={open} close={() => setOpen(false)} slides={slides} />
</>
)}
</>
);
};
export default SearchImages;