Skip to content

Commit

Permalink
Support download image and copy image url
Browse files Browse the repository at this point in the history
  • Loading branch information
ahaapple committed Dec 26, 2024
1 parent 77e7790 commit 72a4cdf
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 34 deletions.

This file was deleted.

6 changes: 4 additions & 2 deletions frontend/app/[locale]/(marketing)/generate-image/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import PageWrapper from '@/app/[locale]/(marketing)/generate-image/generate-image-wrapper';
import { AIImageGenerator } from '@/components/image/image-generator';
import { siteConfig } from '@/config';
import { Metadata } from 'next/types';

Expand Down Expand Up @@ -26,7 +26,9 @@ export default async function Page() {
return (
<div className="min-h-screen flex flex-col w-full items-center justify-center bg-gradient-to-b from-primary/[0.04] to-transparen">
<h1 className="text-2xl font-bold text-center pt-24 md:pt-32">Generate Image With AI</h1>
<PageWrapper />
<div className="container mx-auto px-4 py-12">
<AIImageGenerator />
</div>
</div>
);
}
46 changes: 28 additions & 18 deletions frontend/components/image/image-generator.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
// components/AIImageGenerator.tsx
'use client';

import { useState, useEffect } from 'react';
import { useState, useEffect, useCallback } from 'react';
import { toast } from 'sonner';
import { Check, Palette } from 'lucide-react';
import { Check, Loader2, Palette } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import TextareaAutosize from 'react-textarea-autosize';
Expand All @@ -14,11 +13,8 @@ import { Label } from '@/components/ui/label';
import ImageUseCaseSelector from '@/components/image/image-use-case-selector';
import { useSigninModal } from '@/hooks/use-signin-modal';
import { useUserStore } from '@/lib/store/local-store';

interface AIImageGeneratorProps {
onImageGenerated: (imageUrl: string) => void;
onCancel?: () => void;
}
import useCopyToClipboard from '@/hooks/use-copy-clipboard';
import { useDownloadImage } from '@/hooks/use-download-image';

const imageStyles = [
{ value: 'digital_illustration', label: 'Digital' },
Expand All @@ -36,7 +32,7 @@ const imageColors = [
{ value: 'violet', name: 'Violet', color: 'bg-violet-500' },
];

export function AIImageGenerator({ onImageGenerated, onCancel }: AIImageGeneratorProps) {
export function AIImageGenerator() {
const [prompt, setPrompt] = useState('');
const [useCase, setUseCase] = useState('social_media_post');
const [style, setStyle] = useState('realistic_image');
Expand All @@ -47,20 +43,23 @@ export function AIImageGenerator({ onImageGenerated, onCancel }: AIImageGenerato
const [showText, setShowText] = useState(true);
const user = useUserStore((state) => state.user);
const signInModal = useSigninModal();
const { hasCopied, copyToClipboard } = useCopyToClipboard();

const [imageSize, setImageSize] = useState({
selectedSize: 'landscape',
width: '1024',
height: '576',
});

const handleCopy = useCallback(() => {
copyToClipboard(generatedImage);
}, [generatedImage, copyToClipboard]);

const handleSizeChange = (size: { selectedSize: string; width: string; height: string }) => {
setImageSize(size);
};

const handleUseCaseChange = (useCase) => {
console.log(useCase.selectedUseCase);
console.log(useCase.customUseCase);
if (useCase.selectedUseCase === 'custom') {
setUseCase(useCase.customUseCase || 'social_media_post');
} else {
Expand All @@ -69,6 +68,8 @@ export function AIImageGenerator({ onImageGenerated, onCancel }: AIImageGenerato
console.log('Use case changed', useCase);
};

const { downloadImage, isDownloading } = useDownloadImage();

const handleGenerateImage = async () => {
if (!prompt) return;

Expand Down Expand Up @@ -223,7 +224,7 @@ export function AIImageGenerator({ onImageGenerated, onCancel }: AIImageGenerato
{isGenerating && (
<div className="space-y-2">
<div className="w-full bg-gray-200 rounded-full h-2.5">
<div className="bg-blue-600 h-2.5 rounded-full transition-all duration-300 ease-out" style={{ width: `${progress}%` }}></div>
<div className="bg-primary h-2.5 rounded-full transition-all duration-300 ease-out" style={{ width: `${progress}%` }}></div>
</div>
<p className="text-sm text-muted-foreground text-center animate-pulse">
{progress < 30
Expand All @@ -246,13 +247,22 @@ export function AIImageGenerator({ onImageGenerated, onCancel }: AIImageGenerato
Regenerate
</Button>

{/* <Button onClick={() => onImageGenerated(generatedImage)}>Save Image</Button> */}
<Button onClick={handleCopy}>{hasCopied ? 'Copied' : 'Copy Image Link'}</Button>

{onCancel && (
<Button variant="outline" onClick={onCancel}>
Cancel
</Button>
)}
<Button
variant="secondary"
onClick={() => downloadImage(generatedImage, `memfree-generate-image-${Date.now()}.png`)}
disabled={isDownloading}
>
{isDownloading ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Downloading...
</>
) : (
'Download Image'
)}
</Button>
</div>
</div>
)}
Expand Down
46 changes: 46 additions & 0 deletions frontend/hooks/use-download-image.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// hooks/useImageDownloader.ts
import { useState } from 'react';
import { toast } from 'sonner';

interface UseImageDownloaderReturn {
downloadImage: (imageUrl: string, filename?: string) => Promise<void>;
isDownloading: boolean;
}

export function useDownloadImage(): UseImageDownloaderReturn {
const [isDownloading, setIsDownloading] = useState(false);

const downloadImage = async (imageUrl: string, filename = 'generated-image.png') => {
if (!imageUrl) return;

try {
setIsDownloading(true);

const response = await fetch(imageUrl);
if (!response.ok) throw new Error('Network response was not ok');

const blob = await response.blob();
const blobUrl = window.URL.createObjectURL(blob);

const link = document.createElement('a');
link.href = blobUrl;
link.download = filename;

document.body.appendChild(link);
link.click();
document.body.removeChild(link);

window.URL.revokeObjectURL(blobUrl);
} catch (error) {
console.error('Download failed:', error);
toast.error('Failed to download image');
} finally {
setIsDownloading(false);
}
};

return {
downloadImage,
isDownloading,
};
}

0 comments on commit 72a4cdf

Please sign in to comment.