Update
This commit is contained in:
@@ -12,136 +12,190 @@ post {
|
||||
|
||||
body:json {
|
||||
{
|
||||
"id": "evt_1RgRPrEEXQJo9EEOWggvNEHe",
|
||||
"id": "evt_1RghW8EEXQJo9EEOI2W1LyN4",
|
||||
"object": "event",
|
||||
"api_version": "2025-05-28.basil",
|
||||
"created": 1751465619,
|
||||
"created": 1751527512,
|
||||
"data": {
|
||||
"object": {
|
||||
"id": "cs_test_a1RMSyzQu5Mk2cZYoAYLOCV9hfoc5z9RuYdE4jLY2htoizGr1AJZeAcnDh",
|
||||
"object": "checkout.session",
|
||||
"adaptive_pricing": {
|
||||
"enabled": true
|
||||
},
|
||||
"after_expiration": null,
|
||||
"allow_promotion_codes": null,
|
||||
"amount_subtotal": 400,
|
||||
"amount_total": 400,
|
||||
"id": "sub_1RgU1iEEXQJo9EEOBmsO7ZIo",
|
||||
"object": "subscription",
|
||||
"application": null,
|
||||
"application_fee_percent": null,
|
||||
"automatic_tax": {
|
||||
"disabled_reason": null,
|
||||
"enabled": false,
|
||||
"liability": null,
|
||||
"provider": null,
|
||||
"status": null
|
||||
"liability": null
|
||||
},
|
||||
"billing_address_collection": null,
|
||||
"cancel_url": "https://memeaigen.test/subscribe/cancelled?session_id={CHECKOUT_SESSION_ID}",
|
||||
"client_reference_id": null,
|
||||
"client_secret": null,
|
||||
"collected_information": {
|
||||
"shipping_details": null
|
||||
"billing_cycle_anchor": 1751475654,
|
||||
"billing_cycle_anchor_config": null,
|
||||
"billing_mode": {
|
||||
"type": "classic"
|
||||
},
|
||||
"consent": null,
|
||||
"consent_collection": null,
|
||||
"created": 1751465613,
|
||||
"billing_thresholds": null,
|
||||
"cancel_at": null,
|
||||
"cancel_at_period_end": false,
|
||||
"canceled_at": 1751527511,
|
||||
"cancellation_details": {
|
||||
"comment": null,
|
||||
"feedback": null,
|
||||
"reason": "cancellation_requested"
|
||||
},
|
||||
"collection_method": "charge_automatically",
|
||||
"created": 1751475654,
|
||||
"currency": "usd",
|
||||
"currency_conversion": null,
|
||||
"custom_fields": [],
|
||||
"custom_text": {
|
||||
"after_submit": null,
|
||||
"shipping_address": null,
|
||||
"submit": null,
|
||||
"terms_of_service_acceptance": null
|
||||
},
|
||||
"customer": "cus_SbGYl34MpG4nv5",
|
||||
"customer_creation": null,
|
||||
"customer_details": {
|
||||
"address": {
|
||||
"city": null,
|
||||
"country": "MY",
|
||||
"line1": null,
|
||||
"line2": null,
|
||||
"postal_code": null,
|
||||
"state": null
|
||||
},
|
||||
"email": "memeaigen.com@gmail.com",
|
||||
"name": "TEST NAME",
|
||||
"phone": null,
|
||||
"tax_exempt": "none",
|
||||
"tax_ids": []
|
||||
},
|
||||
"customer_email": null,
|
||||
"discounts": [],
|
||||
"expires_at": 1751552013,
|
||||
"invoice": null,
|
||||
"invoice_creation": {
|
||||
"enabled": false,
|
||||
"invoice_data": {
|
||||
"account_tax_ids": null,
|
||||
"custom_fields": null,
|
||||
"days_until_due": null,
|
||||
"default_payment_method": "pm_1RgU1hEEXQJo9EEOBnHcyDTz",
|
||||
"default_source": null,
|
||||
"default_tax_rates": [],
|
||||
"description": null,
|
||||
"footer": null,
|
||||
"issuer": null,
|
||||
"metadata": {},
|
||||
"rendering_options": null
|
||||
"discounts": [],
|
||||
"ended_at": 1751527511,
|
||||
"invoice_settings": {
|
||||
"account_tax_ids": null,
|
||||
"issuer": {
|
||||
"type": "self"
|
||||
}
|
||||
},
|
||||
"items": {
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"id": "si_SbhQOOEJ1xUQ7Y",
|
||||
"object": "subscription_item",
|
||||
"billing_thresholds": null,
|
||||
"created": 1751475654,
|
||||
"current_period_end": 1754154054,
|
||||
"current_period_start": 1751475654,
|
||||
"discounts": [],
|
||||
"metadata": {},
|
||||
"plan": {
|
||||
"id": "price_1RfN2VEEXQJo9EEOzjPI2HGt",
|
||||
"object": "plan",
|
||||
"active": true,
|
||||
"amount": 400,
|
||||
"amount_decimal": "400",
|
||||
"billing_scheme": "per_unit",
|
||||
"created": 1751210467,
|
||||
"currency": "usd",
|
||||
"interval": "month",
|
||||
"interval_count": 1,
|
||||
"livemode": false,
|
||||
"locale": null,
|
||||
"metadata": {},
|
||||
"mode": "payment",
|
||||
"payment_intent": "pi_3RgRPqEEXQJo9EEO1a43uB5g",
|
||||
"payment_link": null,
|
||||
"payment_method_collection": "if_required",
|
||||
"payment_method_configuration_details": {
|
||||
"id": "pmc_1RfN0QEEXQJo9EEOzYHrN3LV",
|
||||
"parent": null
|
||||
"meter": null,
|
||||
"nickname": null,
|
||||
"product": "prod_SaY8TGjiPi5hWu",
|
||||
"tiers_mode": null,
|
||||
"transform_usage": null,
|
||||
"trial_period_days": null,
|
||||
"usage_type": "licensed"
|
||||
},
|
||||
"price": {
|
||||
"id": "price_1RfN2VEEXQJo9EEOzjPI2HGt",
|
||||
"object": "price",
|
||||
"active": true,
|
||||
"billing_scheme": "per_unit",
|
||||
"created": 1751210467,
|
||||
"currency": "usd",
|
||||
"custom_unit_amount": null,
|
||||
"livemode": false,
|
||||
"lookup_key": null,
|
||||
"metadata": {},
|
||||
"nickname": null,
|
||||
"product": "prod_SaY8TGjiPi5hWu",
|
||||
"recurring": {
|
||||
"interval": "month",
|
||||
"interval_count": 1,
|
||||
"meter": null,
|
||||
"trial_period_days": null,
|
||||
"usage_type": "licensed"
|
||||
},
|
||||
"tax_behavior": "unspecified",
|
||||
"tiers_mode": null,
|
||||
"transform_quantity": null,
|
||||
"type": "recurring",
|
||||
"unit_amount": 400,
|
||||
"unit_amount_decimal": "400"
|
||||
},
|
||||
"quantity": 1,
|
||||
"subscription": "sub_1RgU1iEEXQJo9EEOBmsO7ZIo",
|
||||
"tax_rates": []
|
||||
}
|
||||
],
|
||||
"has_more": false,
|
||||
"total_count": 1,
|
||||
"url": "/v1/subscription_items?subscription=sub_1RgU1iEEXQJo9EEOBmsO7ZIo"
|
||||
},
|
||||
"latest_invoice": "in_1RgU1iEEXQJo9EEObpcu0isX",
|
||||
"livemode": false,
|
||||
"metadata": {
|
||||
"is_on_session_checkout": "true"
|
||||
},
|
||||
"next_pending_invoice_item_invoice": null,
|
||||
"on_behalf_of": null,
|
||||
"pause_collection": null,
|
||||
"payment_settings": {
|
||||
"payment_method_options": {
|
||||
"acss_debit": null,
|
||||
"bancontact": null,
|
||||
"card": {
|
||||
"network": null,
|
||||
"request_three_d_secure": "automatic"
|
||||
},
|
||||
"customer_balance": null,
|
||||
"konbini": null,
|
||||
"sepa_debit": null,
|
||||
"us_bank_account": null
|
||||
},
|
||||
"payment_method_types": null,
|
||||
"save_default_payment_method": "off"
|
||||
},
|
||||
"pending_invoice_item_interval": null,
|
||||
"pending_setup_intent": null,
|
||||
"pending_update": null,
|
||||
"plan": {
|
||||
"id": "price_1RfN2VEEXQJo9EEOzjPI2HGt",
|
||||
"object": "plan",
|
||||
"active": true,
|
||||
"amount": 400,
|
||||
"amount_decimal": "400",
|
||||
"billing_scheme": "per_unit",
|
||||
"created": 1751210467,
|
||||
"currency": "usd",
|
||||
"interval": "month",
|
||||
"interval_count": 1,
|
||||
"livemode": false,
|
||||
"metadata": {},
|
||||
"meter": null,
|
||||
"nickname": null,
|
||||
"product": "prod_SaY8TGjiPi5hWu",
|
||||
"tiers_mode": null,
|
||||
"transform_usage": null,
|
||||
"trial_period_days": null,
|
||||
"usage_type": "licensed"
|
||||
},
|
||||
"quantity": 1,
|
||||
"schedule": null,
|
||||
"start_date": 1751475654,
|
||||
"status": "canceled",
|
||||
"test_clock": null,
|
||||
"transfer_data": null,
|
||||
"trial_end": null,
|
||||
"trial_settings": {
|
||||
"end_behavior": {
|
||||
"missing_payment_method": "create_invoice"
|
||||
}
|
||||
},
|
||||
"payment_method_types": [
|
||||
"card",
|
||||
"link"
|
||||
],
|
||||
"payment_status": "paid",
|
||||
"permissions": null,
|
||||
"phone_number_collection": {
|
||||
"enabled": false
|
||||
},
|
||||
"recovered_from": null,
|
||||
"saved_payment_method_options": {
|
||||
"allow_redisplay_filters": [
|
||||
"always"
|
||||
],
|
||||
"payment_method_remove": "disabled",
|
||||
"payment_method_save": null
|
||||
},
|
||||
"setup_intent": null,
|
||||
"shipping_address_collection": null,
|
||||
"shipping_cost": null,
|
||||
"shipping_options": [],
|
||||
"status": "complete",
|
||||
"submit_type": null,
|
||||
"subscription": null,
|
||||
"success_url": "https://memeaigen.test/subscribe/success?session_id={CHECKOUT_SESSION_ID}",
|
||||
"total_details": {
|
||||
"amount_discount": 0,
|
||||
"amount_shipping": 0,
|
||||
"amount_tax": 0
|
||||
},
|
||||
"ui_mode": "hosted",
|
||||
"url": null,
|
||||
"wallet_options": null
|
||||
"trial_start": null
|
||||
}
|
||||
},
|
||||
"livemode": false,
|
||||
"pending_webhooks": 2,
|
||||
"request": {
|
||||
"id": null,
|
||||
"id": "req_cCXhmqNMHSgIwr",
|
||||
"idempotency_key": null
|
||||
},
|
||||
"type": "checkout.session.completed"
|
||||
"type": "customer.subscription.deleted"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,24 +108,58 @@ public static function generateMemeByCategory(Category $category)
|
||||
return $meme;
|
||||
}
|
||||
|
||||
public static function generateMemeOutputByCategory(Category $category)
|
||||
public static function generateMemeByKeyword($keyword)
|
||||
{
|
||||
$meme_output = self::generateMemeOutputByKeyword($keyword);
|
||||
|
||||
$meme = null;
|
||||
|
||||
if ($meme_output->success) {
|
||||
$meme = Meme::create([
|
||||
'type' => self::TYPE_SINGLE_CAPTION_MEME_BACKGROUND,
|
||||
'prompt' => $meme_output->prompt,
|
||||
'category_id' => null,
|
||||
'caption' => $meme_output->caption,
|
||||
'meme_keywords' => $meme_output->keywords,
|
||||
'background' => $meme_output->background,
|
||||
'keywords' => $meme_output->keywords,
|
||||
'is_system' => true,
|
||||
'status' => self::STATUS_PENDING,
|
||||
'primary_keyword_type' => $meme_output->primary_keyword_type,
|
||||
'action_keywords' => $meme_output->action_keywords,
|
||||
'emotion_keywords' => $meme_output->emotion_keywords,
|
||||
'misc_keywords' => $meme_output->misc_keywords,
|
||||
]);
|
||||
|
||||
$meme->attachTags($meme_output->keywords, 'meme');
|
||||
}
|
||||
|
||||
if (! is_null($meme) && $meme->status == self::STATUS_PENDING) {
|
||||
// populate meme_media_id
|
||||
$meme->meme_media_id = self::getSuitableMemeMedia($meme)->id;
|
||||
$meme->background_media_id = self::generateBackgroundMediaWithRunware($meme_output->background)->id;
|
||||
|
||||
if (
|
||||
// !is_null($meme->meme_media_id) &&
|
||||
! is_null($meme->background_media_id)
|
||||
) {
|
||||
$meme->status = self::STATUS_COMPLETED;
|
||||
}
|
||||
|
||||
$meme->save();
|
||||
}
|
||||
|
||||
return $meme;
|
||||
}
|
||||
|
||||
|
||||
public static function generateMemeOutputByKeyword($keyword, $category = null)
|
||||
{
|
||||
|
||||
$retries = 3;
|
||||
$attempt = 0;
|
||||
|
||||
$random_keyword = Str::lower($category->name);
|
||||
|
||||
if (! is_null($category->parent_id)) {
|
||||
$random_keyword = $category->parent->name.' - '.$random_keyword;
|
||||
}
|
||||
|
||||
if (! is_null($category->meme_angles)) {
|
||||
$random_keyword .= ' - '.collect($category->meme_angles)->random();
|
||||
} elseif (! is_null($category->keywords)) {
|
||||
$random_keyword .= ' - '.collect($category->keywords)->random();
|
||||
}
|
||||
|
||||
$prompt = "Write me 1 meme about {$random_keyword}";
|
||||
$prompt = "Write me 1 meme about {$keyword}";
|
||||
|
||||
// RETRY MECHANISM START
|
||||
do {
|
||||
@@ -176,6 +210,24 @@ public static function generateMemeOutputByCategory(Category $category)
|
||||
return $meme_output;
|
||||
}
|
||||
|
||||
public static function generateMemeOutputByCategory(Category $category)
|
||||
{
|
||||
|
||||
$random_keyword = Str::lower($category->name);
|
||||
|
||||
if (! is_null($category->parent_id)) {
|
||||
$random_keyword = $category->parent->name . ' - ' . $random_keyword;
|
||||
}
|
||||
|
||||
if (! is_null($category->meme_angles)) {
|
||||
$random_keyword .= ' - ' . collect($category->meme_angles)->random();
|
||||
} elseif (! is_null($category->keywords)) {
|
||||
$random_keyword .= ' - ' . collect($category->keywords)->random();
|
||||
}
|
||||
|
||||
return self::generateMemeOutputByKeyword($random_keyword, $category);
|
||||
}
|
||||
|
||||
public static function generateBackgroundMediaWithRunware($prompt)
|
||||
{
|
||||
$media_width = 1024;
|
||||
|
||||
@@ -2,12 +2,74 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Helpers\FirstParty\Credits\CreditsService;
|
||||
use App\Helpers\FirstParty\Meme\MemeGenerator;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Category;
|
||||
use Auth;
|
||||
|
||||
class UserAIController extends Controller
|
||||
{
|
||||
public function generateMeme(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
|
||||
if (!CreditsService::canSpend($user->id, 2)) {
|
||||
return response()->json([
|
||||
'error' => [
|
||||
'message' => 'You do not have enough credits to generate a meme. Please purchase credits from the Store.',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
CreditsService::spend($user->id, 2);
|
||||
|
||||
$meme = MemeGenerator::generateMemeByKeyword($request->prompt);
|
||||
|
||||
$meme_media = MemeGenerator::getSuitableMemeMedia($meme, 2);
|
||||
|
||||
return response()->json([
|
||||
'success' => [
|
||||
'data' => [
|
||||
'generate' => [
|
||||
'info' => $meme,
|
||||
'caption' => $meme->caption,
|
||||
'meme' => $meme_media,
|
||||
'background' => $meme->background_media,
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function aiHints()
|
||||
{
|
||||
// TODO: Take 5 Category where meme_angles is not null, and return a random one from the array
|
||||
$categories = Category::whereNotNull('keywords')
|
||||
->inRandomOrder()
|
||||
->take(8)
|
||||
->get();
|
||||
|
||||
if ($categories->isEmpty()) {
|
||||
return response()->json([
|
||||
'success' => [
|
||||
'data' => [
|
||||
'keywords' => []
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
$keywords = $categories->map(function ($category) {
|
||||
return collect($category->keywords)->random();
|
||||
})->toArray();
|
||||
|
||||
return response()->json([
|
||||
'success' => [
|
||||
'data' => [
|
||||
'keywords' => $keywords
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
/>
|
||||
|
||||
{/* 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>
|
||||
)}
|
||||
<div className={cn('space-y-2', !prompt.trim() && 'invisible')}>
|
||||
<Button
|
||||
onClick={handleSend}
|
||||
className={cn('w-full rounded-full', !prompt.trim() && 'invisible')}
|
||||
className={cn('w-full rounded-full')}
|
||||
size="lg"
|
||||
variant="outline"
|
||||
disabled={!prompt.trim()}
|
||||
disabled={!prompt.trim() || isGeneratingMeme}
|
||||
>
|
||||
{isGeneratingMeme ? (
|
||||
<Spinner className="text-primary h-4 w-4" />
|
||||
) : (
|
||||
<>
|
||||
Generate Meme
|
||||
<div className="flex items-center gap-1">
|
||||
<CoinIcon></CoinIcon> 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 });
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
Route::post('/premium-export/request', [UserExportController::class, 'premiumExportRequest'])->name('api.user.premium_export.request');
|
||||
|
||||
Route::post('/premium-export/complete', [UserExportController::class, 'premiumExportComplete'])->name('api.user.premium_export.complete');
|
||||
|
||||
Route::post('generate_meme', [UserAIController::class, 'generateMeme'])->name('api.user.generate_meme');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user