Spaces:
Sleeping
Sleeping
| 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; | |