diff --git a/app/Http/Controllers/FrontMemeController.php b/app/Http/Controllers/FrontMemeController.php index 3c7cc56..d45dfb2 100644 --- a/app/Http/Controllers/FrontMemeController.php +++ b/app/Http/Controllers/FrontMemeController.php @@ -90,6 +90,22 @@ public function show(string $slug): Response ->limit(6) ->get(); + // If we have less than 6 related memes, fill up with random ones + if ($relatedMemes->count() < 6) { + $excludeIds = $relatedMemes->pluck('id')->push($meme->id)->toArray(); + $needed = 6 - $relatedMemes->count(); + + $randomMemes = MemeMedia::where('is_enabled', true) + ->whereNotIn('id', $excludeIds) + ->inRandomOrder() + ->limit($needed) + ->get(); + + $relatedMemes = $relatedMemes->merge($randomMemes); + } + + //dd($meme); + return Inertia::render('memes/show', [ 'meme' => $meme, 'relatedMemes' => $relatedMemes, diff --git a/app/Models/MemeMedia.php b/app/Models/MemeMedia.php index f1f92a4..49fddc2 100644 --- a/app/Models/MemeMedia.php +++ b/app/Models/MemeMedia.php @@ -80,7 +80,7 @@ class MemeMedia extends Model 'type', 'sub_type', 'original_id', - 'description', + //'description', 'mov_uuid', 'webm_uuid', 'gif_uuid', @@ -98,7 +98,7 @@ class MemeMedia extends Model protected function ids(): Attribute { return Attribute::make( - get: fn ($value, $attributes) => hashids_encode($attributes['id']), + get: fn($value, $attributes) => hashids_encode($attributes['id']), ); } } diff --git a/resources/js/components/custom/meme-card.tsx b/resources/js/components/custom/meme-card.tsx new file mode 100644 index 0000000..bdbb052 --- /dev/null +++ b/resources/js/components/custom/meme-card.tsx @@ -0,0 +1,89 @@ +import { Badge } from '@/components/ui/badge'; +import { Button } from '@/components/ui/button'; +import { Card } from '@/components/ui/card'; +import { KeywordBadge } from '@/components/ui/keyword-badge'; +import { Link } from '@inertiajs/react'; +import { Edit } from 'lucide-react'; +import { route } from 'ziggy-js'; + +interface MemeMedia { + ids: string; + name: string; + description: string; + keywords: string[]; + action_keywords: string[]; + emotion_keywords: string[]; + misc_keywords: string[]; + mov_url: string; + webm_url: string; + gif_url: string; + webp_url: string; + slug: string; +} + +interface MemeCardProps { + meme: MemeMedia; + showButton?: boolean; + showKeywords?: boolean; + className?: string; +} + +export function MemeCard({ meme, showButton = true, showKeywords = true, className = '' }: MemeCardProps) { + return ( + +
+ {meme.name} + + + +
+
+

{meme.name}

