Spaces:
Running
Running
| import { useRef, useState } from "react"; | |
| import { Images, Upload } from "lucide-react"; | |
| import Image from "next/image"; | |
| import { | |
| Popover, | |
| PopoverContent, | |
| PopoverTrigger, | |
| } from "@/components/ui/popover"; | |
| import { Button } from "@/components/ui/button"; | |
| import { Page, Project } from "@/types"; | |
| import Loading from "@/components/loading"; | |
| import { RiCheckboxCircleFill } from "react-icons/ri"; | |
| import { useUser } from "@/hooks/useUser"; | |
| import { LoginModal } from "@/components/login-modal"; | |
| import { DeployButtonContent } from "../deploy-button/content"; | |
| export const Uploader = ({ | |
| pages, | |
| onLoading, | |
| isLoading, | |
| onFiles, | |
| onSelectFile, | |
| selectedFiles, | |
| files, | |
| project, | |
| }: { | |
| pages: Page[]; | |
| onLoading: (isLoading: boolean) => void; | |
| isLoading: boolean; | |
| files: string[]; | |
| onFiles: React.Dispatch<React.SetStateAction<string[]>>; | |
| onSelectFile: (file: string) => void; | |
| selectedFiles: string[]; | |
| project?: Project | null; | |
| }) => { | |
| const { user } = useUser(); | |
| const [open, setOpen] = useState(false); | |
| const fileInputRef = useRef<HTMLInputElement>(null); | |
| const uploadFiles = async (files: FileList | null) => { | |
| if (!files) return; | |
| if (!project) return; | |
| onLoading(true); | |
| const images = Array.from(files).filter((file) => { | |
| return file.type.startsWith("image/"); | |
| }); | |
| const data = new FormData(); | |
| images.forEach((image) => { | |
| data.append("images", image); | |
| }); | |
| const response = await fetch( | |
| `/api/me/projects/${project.space_id}/images`, | |
| { | |
| method: "POST", | |
| body: data, | |
| } | |
| ); | |
| if (response.ok) { | |
| const data = await response.json(); | |
| onFiles((prev) => [...prev, ...data.uploadedFiles]); | |
| } | |
| onLoading(false); | |
| }; | |
| // TODO FIRST PUBLISH YOUR PROJECT TO UPLOAD IMAGES. | |
| return user?.id ? ( | |
| <Popover open={open} onOpenChange={setOpen}> | |
| <form> | |
| <PopoverTrigger asChild> | |
| <Button | |
| size="iconXs" | |
| variant="outline" | |
| className="!border-neutral-600 !text-neutral-400 !hover:!border-neutral-500 hover:!text-neutral-300" | |
| > | |
| <Images className="size-4" /> | |
| </Button> | |
| </PopoverTrigger> | |
| <PopoverContent | |
| align="start" | |
| className="!rounded-2xl !p-0 !bg-white !border-neutral-100 min-w-xs text-center overflow-hidden" | |
| > | |
| {project?.space_id ? ( | |
| <> | |
| <header className="bg-neutral-50 p-6 border-b border-neutral-200/60"> | |
| <div className="flex items-center justify-center -space-x-4 mb-3"> | |
| <div className="size-9 rounded-full bg-pink-200 shadow-2xs flex items-center justify-center text-xl opacity-50"> | |
| 🎨 | |
| </div> | |
| <div className="size-11 rounded-full bg-amber-200 shadow-2xl flex items-center justify-center text-2xl z-2"> | |
| 🖼️ | |
| </div> | |
| <div className="size-9 rounded-full bg-sky-200 shadow-2xs flex items-center justify-center text-xl opacity-50"> | |
| 💻 | |
| </div> | |
| </div> | |
| <p className="text-xl font-semibold text-neutral-950"> | |
| Add Custom Images | |
| </p> | |
| <p className="text-sm text-neutral-500 mt-1.5"> | |
| Upload images to your project and use them with DeepSite! | |
| </p> | |
| </header> | |
| <main className="space-y-4 p-5"> | |
| <div> | |
| <p className="text-xs text-left text-neutral-700 mb-2"> | |
| Uploaded Images | |
| </p> | |
| <div className="grid grid-cols-4 gap-1 flex-wrap max-h-40 overflow-y-auto"> | |
| {files.map((file) => ( | |
| <div | |
| key={file} | |
| className="select-none relative cursor-pointer bg-white rounded-md border-[2px] border-white hover:shadow-2xl transition-all duration-300" | |
| onClick={() => onSelectFile(file)} | |
| > | |
| <Image | |
| src={file} | |
| alt="uploaded image" | |
| width={56} | |
| height={56} | |
| className="object-cover w-full rounded-sm aspect-square" | |
| /> | |
| {selectedFiles.includes(file) && ( | |
| <div className="absolute top-0 right-0 h-full w-full flex items-center justify-center bg-black/50 rounded-md"> | |
| <RiCheckboxCircleFill className="size-6 text-neutral-100" /> | |
| </div> | |
| )} | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| <div> | |
| <p className="text-xs text-left text-neutral-700 mb-2"> | |
| Or import images from your computer | |
| </p> | |
| <Button | |
| variant="black" | |
| onClick={() => fileInputRef.current?.click()} | |
| className="relative w-full" | |
| > | |
| {isLoading ? ( | |
| <> | |
| <Loading | |
| overlay={false} | |
| className="ml-2 size-4 animate-spin" | |
| /> | |
| Uploading image(s)... | |
| </> | |
| ) : ( | |
| <> | |
| <Upload className="size-4" /> | |
| Upload Images | |
| </> | |
| )} | |
| </Button> | |
| <input | |
| ref={fileInputRef} | |
| type="file" | |
| className="hidden" | |
| multiple | |
| accept="image/*" | |
| onChange={(e) => uploadFiles(e.target.files)} | |
| /> | |
| </div> | |
| </main> | |
| </> | |
| ) : ( | |
| <DeployButtonContent | |
| pages={pages} | |
| prompts={[]} | |
| options={{ | |
| description: "Publish your project first to add custom images.", | |
| }} | |
| /> | |
| )} | |
| </PopoverContent> | |
| </form> | |
| </Popover> | |
| ) : ( | |
| <> | |
| <Button | |
| size="iconXs" | |
| variant="outline" | |
| className="!border-neutral-600 !text-neutral-400 !hover:!border-neutral-500 hover:!text-neutral-300" | |
| onClick={() => setOpen(true)} | |
| > | |
| <Images className="size-4" /> | |
| </Button> | |
| <LoginModal | |
| open={open} | |
| onClose={() => setOpen(false)} | |
| pages={pages} | |
| title="Log In to add Custom Images" | |
| description="Log In through your Hugging Face account to publish your project and increase your monthly free limit." | |
| /> | |
| </> | |
| ); | |
| }; | |