Update
This commit is contained in:
@@ -432,3 +432,17 @@ @keyframes shine {
|
||||
.shiny-text.disabled {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
/* Shimmer animation for skeleton loading */
|
||||
@keyframes shimmer {
|
||||
0% {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
100% {
|
||||
background-position: 200% 0;
|
||||
}
|
||||
}
|
||||
|
||||
@utility animate-shimmer {
|
||||
animation: shimmer 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { GridSkeleton } from '@/components/ui/grid-skeleton';
|
||||
import { Input } from '@/components/ui/input';
|
||||
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 { cn } from '@/lib/utils';
|
||||
import useMediaStore from '@/stores/MediaStore';
|
||||
import { Edit3 } from 'lucide-react';
|
||||
import { Edit3, Search, X } from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export default function EditSidebar({ isOpen, onClose }) {
|
||||
@@ -17,6 +18,8 @@ export default function EditSidebar({ isOpen, onClose }) {
|
||||
selectedBackground,
|
||||
fetchMemes,
|
||||
fetchBackgrounds,
|
||||
searchMemes,
|
||||
searchBackgrounds,
|
||||
selectMeme,
|
||||
selectBackground,
|
||||
clearSelectedMeme,
|
||||
@@ -25,24 +28,120 @@ export default function EditSidebar({ isOpen, onClose }) {
|
||||
|
||||
// Track the current active tab
|
||||
const [activeTab, setActiveTab] = useState('memes');
|
||||
|
||||
// Track search queries
|
||||
const [searchQueries, setSearchQueries] = useState({
|
||||
memes: '',
|
||||
backgrounds: ''
|
||||
});
|
||||
|
||||
// Track if data has been loaded for each tab (to prevent infinite loading on empty results)
|
||||
const [dataLoaded, setDataLoaded] = useState({
|
||||
memes: false,
|
||||
backgrounds: false
|
||||
});
|
||||
|
||||
// Fetch data when sidebar opens for the current active tab
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
if (activeTab === 'memes' && memes.length === 0 && !isFetchingMemes) {
|
||||
fetchMemes();
|
||||
} else if (activeTab === 'backgrounds' && backgrounds.length === 0 && !isFetchingBackgrounds) {
|
||||
fetchBackgrounds();
|
||||
if (activeTab === 'memes' && !dataLoaded.memes && !isFetchingMemes) {
|
||||
const query = searchQueries.memes;
|
||||
if (query) {
|
||||
searchMemes(query).finally(() => {
|
||||
setDataLoaded(prev => ({ ...prev, memes: true }));
|
||||
});
|
||||
} else {
|
||||
fetchMemes().finally(() => {
|
||||
setDataLoaded(prev => ({ ...prev, memes: true }));
|
||||
});
|
||||
}
|
||||
} else if (activeTab === 'backgrounds' && !dataLoaded.backgrounds && !isFetchingBackgrounds) {
|
||||
const query = searchQueries.backgrounds;
|
||||
if (query) {
|
||||
searchBackgrounds(query).finally(() => {
|
||||
setDataLoaded(prev => ({ ...prev, backgrounds: true }));
|
||||
});
|
||||
} else {
|
||||
fetchBackgrounds().finally(() => {
|
||||
setDataLoaded(prev => ({ ...prev, backgrounds: true }));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [isOpen, activeTab, memes.length, backgrounds.length, isFetchingMemes, isFetchingBackgrounds]);
|
||||
}, [isOpen, activeTab, dataLoaded.memes, dataLoaded.backgrounds, isFetchingMemes, isFetchingBackgrounds]);
|
||||
|
||||
const handleTabChange = (value) => {
|
||||
setActiveTab(value);
|
||||
if (value === 'memes' && memes.length === 0 && !isFetchingMemes) {
|
||||
fetchMemes();
|
||||
} else if (value === 'backgrounds' && backgrounds.length === 0 && !isFetchingBackgrounds) {
|
||||
fetchBackgrounds();
|
||||
if (value === 'memes' && !dataLoaded.memes && !isFetchingMemes) {
|
||||
const query = searchQueries.memes;
|
||||
if (query) {
|
||||
searchMemes(query).finally(() => {
|
||||
setDataLoaded(prev => ({ ...prev, memes: true }));
|
||||
});
|
||||
} else {
|
||||
fetchMemes().finally(() => {
|
||||
setDataLoaded(prev => ({ ...prev, memes: true }));
|
||||
});
|
||||
}
|
||||
} else if (value === 'backgrounds' && !dataLoaded.backgrounds && !isFetchingBackgrounds) {
|
||||
const query = searchQueries.backgrounds;
|
||||
if (query) {
|
||||
searchBackgrounds(query).finally(() => {
|
||||
setDataLoaded(prev => ({ ...prev, backgrounds: true }));
|
||||
});
|
||||
} else {
|
||||
fetchBackgrounds().finally(() => {
|
||||
setDataLoaded(prev => ({ ...prev, backgrounds: true }));
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Handle search input changes
|
||||
const handleSearchChange = (value) => {
|
||||
setSearchQueries(prev => ({
|
||||
...prev,
|
||||
[activeTab]: value
|
||||
}));
|
||||
};
|
||||
|
||||
// Handle search submission
|
||||
const handleSearch = () => {
|
||||
const query = searchQueries[activeTab];
|
||||
if (activeTab === 'memes') {
|
||||
searchMemes(query).finally(() => {
|
||||
setDataLoaded(prev => ({ ...prev, memes: true }));
|
||||
});
|
||||
} else if (activeTab === 'backgrounds') {
|
||||
searchBackgrounds(query).finally(() => {
|
||||
setDataLoaded(prev => ({ ...prev, backgrounds: true }));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Handle clearing search
|
||||
const handleClearSearch = () => {
|
||||
setSearchQueries(prev => ({
|
||||
...prev,
|
||||
[activeTab]: ''
|
||||
}));
|
||||
|
||||
// Reset data loaded state and fetch fresh data without search
|
||||
if (activeTab === 'memes') {
|
||||
fetchMemes().finally(() => {
|
||||
setDataLoaded(prev => ({ ...prev, memes: true }));
|
||||
});
|
||||
} else if (activeTab === 'backgrounds') {
|
||||
fetchBackgrounds().finally(() => {
|
||||
setDataLoaded(prev => ({ ...prev, backgrounds: true }));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Handle Enter key in search input
|
||||
const handleSearchKeyDown = (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleSearch();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -114,9 +213,39 @@ export default function EditSidebar({ isOpen, onClose }) {
|
||||
<TabsTrigger value="backgrounds">Background</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
{/* Search Bar */}
|
||||
<div className="relative flex items-center gap-2 px-2 pt-3">
|
||||
<div className="relative flex-1">
|
||||
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400" />
|
||||
<Input
|
||||
placeholder={`Search ${activeTab === 'memes' ? 'memes' : 'backgrounds'}...`}
|
||||
value={searchQueries[activeTab]}
|
||||
onChange={(e) => handleSearchChange(e.target.value)}
|
||||
onKeyDown={handleSearchKeyDown}
|
||||
className="pl-10 pr-10"
|
||||
/>
|
||||
{searchQueries[activeTab] && (
|
||||
<button
|
||||
onClick={handleClearSearch}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
onClick={handleSearch}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="px-3"
|
||||
>
|
||||
<Search className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<TabsContent value="backgrounds" className="">
|
||||
{isFetchingBackgrounds && <Spinner className="h-4 w-4"></Spinner>}
|
||||
{!isFetchingBackgrounds && backgrounds.length === 0 && <div className="w-full text-center">No backgrounds available.</div>}
|
||||
{isFetchingBackgrounds && <GridSkeleton itemCount={6} />}
|
||||
{!isFetchingBackgrounds && backgrounds.length === 0 && <div className="w-full text-center p-8 text-gray-500">No backgrounds found.</div>}
|
||||
{!isFetchingBackgrounds && backgrounds.length > 0 && (
|
||||
<>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
@@ -139,18 +268,13 @@ export default function EditSidebar({ isOpen, onClose }) {
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
<div className="fixed bottom-5 mx-auto flex w-75 justify-center gap-2">
|
||||
<Button className="rounded-full px-5" onClick={fetchBackgrounds}>
|
||||
Refresh List{' '}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="memes" className="">
|
||||
{isFetchingMemes && <Spinner className="h-4 w-4"></Spinner>}
|
||||
{!isFetchingMemes && memes.length === 0 && <div className="w-full text-center">No memes available.</div>}
|
||||
{isFetchingMemes && <GridSkeleton itemCount={6} />}
|
||||
{!isFetchingMemes && memes.length === 0 && <div className="w-full text-center p-8 text-gray-500">No memes found.</div>}
|
||||
{!isFetchingMemes && memes.length > 0 && (
|
||||
<>
|
||||
<div className="grid grid-cols-2 gap-2 p-2">
|
||||
@@ -167,11 +291,6 @@ export default function EditSidebar({ isOpen, onClose }) {
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
<div className="fixed bottom-5 mx-auto flex w-75 justify-center gap-2">
|
||||
<Button className="rounded-full px-5 shadow-lg" onClick={fetchMemes}>
|
||||
Refresh List{' '}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</TabsContent>
|
||||
|
||||
@@ -206,6 +206,56 @@ const useMediaStore = create(
|
||||
}
|
||||
},
|
||||
|
||||
// Search memes
|
||||
searchMemes: async (query = '') => {
|
||||
set({ isFetchingMemes: true });
|
||||
try {
|
||||
const response = await axiosInstance.post(route('api.app.search.memes'), { query });
|
||||
|
||||
if (response?.data?.success?.data?.memes) {
|
||||
set({
|
||||
memes: response.data.success.data.memes,
|
||||
isFetchingMemes: false,
|
||||
});
|
||||
return response.data.success.data.memes;
|
||||
} else {
|
||||
throw 'Invalid API response';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error searching memes:', error);
|
||||
set({ isFetchingMemes: false });
|
||||
if (error?.response?.data?.error?.message?.length > 0) {
|
||||
toast.error(error.response.data.error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Search backgrounds
|
||||
searchBackgrounds: async (query = '') => {
|
||||
set({ isFetchingBackgrounds: true });
|
||||
try {
|
||||
const response = await axiosInstance.post(route('api.app.search.background'), { query });
|
||||
|
||||
if (response?.data?.success?.data?.backgrounds) {
|
||||
set({
|
||||
backgrounds: response.data.success.data.backgrounds,
|
||||
isFetchingBackgrounds: false,
|
||||
});
|
||||
return response.data.success.data.backgrounds;
|
||||
} else {
|
||||
throw 'Invalid API response';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error searching backgrounds:', error);
|
||||
set({ isFetchingBackgrounds: false });
|
||||
if (error?.response?.data?.error?.message?.length > 0) {
|
||||
toast.error(error.response.data.error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// Reset store to default state
|
||||
restoreMemeStateToDefault: () => {
|
||||
console.log('restoreMemeStateToDefault');
|
||||
|
||||
Reference in New Issue
Block a user