From 8dc4b55bd64393b8cf80e3875bb37f7c15e5397c Mon Sep 17 00:00:00 2001 From: alex bergvall Date: Thu, 26 Jan 2023 23:25:16 -0600 Subject: [PATCH] upscaling poggers --- package.json | 2 + src/components/projects/CustomZoomContent.tsx | 147 +++++++++++++++--- src/components/projects/ProjectCard.tsx | 5 +- src/components/projects/ShotCard.tsx | 24 +-- src/components/shared/ConfirmationModal.tsx | 79 ---------- src/core/clients/replicate.ts | 2 +- .../[id]/predictions/[predictionId].tsx | 9 ++ src/pages/dashboard.tsx | 1 - yarn.lock | 10 ++ 9 files changed, 159 insertions(+), 120 deletions(-) delete mode 100644 src/components/shared/ConfirmationModal.tsx diff --git a/package.json b/package.json index 2d5d3f8..5f08821 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@stripe/stripe-js": "^1.46.0", "@supabase/auth-helpers-react": "^0.3.1", "@supabase/supabase-js": "^2.1.3", + "@types/file-saver": "^2.0.5", "@types/node": "18.11.10", "@types/react": "18.0.26", "@types/react-dom": "18.0.9", @@ -30,6 +31,7 @@ "date-fns": "^2.29.3", "eslint": "8.29.0", "eslint-config-next": "13.0.6", + "file-saver": "^2.0.5", "framer-motion": "^7.6.19", "image-blob-reduce": "^4.1.0", "jsonwebtoken": "^8.5.1", diff --git a/src/components/projects/CustomZoomContent.tsx b/src/components/projects/CustomZoomContent.tsx index 0fdd550..c360608 100644 --- a/src/components/projects/CustomZoomContent.tsx +++ b/src/components/projects/CustomZoomContent.tsx @@ -1,11 +1,23 @@ +import { getFullShotUrl } from "@/core/utils/bucketHelpers"; import { ShotsPick } from "@/pages/api/projects"; import { Box, Text } from "@chakra-ui/layout"; -import { Button, Portal, useDisclosure } from "@chakra-ui/react"; +import { + Button, + Popover, + PopoverArrow, + PopoverBody, + PopoverCloseButton, + PopoverContent, + PopoverFooter, + PopoverHeader, + PopoverTrigger, + Spinner, + useDisclosure, +} from "@chakra-ui/react"; import axios from "axios"; -import { useRouter } from "next/router"; -import { ReactElement, FC } from "react"; +import { ReactElement, FC, useState, useEffect } from "react"; import { useMutation } from "react-query"; -import ConfirmationModal from "../shared/ConfirmationModal"; +import { saveAs } from "file-saver"; declare const enum ModalState { LOADED = "LOADED", @@ -20,6 +32,7 @@ export type ZoomContentProps = { modalState: ModalState; shot: ShotsPick; onUnzoom: () => void; + refetchShot: () => void; }; type CustomZoomContentProps = {} & ZoomContentProps; @@ -29,25 +42,37 @@ const CustomZoomContent: FC = ({ modalState, img, shot, + refetchShot, }) => { + const [isLoadingUpscale, setIsLoadingUpscale] = useState( + shot.upscaleId && !shot.upscaledImageUrl + ); // If we don't have this check, the unzoom button / description will render // before the modals transition is complete. const modalLoaded = modalState === "LOADED"; - const { isOpen, onClose, onOpen } = useDisclosure(); + const { isOpen, onClose, onOpen, onToggle } = useDisclosure(); - const { - mutate: mutateUpscale, - isLoading: isLoadingUpscale, - isSuccess, - } = useMutation( + const { mutate: mutateUpscale, isLoading: isLoading } = useMutation( `upscale-shot-${shot.id}`, - (shot: ShotsPick) => axios.post(`/api/shots/upscale?id=${shot.id}`), + (shot: ShotsPick) => + axios.post(`/api/shots/upscale?id=${shot.id}`).then((res) => res.data), { onSuccess: () => { - // refresh shots later + setIsLoadingUpscale(true); + refetchShot(); }, } ); + + useEffect(() => { + if (!isLoadingUpscale) return; + const interval = setInterval(() => { + refetchShot(); + }, 5000); + + return () => clearInterval(interval); + }, [isLoadingUpscale]); + return ( <> {modalLoaded && buttonUnzoom} @@ -60,23 +85,95 @@ const CustomZoomContent: FC = ({ )} - - {modalLoaded && ( - - )} - + - mutateUpscale(shot)} - /> + {shot.imageUrl && ( + + )} + {shot.upscaledImageUrl && ( + + )} + {isLoadingUpscale && ( + + )} + {!isLoadingUpscale && ( + + + + + + + + Upscale Image + + + + + Are you sure you want to spend 1 credit to upscale this + image? + + + + + + + + + + + + )} ); diff --git a/src/components/projects/ProjectCard.tsx b/src/components/projects/ProjectCard.tsx index fb1a06e..0238db4 100644 --- a/src/components/projects/ProjectCard.tsx +++ b/src/components/projects/ProjectCard.tsx @@ -50,7 +50,6 @@ const ProjectCard = ({ }, } ); - const [startTime, setStartTime] = useState(0); const isWaitingPayment = !project.stripePaymentId; const isWaitingTraining = @@ -66,8 +65,8 @@ const ProjectCard = ({ }; const checkBackTime = formatRelative( - new Date(new Date(startTime).getTime() + 30 * 60 * 1000), - new Date(startTime) + new Date(new Date(project.createdAt).getTime() + 30 * 60 * 1000), + new Date(project.createdAt) ) .replace("at", "around") .replace("today", ""); diff --git a/src/components/projects/ShotCard.tsx b/src/components/projects/ShotCard.tsx index 14f76b3..6a7d757 100644 --- a/src/components/projects/ShotCard.tsx +++ b/src/components/projects/ShotCard.tsx @@ -15,7 +15,15 @@ const ShotCard = ({ shot: ShotsPick; projectId: string; }) => { - const { data } = useQuery( + // if shot has failed status, dont refetch, + // if shot has upscaleId and no upscaledImageUrl, refetch + // if shot has no imageUrl, refetch + const enableFetch = + initialShot.status !== "failed" && + ((initialShot.upscaleId && !initialShot.upscaledImageUrl) || + !initialShot.imageUrl); + + const { data, refetch: refetchShot } = useQuery( `shot-${initialShot.id}`, () => axios @@ -24,22 +32,15 @@ const ShotCard = ({ ) .then((res) => res.data), { - refetchInterval: (data) => - !data?.shot.imageUrl || - (data?.shot.upscaleId && !data?.shot.upscaledImageUrl) - ? 5000 - : false, + refetchInterval: (data) => (enableFetch ? 5000 : false), refetchOnWindowFocus: false, - enabled: - (initialShot.upscaleId && - initialShot.status !== "failed" && - !initialShot.upscaledImageUrl) || - (!initialShot.imageUrl && initialShot.status !== "failed"), + enabled: enableFetch, initialData: { shot: initialShot }, } ); const shot = data!.shot; + return ( {shot.status === "failed" && ( @@ -56,6 +57,7 @@ const ShotCard = ({ // Type script being annoying, i'll figure it out later. {...(zoomContentProps as unknown as ZoomContentProps)} shot={shot} + refetchShot={refetchShot} /> )} > diff --git a/src/components/shared/ConfirmationModal.tsx b/src/components/shared/ConfirmationModal.tsx deleted file mode 100644 index 4e89f4f..0000000 --- a/src/components/shared/ConfirmationModal.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { - Box, - Button, - Modal, - ModalBody, - ModalContent, - ModalFooter, - ModalHeader, - ModalOverlay, - Portal, - Text, -} from "@chakra-ui/react"; -import { useSession } from "next-auth/react"; -import { useRouter } from "next/router"; - -interface CreditsModalProps { - /** - * Whether modal is open - */ - isOpen: boolean; - /** - * Modal close callback - */ - onClose: () => void; - onConfirm: () => void; -} - -const ConfirmationModal = ({ - isOpen, - onClose, - onConfirm, -}: CreditsModalProps) => { - const router = useRouter(); - const { data: userSession } = useSession(); - const userId = userSession?.user.id; - // Options for credit packages radio buttons, we only want the id here to render options - return ( -
- { - onClose(); - }} - isCentered={true} - > - - - Upscale Image - - - Are you sure you want to spend 1 credit to upscale this image? - - - - - - - - - - -
- ); -}; - -export default ConfirmationModal; diff --git a/src/core/clients/replicate.ts b/src/core/clients/replicate.ts index a8e3b64..92ebb66 100644 --- a/src/core/clients/replicate.ts +++ b/src/core/clients/replicate.ts @@ -347,7 +347,7 @@ export type PredictionResponse = { created_at: Date; started_at: Date | null; completed_at: Date | null; - status: "starting" | "pushing" | "succeeded"; + status: "starting" | "pushing" | "succeeded" | "failed"; input: any; // the input is free form it comes output: any; error: any; diff --git a/src/pages/api/projects/[id]/predictions/[predictionId].tsx b/src/pages/api/projects/[id]/predictions/[predictionId].tsx index 20597c7..937fd98 100644 --- a/src/pages/api/projects/[id]/predictions/[predictionId].tsx +++ b/src/pages/api/projects/[id]/predictions/[predictionId].tsx @@ -18,6 +18,15 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { const { data: prediction } = await replicateClient.get( `https://api.replicate.com/v1/predictions/${fetchId}` ); + if (prediction.status === "failed") { + await db.shot.update({ + where: { id: shot.id }, + data: { + status: prediction.status, + upscaleId: null, + }, + }); + } // If the initial shot status changes from the prediction, update the shot in database. if (shot.status !== prediction.status) { const outputUrl = shot.upscaleId diff --git a/src/pages/dashboard.tsx b/src/pages/dashboard.tsx index f392f72..abd0792 100644 --- a/src/pages/dashboard.tsx +++ b/src/pages/dashboard.tsx @@ -17,7 +17,6 @@ import { useQuery } from "react-query"; import PageContainer from "@/components/layout/PageContainer"; import { useRouter } from "next/router"; import { useSession } from "next-auth/react"; -import { useEffect } from "react"; import ExamplePictures from "@/components/dashboard/ExamplePictures"; import { ProjectsGetResponse } from "./api/projects"; diff --git a/yarn.lock b/yarn.lock index 0c108e6..c1464c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1250,6 +1250,11 @@ dependencies: tslib "^2.4.0" +"@types/file-saver@^2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@types/file-saver/-/file-saver-2.0.5.tgz#9ee342a5d1314bb0928375424a2f162f97c310c7" + integrity sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ== + "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" @@ -2293,6 +2298,11 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" +file-saver@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.5.tgz#d61cfe2ce059f414d899e9dd6d4107ee25670c38" + integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA== + file-selector@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.6.0.tgz#fa0a8d9007b829504db4d07dd4de0310b65287dc"