+ {showKeywords && ( +
+ {meme.keywords?.slice(0, 6).map((keyword, index) => ( + + ))} + {meme.keywords && meme.keywords.length > 6 && ( + + +{meme.keywords.length - 6} more + + )} +
+ )} + {showButton && ( +
+ + + +
+ )} +
+
+ ); +} \ No newline at end of file diff --git a/resources/js/modules/editor/editor.jsx b/resources/js/modules/editor/editor.jsx index 4ba0880..407be4b 100644 --- a/resources/js/modules/editor/editor.jsx +++ b/resources/js/modules/editor/editor.jsx @@ -173,7 +173,7 @@ const Editor = () => { return ( <> -
+
diff --git a/resources/js/modules/editor/partials/editor-header.jsx b/resources/js/modules/editor/partials/editor-header.jsx index bd483bf..8130514 100644 --- a/resources/js/modules/editor/partials/editor-header.jsx +++ b/resources/js/modules/editor/partials/editor-header.jsx @@ -1,4 +1,3 @@ -import BrandLogo from '@/pages/home/partials/BrandLogo'; import { useMitt } from '@/plugins/MittContext'; import useLocalSettingsStore from '@/stores/localSettingsStore'; @@ -12,7 +11,8 @@ const EditorHeader = ({ className = '', onNavClick = () => {}, isNavActive = fal }; return ( - + <> + // //
// - -
-
- + ))}
diff --git a/resources/js/pages/memes/show.tsx b/resources/js/pages/memes/show.tsx index 68fa675..2f05500 100644 --- a/resources/js/pages/memes/show.tsx +++ b/resources/js/pages/memes/show.tsx @@ -1,174 +1,148 @@ -import { Head } from '@inertiajs/react'; -import { Button } from '@/components/ui/button'; +import { MemeCard } from '@/components/custom/meme-card'; +import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from '@/components/ui/breadcrumb'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; -import { Badge } from '@/components/ui/badge'; -import { ArrowLeft, Play, Download, Share2 } from 'lucide-react'; +import { KeywordBadge } from '@/components/ui/keyword-badge'; +import { Spinner } from '@/components/ui/spinner'; +import Footer from '@/pages/home/partials/Footer'; import { Link } from '@inertiajs/react'; +import { useEffect, useState } from 'react'; import { route } from 'ziggy-js'; +import BrandLogo from '../home/partials/BrandLogo'; interface MemeMedia { - ids: string; - name: string; - description: string; - keywords: string[]; - action_keywords: string[]; - emotion_keywords: string[]; - misc_keywords: string[]; - mov_url: string; - webm_url: string; - gif_url: string; - webp_url: string; - slug: string; + ids: string; + name: string; + description: string; + keywords: string[]; + action_keywords: string[]; + emotion_keywords: string[]; + misc_keywords: string[]; + mov_url: string; + webm_url: string; + gif_url: string; + webp_url: string; + slug: string; } interface Props { - meme: MemeMedia; - relatedMemes: MemeMedia[]; + meme: MemeMedia; + relatedMemes: MemeMedia[]; } export default function MemeShow({ meme, relatedMemes }: Props) { - const allKeywords = [ - ...(meme.keywords || []), - ...(meme.action_keywords || []), - ...(meme.emotion_keywords || []), - ...(meme.misc_keywords || []) - ].filter(Boolean); + const [isClient, setIsClient] = useState(false); + const [Editor, setEditor] = useState(null); - return ( - <> - - - - - - - - - - - - - - -
-
- {/* Header */} -
- - - -
-

{meme.name}

-

{meme.description}

-
-
+ useEffect(() => { + setIsClient(true); + // Dynamically import Editor only on client-side to avoid SSR issues with Konva + if (typeof window !== 'undefined') { + import('@/modules/editor/editor.jsx').then((module) => { + setEditor(() => module.default); + }); + } + }, []); -
- {/* Main Content */} -
- - - {/* Video Preview */} -
-
+ const allKeywords = [ + ...(meme.keywords || []), + ...(meme.action_keywords || []), + ...(meme.emotion_keywords || []), + ...(meme.misc_keywords || []), + ].filter(Boolean); - {/* Action Buttons */} -
- - - -
-
-
-
+ return ( + <> +
+
+ - {/* Sidebar */} -
- {/* Keywords */} - - - Keywords - - Related keywords for this meme template - - - -
- {meme.action_keywords?.map((keyword, index) => ( - - {keyword} - - ))} - {meme.emotion_keywords?.map((keyword, index) => ( - - {keyword} - - ))} - {meme.misc_keywords?.map((keyword, index) => ( - - {keyword} - - ))} -
-
-
+ {/* Breadcrumbs */} + + + + + Home + + + + + + Meme Library + + + + + {meme.name} + + + +
- {/* Related Memes */} - {relatedMemes.length > 0 && ( - - - Related Memes - - Similar meme templates you might like - - - -
- {relatedMemes.map((related) => ( - -
- {related.name} -
-

- {related.name} -

- - ))} +
+
+

{meme.name} Meme

+

+ Use our video meme editor to customise your {meme.name} meme and save to a video in minutes! +

- - - )} + {isClient && Editor ? ( + + ) : ( +
+
+ +
+ Loading meme video editor... +
+
+
+ )} +
+
+
+ {/* Keywords */} + + + Know Your Meme + Learn more about {meme.name} meme! + + + {meme.description && ( +
+

{meme.description}

+
+ )} +
+ {allKeywords.map((keyword, index) => ( + + ))} +
+
+
+
+ + {/* Related Memes */} +
+ {relatedMemes.length > 0 && ( + + + Related Memes + Similar meme templates you might like + + +
+ {relatedMemes.map((related) => ( + + ))} +
+
+
+ )} +
+
+
-
-
-
- - ); -} \ No newline at end of file + + ); +}