Skip to content

feat: Recent image preview card added in generate image tab #53

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apps/backend/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import dotenv from "dotenv";

import paymentRoutes from "./routes/payment.routes";
import { router as webhookRouter } from "./routes/webhook.routes";
import { router as imageRouter } from "./routes/getimage.routes";

const IMAGE_GEN_CREDITS = 1;
const TRAIN_MODEL_CREDITS = 20;
Expand Down Expand Up @@ -155,6 +156,7 @@ app.post("/ai/generate", authMiddleware, async (req, res) => {

res.json({
imageId: data.id,
falReqId: request_id
});
});

Expand Down Expand Up @@ -443,6 +445,7 @@ app.get("/model/status/:modelId", authMiddleware, async (req, res) => {

app.use("/payment", paymentRoutes);
app.use("/api/webhook", webhookRouter);
app.use("/api/image/", imageRouter);

app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
Expand Down
24 changes: 24 additions & 0 deletions apps/backend/routes/getimage.routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { prismaClient } from "db";
import { Router } from "express"

export const router = Router()

router.post('/status/:falreqid', async (req, res) => {
const falReqId = req.params.falreqid;
try {
const image = await prismaClient.outputImages.findFirst({
where: {
AND: [{ falAiRequestId: falReqId }, { userId: req.userId }]
},
select: {
imageUrl: true,
status: true
}
})

res.json({ imageUrl: image?.imageUrl, status: image?.status })
} catch (error) {
console.error(error)
res.status(403).json({ msg: "Error while fetching image" })
}
})
81 changes: 74 additions & 7 deletions apps/web/components/GenerateImage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client"
import { useAuth } from "@clerk/nextjs"
import { useState } from "react"
import { useEffect, useState } from "react"
import { Button } from "./ui/button"
import { Textarea } from "@/components/ui/textarea"
import axios from "axios"
Expand All @@ -11,10 +11,18 @@ import { motion } from "framer-motion"
import { Sparkles } from "lucide-react"
import { useCredits } from "@/hooks/use-credits"
import { useRouter } from "next/navigation"
import { Badge } from "./ui/badge"
import { Dialog, DialogTrigger } from "./ui/dialog"
import Image from "next/image"
import { Skeleton } from "./ui/skeleton"
export function GenerateImage() {
const [prompt, setPrompt] = useState("")
const [selectedModel, setSelectedModel] = useState<string>()
const [isGenerating, setIsGenerating] = useState(false)
const [falReqId, setFalReqId] = useState<string>("e05658a5-4904-49fd-bc4d-bf67d32511fa")
const [imageStatus, setImageStatus] = useState<"Pending" | "Generated" | "Failed">("Pending")
const [imageUrl, setImageUrl] = useState<string>()
const [, setImageLoading] = useState(true)
const { getToken } = useAuth()
const { credits } = useCredits();
const router = useRouter()
Expand All @@ -26,11 +34,11 @@ export function GenerateImage() {
router.push("/pricing")
return
}

setIsGenerating(true)
try {
const token = await getToken()
await axios.post(`${BACKEND_URL}/ai/generate`, {
const response = await axios.post(`${BACKEND_URL}/ai/generate`, {
prompt,
modelId: selectedModel,
num: 1
Expand All @@ -39,22 +47,52 @@ export function GenerateImage() {
})
toast.success("Image generation started!")
setPrompt("")
setFalReqId(response.data.falReqId)
} catch (error) {
toast.error("Failed to generate image")
} finally {
setIsGenerating(false)
}
}

async function fetchImage() {
try {
setImageStatus('Pending')
const res = await axios.post(`${BACKEND_URL}/api/image/status/${falReqId}`)
if (res.data.status == 'Failed') {
setImageLoading(false)
setImageStatus('Failed')
return
}
setImageStatus(res.data.status)
if (res.data.imageUrl) setImageUrl(res.data.imageUrl)
} catch (error) {
setImageStatus('Failed')
setImageLoading(false)
console.error(error)
}
}

useEffect(() => {
if (!falReqId || imageUrl) return;

const interval = setInterval(() => {
fetchImage();
}, 2000);

return () => clearInterval(interval);

}, [falReqId, imageUrl])

return (
<motion.div
className="max-w-2xl mx-auto space-y-6"
<motion.div
className={`grid ${isGenerating ? 'md:grid-cols-3' : 'grid-cols-1'} grid-cols-1 w-full gap-4 px-2`}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>

<div className="space-y-4">
<div className="space-y-4 w-full col-span-2">
<SelectModel
selectedModel={selectedModel}
setSelectedModel={setSelectedModel}
Expand All @@ -73,7 +111,7 @@ export function GenerateImage() {
/>
</motion.div>

<motion.div
<motion.div
className="flex justify-end"
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
Expand All @@ -88,6 +126,35 @@ export function GenerateImage() {
</Button>
</motion.div>
</div>
{
isGenerating &&
<div className="space-y-4 w-full">
<div className="bg-neutral-900 rounded-lg p-4">
<div className="flex items-center justify-between mb-4">
<h2 className="text-xl font-semibold">Result</h2>
<Badge>{imageStatus}</Badge>
</div>
<Dialog>
<DialogTrigger className="w-full h-full">
{
!imageUrl ?
<Skeleton className="w-full h-86" /> :
<Image
src={imageUrl || ''}
alt='image'
width={1000}
height={1000}
className='object-cover rounded-md'
/>
}
</DialogTrigger>
</Dialog>
<div className="mt-4 text-sm text-neutral-400">
<p>Your request will cost 1 credit per operation.</p>
</div>
</div>
</div>
}
</motion.div>
)
}