From 8a732b3c313b505b799944476a53e0b803a0f240 Mon Sep 17 00:00:00 2001 From: ct Date: Sat, 14 Jun 2025 10:12:19 +0800 Subject: [PATCH] Update --- app/Http/Controllers/FrontMediaController.php | 18 ++ app/Models/BackgroundMedia.php | 25 +++ app/Models/MemeMedia.php | 11 ++ resources/js/modules/editor/editor.tsx | 7 + .../modules/editor/partials/edit-sidebar.tsx | 186 ++++++++++++------ resources/js/stores/MediaStore.ts | 43 ++++ resources/js/ziggy.js | 2 +- routes/api.php | 2 + 8 files changed, 228 insertions(+), 66 deletions(-) diff --git a/app/Http/Controllers/FrontMediaController.php b/app/Http/Controllers/FrontMediaController.php index c36437e..ac8c336 100644 --- a/app/Http/Controllers/FrontMediaController.php +++ b/app/Http/Controllers/FrontMediaController.php @@ -8,6 +8,24 @@ class FrontMediaController extends Controller { + public function init(Request $request) + { + + $meme = MemeMedia::where('type', 'video')->where('sub_type', 'overlay')->take(1)->inRandomOrder()->first(); + $background = BackgroundMedia::where('status', 'completed')->take(1)->inRandomOrder()->first(); + + return response()->json([ + 'success' => [ + 'data' => [ + 'init' => [ + 'meme' => $meme, + 'background' => $background, + ], + ], + ], + ]); + } + public function memes(Request $request) { $memes = MemeMedia::where('type', 'video')->where('sub_type', 'overlay')->take('30')->inRandomOrder()->get(); diff --git a/app/Models/BackgroundMedia.php b/app/Models/BackgroundMedia.php index 75fe36d..45a6c34 100644 --- a/app/Models/BackgroundMedia.php +++ b/app/Models/BackgroundMedia.php @@ -7,6 +7,7 @@ namespace App\Models; use Carbon\Carbon; +use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Pgvector\Laravel\Vector; @@ -46,4 +47,28 @@ class BackgroundMedia extends Model 'embedding', 'prompt', ]; + + protected $hidden = [ + 'id', + 'created_at', + 'updated_at', + 'deleted_at', + 'list_type', + 'area', + 'location_name', + 'status', + 'media_uuid', + 'embedding', + ]; + + protected $appends = [ + 'ids', + ]; + + protected function ids(): Attribute + { + return Attribute::make( + get: fn ($value, $attributes) => hashids_encode($attributes['id']), + ); + } } diff --git a/app/Models/MemeMedia.php b/app/Models/MemeMedia.php index 6e8a723..aa4933b 100644 --- a/app/Models/MemeMedia.php +++ b/app/Models/MemeMedia.php @@ -64,10 +64,21 @@ class MemeMedia extends Model 'created_at', 'updated_at', 'deleted_at', + 'type', + 'sub_type', + 'original_id', + 'description', 'mov_uuid', 'webm_uuid', 'gif_uuid', 'webp_uuid', + // 'mov_url', + // 'webm_url', + 'embedding', + ]; + + protected $appends = [ + 'ids', ]; protected function ids(): Attribute diff --git a/resources/js/modules/editor/editor.tsx b/resources/js/modules/editor/editor.tsx index 659838c..d5671be 100644 --- a/resources/js/modules/editor/editor.tsx +++ b/resources/js/modules/editor/editor.tsx @@ -1,6 +1,7 @@ import { useEffect, useLayoutEffect, useState } from 'react'; import useLocalSettingsStore from '@/stores/localSettingsStore'; +import useMediaStore from '@/stores/MediaStore'; import { Volume2Icon, VolumeOffIcon } from 'lucide-react'; import EditNavSidebar from './partials/edit-nav-sidebar'; import EditSidebar from './partials/edit-sidebar'; @@ -100,6 +101,8 @@ const useResponsiveDimensions = () => { }; const Editor = () => { + const { init } = useMediaStore(); + const { getSetting } = useLocalSettingsStore(); const [isEditNavSidebarOpen, setIsEditNavSidebarOpen] = useState(false); @@ -108,6 +111,10 @@ const Editor = () => { const { maxWidth, responsiveWidth } = useResponsiveDimensions(); const isBelowMinWidth = useViewportDetection(320); + useEffect(() => { + init(); + }, []); + const handleEditNavClick = () => { setIsEditNavSidebarOpen(!isEditNavSidebarOpen); }; diff --git a/resources/js/modules/editor/partials/edit-sidebar.tsx b/resources/js/modules/editor/partials/edit-sidebar.tsx index 2749792..3a2ada6 100644 --- a/resources/js/modules/editor/partials/edit-sidebar.tsx +++ b/resources/js/modules/editor/partials/edit-sidebar.tsx @@ -1,8 +1,10 @@ +import { Button } from '@/components/ui/button'; import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet'; import { Spinner } from '@/components/ui/spinner'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import useMediaStore from '@/stores/MediaStore'; import { Edit3 } from 'lucide-react'; -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; interface EditSidebarProps { isOpen: boolean; @@ -10,25 +12,55 @@ interface EditSidebarProps { } export default function EditSidebar({ isOpen, onClose }: EditSidebarProps) { - const { memes, backgrounds, isFetchingMemes, isFetchingBackgrounds, currentTab, setCurrentTab, fetchMemes, fetchBackgrounds } = useMediaStore(); + const { + memes, + backgrounds, + isFetchingMemes, + isFetchingBackgrounds, + selectedMeme, + selectedBackground, + fetchMemes, + fetchBackgrounds, + selectMeme, + selectBackground, + clearSelectedMeme, + clearSelectedBackground, + } = useMediaStore(); - // Fetch media based on currentTab when it changes and data isn't fetched yet + // Track the current active tab + const [activeTab, setActiveTab] = useState('backgrounds'); + + // Fetch data when sidebar opens for the current active tab useEffect(() => { - if (currentTab === 'memes' && memes.length === 0 && !isFetchingMemes) { + if (isOpen) { + if (activeTab === 'memes' && memes.length === 0 && !isFetchingMemes) { + fetchMemes(); + } else if (activeTab === 'backgrounds' && backgrounds.length === 0 && !isFetchingBackgrounds) { + fetchBackgrounds(); + } + } + }, [isOpen, activeTab, memes.length, backgrounds.length, isFetchingMemes, isFetchingBackgrounds]); + + const handleTabChange = (value: string) => { + setActiveTab(value); + if (value === 'memes' && memes.length === 0 && !isFetchingMemes) { fetchMemes(); - } else if (currentTab === 'backgrounds' && backgrounds.length === 0 && !isFetchingBackgrounds) { + } else if (value === 'backgrounds' && backgrounds.length === 0 && !isFetchingBackgrounds) { fetchBackgrounds(); } - }, [currentTab]); + }; - // Determine display states - const isFetching = currentTab === 'memes' ? isFetchingMemes : isFetchingBackgrounds; - const media = currentTab === 'memes' ? memes : backgrounds; - const mediaType = currentTab === 'memes' ? 'memes' : 'backgrounds'; + const handleMemeSelect = (meme) => { + selectMeme(meme); + }; + + const handleBackgroundSelect = (background) => { + selectBackground(background); + }; return ( !open && onClose()}> - + @@ -36,64 +68,88 @@ export default function EditSidebar({ isOpen, onClose }: EditSidebarProps) { -
- {/* Background and Meme Selection */} -
-
setCurrentTab('backgrounds')} - > -
- Gaming background -
- Background + {/* Currently Selected Items */} +
+
+ {/* Selected Background */} +
+ {selectedBackground ? ( +
+ Selected Background +
+ ) : ( +
+ No background +
+ )} +

