Skip to content

Commit

Permalink
feat: add img2img feature for new model (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
baptadn authored Jan 11, 2023
1 parent 22d0b96 commit 926fc81
Show file tree
Hide file tree
Showing 17 changed files with 669 additions and 203 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"openai": "^3.1.0",
"plaiceholder": "^2.5.0",
"react": "18.2.0",
"react-advanced-cropper": "^0.17.0",
"react-dom": "18.2.0",
"react-dropzone": "^14.2.3",
"react-icons": "^4.7.1",
Expand Down
11 changes: 4 additions & 7 deletions src/components/dashboard/Uploader.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { resizeImage } from "@/core/utils/upload";
import { createPreviewMedia, resizeImage } from "@/core/utils/upload";
import {
Box,
Button,
Expand Down Expand Up @@ -30,11 +30,12 @@ import { CheckedListItem } from "../home/Pricing";
import UploadErrorMessages from "./UploadErrorMessages";

type TUploadState = "not_uploaded" | "uploading" | "uploaded";
export type FilePreview = (File | Blob) & { preview: string };

const MAX_FILES = 25;

const Uploader = ({ handleOnAdd }: { handleOnAdd: () => void }) => {
const [files, setFiles] = useState<(File & { preview: string })[]>([]);
const [files, setFiles] = useState<FilePreview[]>([]);
const [uploadState, setUploadState] = useState<TUploadState>("not_uploaded");
const [errorMessages, setErrorMessages] = useState<string[]>([]);
const [urls, setUrls] = useState<string[]>([]);
Expand Down Expand Up @@ -74,11 +75,7 @@ const Uploader = ({ handleOnAdd }: { handleOnAdd: () => void }) => {
setErrorMessages([]);
setFiles([
...files,
...acceptedFiles.map((file) =>
Object.assign(file, {
preview: URL.createObjectURL(file),
})
),
...acceptedFiles.map((file) => createPreviewMedia(file)),
]);
}
},
Expand Down
47 changes: 35 additions & 12 deletions src/components/layout/AuthForm.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,56 @@
import { getEmailProvider } from "@/core/utils/mail";
import {
Box,
Button,
FormControl,
FormLabel,
Heading,
Icon,
Input,
Link,
Stack,
Text,
} from "@chakra-ui/react";
import { signIn } from "next-auth/react";
import { useRouter } from "next/router";
import { useState } from "react";
import { FaPaperPlane } from "react-icons/fa";
import { MdCheckCircleOutline } from "react-icons/md";
import { useMutation } from "react-query";

export default function AuthForm() {
const [email, setEmail] = useState("");
const router = useRouter();

const { mutate: login, isLoading } = useMutation(
"login",
() =>
signIn("email", { email, redirect: false, callbackUrl: "/dashboard" }),
{
onSuccess: () => {
router.push("/login?verifyRequest=1");
},
}
const {
mutate: login,
isLoading,
isSuccess,
} = useMutation("login", () =>
signIn("email", { email, redirect: false, callbackUrl: "/dashboard" })
);

if (isSuccess) {
const { name, url } = getEmailProvider(email);

return (
<Box mx={{ base: 4, md: 0 }} textAlign="center">
<Heading>
Check your email <Icon mb="-4px" as={MdCheckCircleOutline} />
</Heading>
<Text maxWidth="30rem" mt={3} fontSize="2xl">
A <b>sign in link</b> has been sent to your email address.{" "}
{name && url && (
<>
Check{" "}
<Link textDecoration="underline" isExternal href={url}>
your {name} inbox
</Link>
.
</>
)}
</Text>
</Box>
);
}

return (
<Stack spacing={4} width="100%" mx="auto" maxW="md" py={12} px={6}>
<Stack textAlign="center" align="center" spacing={0}>
Expand Down
112 changes: 0 additions & 112 deletions src/components/projects/PomptWizardPopover.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion src/components/projects/ProjectCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ const ProjectCard = ({
<Badge colorScheme="teal">{project.credits} shots left</Badge>
)}
</Text>
<Text textTransform="capitalize" fontSize="sm">
<Text textTransform="capitalize" fontSize="sm" color="beige.500">
{formatRelative(new Date(project.createdAt), new Date())}
</Text>
</Box>
Expand Down
164 changes: 164 additions & 0 deletions src/components/projects/PromptImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import { createPreviewMedia, resizeImage } from "@/core/utils/upload";
import useProjectContext from "@/hooks/use-project-context";
import {
Button,
ButtonGroup,
Center,
Icon,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
useDisclosure,
} from "@chakra-ui/react";
import { useS3Upload } from "next-s3-upload";
import { useRef, useState } from "react";
import {
FixedCropper,
FixedCropperRef,
ImageRestriction,
} from "react-advanced-cropper";
import { useDropzone } from "react-dropzone";
import {
BsCloud,
BsCloudArrowDown,
BsCloudArrowUp,
BsImage,
} from "react-icons/bs";
import { FilePreview } from "../dashboard/Uploader";

import "react-advanced-cropper/dist/style.css";
import "react-advanced-cropper/dist/themes/compact.css";

const PromptImage = () => {
const [isLoading, setLoading] = useState(false);
const { isOpen, onOpen, onClose } = useDisclosure({
onClose: () => {
setImagePreview(undefined);
},
});

const { setPromptImageUrl } = useProjectContext();
const cropperRef = useRef<FixedCropperRef>(null);

const [imagePreview, setImagePreview] = useState<FilePreview>();

const { uploadToS3 } = useS3Upload();

const { getRootProps, getInputProps } = useDropzone({
accept: {
"image/png": [".png"],
"image/jpeg": [".jpeg", ".jpg"],
},
maxSize: 10000000,
multiple: false,
onDrop: (acceptedFiles) => {
if (acceptedFiles?.[0]) {
setImagePreview(createPreviewMedia(acceptedFiles[0]));
}
},
});

const handleSubmit = async () => {
if (!cropperRef.current) {
return;
}

setLoading(true);
const canvas = cropperRef.current.getCanvas({
height: 512,
width: 512,
})!;

canvas.toBlob(async (blob) => {
const croppedImage = createPreviewMedia(blob!);
const file = await resizeImage(croppedImage as File);

const { url } = await uploadToS3(file);
setLoading(false);

setPromptImageUrl(url);
onClose();
}, "image/jpeg");
};

return (
<>
<Button
rightIcon={<BsImage />}
variant="outline"
size="sm"
onClick={onOpen}
>
Use image as model
</Button>

<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay backdropFilter="auto" backdropBlur="4px" />
<ModalContent
as="form"
onSubmit={async (e) => {
e.preventDefault();
e.stopPropagation();

handleSubmit();
}}
>
<ModalHeader>Use image as model</ModalHeader>
<ModalCloseButton />
<ModalBody>
{imagePreview ? (
<FixedCropper
ref={cropperRef}
src={imagePreview && imagePreview.preview}
stencilSize={{ width: 512, height: 512 }}
imageRestriction={ImageRestriction.stencil}
/>
) : (
<Center
_hover={{
bg: "whiteAlpha.800",
}}
fontSize="sm"
transition="all 0.2s"
backgroundColor="whiteAlpha.500"
cursor="pointer"
borderRadius="xl"
border="1px dashed gray"
p={10}
flexDirection="column"
{...getRootProps({ className: "dropzone" })}
>
<input {...getInputProps()} />
<Icon as={BsCloudArrowUp} fontSize="3xl" />
<Text>Upload reference image</Text>
</Center>
)}
</ModalBody>

<ModalFooter>
<ButtonGroup>
<Button onClick={onClose} variant="ghost">
Cancel
</Button>
<Button
disabled={!imagePreview}
isLoading={isLoading}
variant="brand"
type="submit"
>
Use image
</Button>
</ButtonGroup>
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};

export default PromptImage;
Loading

1 comment on commit 926fc81

@vercel
Copy link

@vercel vercel bot commented on 926fc81 Jan 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.