Update
This commit is contained in:
@@ -3,19 +3,28 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from '@/components/ui/sheet';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useMitt } from '@/plugins/MittContext';
|
||||
import CoinIcon from '@/reusables/coin-icon';
|
||||
import useMediaStore from '@/stores/MediaStore';
|
||||
import useUserStore from '@/stores/UserStore';
|
||||
import { usePage } from '@inertiajs/react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
const EditorAISheet = () => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [prompt, setPrompt] = useState('');
|
||||
const emitter = useMitt();
|
||||
const { generateMeme, isGeneratingMeme, keywords, isLoadingAIHints, fetchAIHints } = useMediaStore();
|
||||
|
||||
const { user } = useUserStore();
|
||||
const { auth } = usePage().props;
|
||||
|
||||
useEffect(() => {
|
||||
const openSheetListener = () => {
|
||||
setIsOpen(true);
|
||||
fetchAIHints();
|
||||
};
|
||||
|
||||
emitter.on('open-ai-editor-sheet', openSheetListener);
|
||||
@@ -23,7 +32,7 @@ const EditorAISheet = () => {
|
||||
return () => {
|
||||
emitter.off('open-ai-editor-sheet', openSheetListener);
|
||||
};
|
||||
}, [emitter]);
|
||||
}, [emitter, fetchAIHints]);
|
||||
|
||||
const handleOpenChange = (open) => {
|
||||
setIsOpen(open);
|
||||
@@ -31,39 +40,77 @@ const EditorAISheet = () => {
|
||||
|
||||
const handleSend = () => {
|
||||
if (prompt.trim()) {
|
||||
console.log('Sending prompt:', prompt);
|
||||
setPrompt('');
|
||||
generateMeme(prompt);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Sheet open={isOpen} onOpenChange={handleOpenChange}>
|
||||
<SheetContent side="bottom" className="gap-0! rounded-t-4xl pb-1">
|
||||
<SheetHeader className="mb-2">
|
||||
<SheetTitle className="text-center text-2xl font-semibold">What can I help with?</SheetTitle>
|
||||
<SheetContent
|
||||
side="bottom"
|
||||
className={cn('gap-0! rounded-t-4xl pb-1', isGeneratingMeme && '[&>button]:hidden')}
|
||||
onInteractOutside={(e) => isGeneratingMeme && e.preventDefault()}
|
||||
onEscapeKeyDown={(e) => isGeneratingMeme && e.preventDefault()}
|
||||
>
|
||||
<SheetHeader className="mb-2 px-5">
|
||||
<SheetTitle className="text-center text-xl font-semibold text-balance">
|
||||
{isGeneratingMeme ? 'Creating...' : 'What meme would you like to create?'}
|
||||
</SheetTitle>
|
||||
<SheetDescription className="hidden"></SheetDescription>
|
||||
</SheetHeader>
|
||||
|
||||
<div className="mx-auto w-full max-w-[600px] space-y-4 px-4 pb-4">
|
||||
<div className="space-y-3">
|
||||
<Input
|
||||
disabled={isGeneratingMeme}
|
||||
placeholder="Make a meme for e.g. work life stress"
|
||||
value={prompt}
|
||||
onChange={(e) => setPrompt(e.target.value)}
|
||||
className="bg-muted/30 max-h-20 min-h-12 resize-none rounded-3xl border-0 p-4 text-base"
|
||||
/>
|
||||
<Button
|
||||
onClick={handleSend}
|
||||
className={cn('w-full rounded-full', !prompt.trim() && 'invisible')}
|
||||
size="lg"
|
||||
variant="outline"
|
||||
disabled={!prompt.trim()}
|
||||
>
|
||||
Generate Meme
|
||||
<div className="flex items-center gap-1">
|
||||
<CoinIcon></CoinIcon> 1
|
||||
|
||||
{/* AI Keywords */}
|
||||
{isLoadingAIHints && <div className="text-muted-foreground text-center text-sm">Loading AI hints...</div>}
|
||||
|
||||
{keywords.length > 0 && !isLoadingAIHints && (
|
||||
<div className="flex flex-wrap justify-center gap-2">
|
||||
{keywords.map((keyword, index) => (
|
||||
<Button
|
||||
disabled={isGeneratingMeme}
|
||||
key={index}
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
className="h-auto rounded-full px-3 py-1 text-xs"
|
||||
onClick={() => setPrompt(keyword)}
|
||||
>
|
||||
{keyword}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</Button>
|
||||
)}
|
||||
<div className={cn('space-y-2', !prompt.trim() && 'invisible')}>
|
||||
<Button
|
||||
onClick={handleSend}
|
||||
className={cn('w-full rounded-full')}
|
||||
size="lg"
|
||||
variant="outline"
|
||||
disabled={!prompt.trim() || isGeneratingMeme}
|
||||
>
|
||||
{isGeneratingMeme ? (
|
||||
<Spinner className="text-primary h-4 w-4" />
|
||||
) : (
|
||||
<>
|
||||
Generate Meme
|
||||
<div className="flex items-center gap-1">
|
||||
<CoinIcon></CoinIcon> 2
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
<div className="text-muted-foreground text-center text-xs">
|
||||
A new meme costs 1 credit for AI captions & 1 credit for AI background.{' '}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</SheetContent>
|
||||
|
||||
@@ -20,6 +20,11 @@ const useMediaStore = create(
|
||||
currentCaption: 'I am chicken rice',
|
||||
watermarked: true,
|
||||
|
||||
keywords: [],
|
||||
isLoadingAIHints: false,
|
||||
|
||||
isGeneratingMeme: false,
|
||||
|
||||
setCurrentTab: (tab) => {
|
||||
set({ currentTab: tab });
|
||||
},
|
||||
@@ -42,6 +47,29 @@ const useMediaStore = create(
|
||||
set({ selectedBackground: null });
|
||||
},
|
||||
|
||||
// Fetch AI hints
|
||||
fetchAIHints: async () => {
|
||||
set({ isLoadingAIHints: true });
|
||||
try {
|
||||
const response = await axiosInstance.post(route('api.ai_hints'));
|
||||
set({
|
||||
keywords: response.data.success?.data?.keywords || [],
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error(route('api.ai_hints'));
|
||||
console.error('Error fetching AI hints:', error);
|
||||
toast.error('Failed to fetch AI hints');
|
||||
} finally {
|
||||
set({ isLoadingAIHints: false });
|
||||
}
|
||||
},
|
||||
|
||||
// Clear keywords
|
||||
clearKeywords: () => {
|
||||
set({ keywords: [] });
|
||||
},
|
||||
|
||||
init: async () => {
|
||||
try {
|
||||
const response = await axiosInstance.post(route('api.app.init'));
|
||||
@@ -62,6 +90,31 @@ const useMediaStore = create(
|
||||
}
|
||||
},
|
||||
|
||||
generateMeme: async (prompt) => {
|
||||
set({ isGeneratingMeme: true });
|
||||
try {
|
||||
const response = await axiosInstance.post(route('api.user.generate_meme', { prompt: prompt }));
|
||||
|
||||
if (response?.data?.success?.data?.generate) {
|
||||
set({
|
||||
currentCaption: response.data.success.data.generate.caption,
|
||||
selectedMeme: response.data.success.data.generate.meme,
|
||||
selectedBackground: response.data.success.data.generate.background,
|
||||
});
|
||||
} else {
|
||||
throw 'Invalid API response';
|
||||
}
|
||||
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error(route('api.app.generate_meme'));
|
||||
console.error('Error generating meme:', error);
|
||||
toast.error('Failed to generate meme');
|
||||
} finally {
|
||||
set({ isGeneratingMeme: false });
|
||||
}
|
||||
},
|
||||
|
||||
// Fetch memes (overlays)
|
||||
fetchMemes: async () => {
|
||||
set({ isFetchingMemes: true });
|
||||
|
||||
Reference in New Issue
Block a user