Background

-
setCurrentTab('memes')} - > -
- Meme character -
- Meme + + {/* Selected Meme */} +
+ {selectedMeme ? ( +
+ Selected Meme +
+ ) : ( +
+ No meme +
+ )} +

Meme Overlay

- - {/* Media Grid */} - {isFetching && } - {!isFetching && media.length === 0 &&
No {mediaType} available.
} - {!isFetching && media.length > 0 && ( -
- {media.map((item, index) => ( -
- {currentTab -
- ))} -
- )}
+ + + + Meme + + Background + + + + {isFetchingBackgrounds && } + {!isFetchingBackgrounds && backgrounds.length === 0 &&
No backgrounds available.
} + {!isFetchingBackgrounds && backgrounds.length > 0 && ( +
+ {backgrounds.map((item, index) => ( + + ))} +
+ )} +
+ + + {isFetchingMemes && } + {!isFetchingMemes && memes.length === 0 &&
No memes available.
} + {!isFetchingMemes && memes.length > 0 && ( +
+ {memes.map((item, index) => ( + + ))} +
+ )} +
+
); diff --git a/resources/js/stores/MediaStore.ts b/resources/js/stores/MediaStore.ts index 2ad4ac2..cf088ed 100644 --- a/resources/js/stores/MediaStore.ts +++ b/resources/js/stores/MediaStore.ts @@ -15,10 +15,51 @@ const useMediaStore = create( isFetchingMemes: false, isFetchingBackgrounds: false, + // Selected items + selectedMeme: null, + selectedBackground: null, + setCurrentTab: (tab) => { set({ currentTab: tab }); }, + // Selection actions + selectMeme: (meme) => { + set({ selectedMeme: meme }); + }, + + selectBackground: (background) => { + set({ selectedBackground: background }); + }, + + // Clear selections + clearSelectedMeme: () => { + set({ selectedMeme: null }); + }, + + clearSelectedBackground: () => { + set({ selectedBackground: null }); + }, + + init: async () => { + try { + const response = await axiosInstance.post(route('api.app.init')); + + if (response?.data?.success?.data?.init) { + set({ + selectedMeme: response.data.success.data.init.meme, + selectedBackground: response.data.success.data.init.background, + }); + } else { + throw 'Invalid API response'; + } + } catch (error) { + console.error('Error fetching init:', error); + + throw error; + } + }, + // Fetch memes (overlays) fetchMemes: async () => { set({ isFetchingMemes: true }); @@ -77,6 +118,8 @@ const useMediaStore = create( backgrounds: [], isFetchingMemes: false, isFetchingBackgrounds: false, + selectedMeme: null, + selectedBackground: null, }); }, })), diff --git a/resources/js/ziggy.js b/resources/js/ziggy.js index 4add60f..8b350a2 100644 --- a/resources/js/ziggy.js +++ b/resources/js/ziggy.js @@ -1,4 +1,4 @@ -const Ziggy = {"url":"https:\/\/memeaigen.com","port":null,"defaults":{},"routes":{"horizon.stats.index":{"uri":"horizon\/api\/stats","methods":["GET","HEAD"]},"horizon.workload.index":{"uri":"horizon\/api\/workload","methods":["GET","HEAD"]},"horizon.masters.index":{"uri":"horizon\/api\/masters","methods":["GET","HEAD"]},"horizon.monitoring.index":{"uri":"horizon\/api\/monitoring","methods":["GET","HEAD"]},"horizon.monitoring.store":{"uri":"horizon\/api\/monitoring","methods":["POST"]},"horizon.monitoring-tag.paginate":{"uri":"horizon\/api\/monitoring\/{tag}","methods":["GET","HEAD"],"parameters":["tag"]},"horizon.monitoring-tag.destroy":{"uri":"horizon\/api\/monitoring\/{tag}","methods":["DELETE"],"wheres":{"tag":".*"},"parameters":["tag"]},"horizon.jobs-metrics.index":{"uri":"horizon\/api\/metrics\/jobs","methods":["GET","HEAD"]},"horizon.jobs-metrics.show":{"uri":"horizon\/api\/metrics\/jobs\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"horizon.queues-metrics.index":{"uri":"horizon\/api\/metrics\/queues","methods":["GET","HEAD"]},"horizon.queues-metrics.show":{"uri":"horizon\/api\/metrics\/queues\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"horizon.jobs-batches.index":{"uri":"horizon\/api\/batches","methods":["GET","HEAD"]},"horizon.jobs-batches.show":{"uri":"horizon\/api\/batches\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"horizon.jobs-batches.retry":{"uri":"horizon\/api\/batches\/retry\/{id}","methods":["POST"],"parameters":["id"]},"horizon.pending-jobs.index":{"uri":"horizon\/api\/jobs\/pending","methods":["GET","HEAD"]},"horizon.completed-jobs.index":{"uri":"horizon\/api\/jobs\/completed","methods":["GET","HEAD"]},"horizon.silenced-jobs.index":{"uri":"horizon\/api\/jobs\/silenced","methods":["GET","HEAD"]},"horizon.failed-jobs.index":{"uri":"horizon\/api\/jobs\/failed","methods":["GET","HEAD"]},"horizon.failed-jobs.show":{"uri":"horizon\/api\/jobs\/failed\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"horizon.retry-jobs.show":{"uri":"horizon\/api\/jobs\/retry\/{id}","methods":["POST"],"parameters":["id"]},"horizon.jobs.show":{"uri":"horizon\/api\/jobs\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"horizon.index":{"uri":"horizon\/{view?}","methods":["GET","HEAD"],"wheres":{"view":"(.*)"},"parameters":["view"]},"sanctum.csrf-cookie":{"uri":"sanctum\/csrf-cookie","methods":["GET","HEAD"]},"api.app.memes":{"uri":"api\/app\/memes","methods":["POST"]},"api.app.background":{"uri":"api\/app\/background","methods":["POST"]},"dashboard":{"uri":"dashboard","methods":["GET","HEAD"]},"admin.dashboard":{"uri":"admin","methods":["GET","HEAD"]},"admin.background-generation":{"uri":"admin\/background-generation","methods":["GET","HEAD"]},"admin.background-generation.generate":{"uri":"admin\/background-generation\/generate","methods":["POST"]},"admin.background-generation.save":{"uri":"admin\/background-generation\/save","methods":["POST"]},"admin.background-generation.delete":{"uri":"admin\/background-generation\/delete\/{id}","methods":["POST"],"parameters":["id"]},"profile.edit":{"uri":"settings\/profile","methods":["GET","HEAD"]},"profile.update":{"uri":"settings\/profile","methods":["PATCH"]},"profile.destroy":{"uri":"settings\/profile","methods":["DELETE"]},"password.edit":{"uri":"settings\/password","methods":["GET","HEAD"]},"password.update":{"uri":"settings\/password","methods":["PUT"]},"appearance":{"uri":"settings\/appearance","methods":["GET","HEAD"]},"register":{"uri":"register","methods":["GET","HEAD"]},"login":{"uri":"login","methods":["GET","HEAD"]},"password.request":{"uri":"forgot-password","methods":["GET","HEAD"]},"password.email":{"uri":"forgot-password","methods":["POST"]},"password.reset":{"uri":"reset-password\/{token}","methods":["GET","HEAD"],"parameters":["token"]},"password.store":{"uri":"reset-password","methods":["POST"]},"verification.notice":{"uri":"verify-email","methods":["GET","HEAD"]},"verification.verify":{"uri":"verify-email\/{id}\/{hash}","methods":["GET","HEAD"],"parameters":["id","hash"]},"verification.send":{"uri":"email\/verification-notification","methods":["POST"]},"password.confirm":{"uri":"confirm-password","methods":["GET","HEAD"]},"logout":{"uri":"logout","methods":["POST"]},"home":{"uri":"\/","methods":["GET","HEAD"]},"test":{"uri":"tests","methods":["GET","HEAD"]},"storage.local":{"uri":"storage\/{path}","methods":["GET","HEAD"],"wheres":{"path":".*"},"parameters":["path"]}}}; +const Ziggy = {"url":"https:\/\/memeaigen.test","port":null,"defaults":{},"routes":{"horizon.stats.index":{"uri":"horizon\/api\/stats","methods":["GET","HEAD"]},"horizon.workload.index":{"uri":"horizon\/api\/workload","methods":["GET","HEAD"]},"horizon.masters.index":{"uri":"horizon\/api\/masters","methods":["GET","HEAD"]},"horizon.monitoring.index":{"uri":"horizon\/api\/monitoring","methods":["GET","HEAD"]},"horizon.monitoring.store":{"uri":"horizon\/api\/monitoring","methods":["POST"]},"horizon.monitoring-tag.paginate":{"uri":"horizon\/api\/monitoring\/{tag}","methods":["GET","HEAD"],"parameters":["tag"]},"horizon.monitoring-tag.destroy":{"uri":"horizon\/api\/monitoring\/{tag}","methods":["DELETE"],"wheres":{"tag":".*"},"parameters":["tag"]},"horizon.jobs-metrics.index":{"uri":"horizon\/api\/metrics\/jobs","methods":["GET","HEAD"]},"horizon.jobs-metrics.show":{"uri":"horizon\/api\/metrics\/jobs\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"horizon.queues-metrics.index":{"uri":"horizon\/api\/metrics\/queues","methods":["GET","HEAD"]},"horizon.queues-metrics.show":{"uri":"horizon\/api\/metrics\/queues\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"horizon.jobs-batches.index":{"uri":"horizon\/api\/batches","methods":["GET","HEAD"]},"horizon.jobs-batches.show":{"uri":"horizon\/api\/batches\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"horizon.jobs-batches.retry":{"uri":"horizon\/api\/batches\/retry\/{id}","methods":["POST"],"parameters":["id"]},"horizon.pending-jobs.index":{"uri":"horizon\/api\/jobs\/pending","methods":["GET","HEAD"]},"horizon.completed-jobs.index":{"uri":"horizon\/api\/jobs\/completed","methods":["GET","HEAD"]},"horizon.silenced-jobs.index":{"uri":"horizon\/api\/jobs\/silenced","methods":["GET","HEAD"]},"horizon.failed-jobs.index":{"uri":"horizon\/api\/jobs\/failed","methods":["GET","HEAD"]},"horizon.failed-jobs.show":{"uri":"horizon\/api\/jobs\/failed\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"horizon.retry-jobs.show":{"uri":"horizon\/api\/jobs\/retry\/{id}","methods":["POST"],"parameters":["id"]},"horizon.jobs.show":{"uri":"horizon\/api\/jobs\/{id}","methods":["GET","HEAD"],"parameters":["id"]},"horizon.index":{"uri":"horizon\/{view?}","methods":["GET","HEAD"],"wheres":{"view":"(.*)"},"parameters":["view"]},"sanctum.csrf-cookie":{"uri":"sanctum\/csrf-cookie","methods":["GET","HEAD"]},"api.app.init":{"uri":"api\/app\/init","methods":["POST"]},"api.app.memes":{"uri":"api\/app\/memes","methods":["POST"]},"api.app.background":{"uri":"api\/app\/background","methods":["POST"]},"dashboard":{"uri":"dashboard","methods":["GET","HEAD"]},"admin.dashboard":{"uri":"admin","methods":["GET","HEAD"]},"admin.background-generation":{"uri":"admin\/background-generation","methods":["GET","HEAD"]},"admin.background-generation.generate":{"uri":"admin\/background-generation\/generate","methods":["POST"]},"admin.background-generation.save":{"uri":"admin\/background-generation\/save","methods":["POST"]},"admin.background-generation.delete":{"uri":"admin\/background-generation\/delete\/{id}","methods":["POST"],"parameters":["id"]},"profile.edit":{"uri":"settings\/profile","methods":["GET","HEAD"]},"profile.update":{"uri":"settings\/profile","methods":["PATCH"]},"profile.destroy":{"uri":"settings\/profile","methods":["DELETE"]},"password.edit":{"uri":"settings\/password","methods":["GET","HEAD"]},"password.update":{"uri":"settings\/password","methods":["PUT"]},"appearance":{"uri":"settings\/appearance","methods":["GET","HEAD"]},"register":{"uri":"register","methods":["GET","HEAD"]},"login":{"uri":"login","methods":["GET","HEAD"]},"password.request":{"uri":"forgot-password","methods":["GET","HEAD"]},"password.email":{"uri":"forgot-password","methods":["POST"]},"password.reset":{"uri":"reset-password\/{token}","methods":["GET","HEAD"],"parameters":["token"]},"password.store":{"uri":"reset-password","methods":["POST"]},"verification.notice":{"uri":"verify-email","methods":["GET","HEAD"]},"verification.verify":{"uri":"verify-email\/{id}\/{hash}","methods":["GET","HEAD"],"parameters":["id","hash"]},"verification.send":{"uri":"email\/verification-notification","methods":["POST"]},"password.confirm":{"uri":"confirm-password","methods":["GET","HEAD"]},"logout":{"uri":"logout","methods":["POST"]},"home":{"uri":"\/","methods":["GET","HEAD"]},"test":{"uri":"tests","methods":["GET","HEAD"]},"storage.local":{"uri":"storage\/{path}","methods":["GET","HEAD"],"wheres":{"path":".*"},"parameters":["path"]}}}; if (typeof window !== 'undefined' && typeof window.Ziggy !== 'undefined') { Object.assign(Ziggy.routes, window.Ziggy.routes); } diff --git a/routes/api.php b/routes/api.php index a050dc2..d23e1ab 100644 --- a/routes/api.php +++ b/routes/api.php @@ -23,6 +23,8 @@ Route::group(['prefix' => 'app'], function () { + Route::post('init', [FrontMediaController::class, 'init'])->name('api.app.init'); + Route::post('memes', [FrontMediaController::class, 'memes'])->name('api.app.memes'); Route::post('background', [FrontMediaController::class, 'background'])->name('api.app.background');