Spaces:
Running
Running
import React, { useState, useEffect } from 'react'; | |
import { useNavigate } from 'react-router-dom'; | |
import PageHeader from '../components/PageHeader'; | |
import ContentGrid, { ContentItem } from '../components/ContentGrid'; | |
import { getAllFromMyList, removeFromMyList } from '../lib/storage'; | |
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; | |
import { Plus, TrashIcon } from 'lucide-react'; | |
import { useToast } from '@/hooks/use-toast'; | |
interface MyListItem { | |
type: 'movie' | 'tvshow'; | |
title: string; | |
addedAt: string; | |
} | |
const MyListPage = () => { | |
const [myListItems, setMyListItems] = useState<MyListItem[]>([]); | |
const [showRemoveButtons, setShowRemoveButtons] = useState(false); | |
const [activeTab, setActiveTab] = useState('all'); | |
const [isLoading, setIsLoading] = useState(true); | |
const navigate = useNavigate(); | |
const { toast } = useToast(); | |
useEffect(() => { | |
const fetchMyList = async () => { | |
try { | |
setIsLoading(true); | |
const items = await getAllFromMyList(); | |
// Sort by most recently added | |
items.sort((a, b) => new Date(b.addedAt).getTime() - new Date(a.addedAt).getTime()); | |
setMyListItems(items); | |
} catch (error) { | |
console.error("Error loading My List:", error); | |
} finally { | |
setIsLoading(false); | |
} | |
}; | |
fetchMyList(); | |
}, []); | |
const handleRemoveItem = async (title: string, type: 'movie' | 'tvshow') => { | |
try { | |
await removeFromMyList(title, type); | |
setMyListItems(prev => prev.filter(item => !(item.title === title && item.type === type))); | |
toast({ | |
title: "Removed from My List", | |
description: `"${title}" has been removed from your list`, | |
}); | |
} catch (error) { | |
console.error("Error removing item from My List:", error); | |
toast({ | |
title: "Error", | |
description: "Failed to remove item from your list", | |
variant: "destructive" | |
}); | |
} | |
}; | |
const toggleRemoveButtons = () => { | |
setShowRemoveButtons(!showRemoveButtons); | |
}; | |
const getFilteredItems = (filter: string): ContentItem[] => { | |
let filtered = myListItems; | |
if (filter === 'movies') { | |
filtered = myListItems.filter(item => item.type === 'movie'); | |
} else if (filter === 'tvshows') { | |
filtered = myListItems.filter(item => item.type === 'tvshow'); | |
} | |
// Convert to ContentItem format | |
return filtered.map(item => ({ | |
type: item.type, | |
title: item.title, | |
image: undefined // ContentCard will fetch the image if not provided | |
})); | |
}; | |
const allItems = getFilteredItems('all'); | |
const movieItems = getFilteredItems('movies'); | |
const tvShowItems = getFilteredItems('tvshows'); | |
if (isLoading) { | |
return ( | |
<div className="container mx-auto px-4 py-8 animate-pulse"> | |
<div className="h-8 w-1/3 bg-gray-700 rounded mb-8"></div> | |
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-6"> | |
{[...Array(10)].map((_, i) => ( | |
<div key={i} className="h-32 rounded bg-gray-800"></div> | |
))} | |
</div> | |
</div> | |
); | |
} | |
return ( | |
<div className="container mx-auto px-4 py-8"> | |
<div className="flex justify-between items-center mb-6"> | |
<PageHeader | |
title="My List" | |
subtitle={`${myListItems.length} ${myListItems.length === 1 ? 'title' : 'titles'}`} | |
/> | |
<div className="flex gap-4 items-center"> | |
{myListItems.length > 0 && ( | |
<button | |
onClick={toggleRemoveButtons} | |
className="px-4 py-2 rounded-full bg-theme-card hover:bg-theme-card-hover text-sm flex items-center gap-2" | |
> | |
{showRemoveButtons ? 'Done' : ( | |
<> | |
<TrashIcon size={16} /> | |
<span className="hidden sm:inline">Edit List</span> | |
</> | |
)} | |
</button> | |
)} | |
<button | |
onClick={() => navigate('/browse')} | |
className="px-4 py-2 rounded-full bg-theme-primary hover:bg-theme-primary-hover text-white text-sm flex items-center gap-2" | |
> | |
<Plus size={16} /> | |
<span className="hidden sm:inline">Add Titles</span> | |
</button> | |
</div> | |
</div> | |
{myListItems.length === 0 ? ( | |
<div className="flex flex-col items-center justify-center py-16 text-center"> | |
<div className="text-5xl mb-4">🎬</div> | |
<h3 className="text-xl font-bold mb-2">Your list is empty</h3> | |
<p className="text-gray-400 mb-6">Start adding movies and shows to create your watchlist.</p> | |
<button | |
onClick={() => navigate('/browse')} | |
className="px-6 py-2 rounded bg-theme-primary hover:bg-theme-primary-hover text-white text-sm font-medium" | |
> | |
Browse Content | |
</button> | |
</div> | |
) : ( | |
<Tabs defaultValue={activeTab} onValueChange={setActiveTab}> | |
<TabsList className="mb-8"> | |
<TabsTrigger value="all">All ({allItems.length})</TabsTrigger> | |
<TabsTrigger value="movies">Movies ({movieItems.length})</TabsTrigger> | |
<TabsTrigger value="tvshows">TV Shows ({tvShowItems.length})</TabsTrigger> | |
</TabsList> | |
<TabsContent value="all"> | |
<ContentGrid items={allItems} emptyMessage="No items in your list" /> | |
</TabsContent> | |
<TabsContent value="movies"> | |
<ContentGrid items={movieItems} emptyMessage="No movies in your list" /> | |
</TabsContent> | |
<TabsContent value="tvshows"> | |
<ContentGrid items={tvShowItems} emptyMessage="No TV shows in your list" /> | |
</TabsContent> | |
</Tabs> | |
)} | |
</div> | |
); | |
}; | |
export default MyListPage; | |