Spaces:
Sleeping
Sleeping
| import React, { useState, useEffect } from 'react'; | |
| import PageHeader from '../components/PageHeader'; | |
| import ContentGrid, { ContentItem } from '../components/ContentGrid'; | |
| import { Button } from '@/components/ui/button'; | |
| import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; | |
| import { useToast } from '@/hooks/use-toast'; | |
| import { Trash2, DownloadCloud, Upload } from 'lucide-react'; | |
| import { getAllFromMyList } from '../lib/storage'; | |
| interface WatchHistoryItem { | |
| type: 'movie' | 'tvshow'; | |
| title: string; | |
| lastWatched: string; | |
| progress: number; | |
| completed: boolean; | |
| } | |
| const ProfilePage = () => { | |
| const [watchHistory, setWatchHistory] = useState<WatchHistoryItem[]>([]); | |
| const [myListItems, setMyListItems] = useState<ContentItem[]>([]); | |
| const [activeTab, setActiveTab] = useState('history'); | |
| const { toast } = useToast(); | |
| // Load watch history from localStorage | |
| useEffect(() => { | |
| const loadWatchHistory = () => { | |
| try { | |
| const history: WatchHistoryItem[] = []; | |
| // Scan localStorage for movie progress | |
| for (let i = 0; i < localStorage.length; i++) { | |
| const key = localStorage.key(i); | |
| if (key?.startsWith('movie-progress-')) { | |
| const title = key.replace('movie-progress-', ''); | |
| const data = JSON.parse(localStorage.getItem(key) || '{}'); | |
| if (data && data.lastPlayed) { | |
| history.push({ | |
| type: 'movie', | |
| title, | |
| lastWatched: data.lastPlayed, | |
| progress: Math.round((data.currentTime / data.duration) * 100) || 0, | |
| completed: data.completed || false | |
| }); | |
| } | |
| } | |
| // Scan for TV show progress | |
| if (key?.startsWith('playback-')) { | |
| const showTitle = key.replace('playback-', ''); | |
| const showData = JSON.parse(localStorage.getItem(key) || '{}'); | |
| let lastEpisodeDate = ''; | |
| let lastEpisodeProgress = 0; | |
| let anyEpisodeCompleted = false; | |
| // Find the most recently watched episode | |
| Object.entries(showData).forEach(([_, value]) => { | |
| const episodeData = value as { | |
| lastPlayed: string; | |
| currentTime: number; | |
| duration: number; | |
| completed: boolean; | |
| }; | |
| if (!lastEpisodeDate || new Date(episodeData.lastPlayed) > new Date(lastEpisodeDate)) { | |
| lastEpisodeDate = episodeData.lastPlayed; | |
| lastEpisodeProgress = Math.round((episodeData.currentTime / episodeData.duration) * 100) || 0; | |
| if (episodeData.completed) anyEpisodeCompleted = true; | |
| } | |
| }); | |
| if (lastEpisodeDate) { | |
| history.push({ | |
| type: 'tvshow', | |
| title: showTitle, | |
| lastWatched: lastEpisodeDate, | |
| progress: lastEpisodeProgress, | |
| completed: anyEpisodeCompleted | |
| }); | |
| } | |
| } | |
| } | |
| // Sort by most recently watched | |
| history.sort((a, b) => | |
| new Date(b.lastWatched).getTime() - new Date(a.lastWatched).getTime() | |
| ); | |
| setWatchHistory(history); | |
| } catch (error) { | |
| console.error('Error loading watch history:', error); | |
| } | |
| }; | |
| loadWatchHistory(); | |
| }, []); | |
| // Load My List items | |
| useEffect(() => { | |
| const loadMyList = async () => { | |
| try { | |
| const items = await getAllFromMyList(); | |
| const contentItems: ContentItem[] = items.map(item => ({ | |
| type: item.type, | |
| title: item.title, | |
| image: undefined // ContentCard component will fetch the image | |
| })); | |
| setMyListItems(contentItems); | |
| } catch (error) { | |
| console.error('Error loading my list:', error); | |
| } | |
| }; | |
| loadMyList(); | |
| }, []); | |
| const clearWatchHistory = () => { | |
| // Filter localStorage keys related to watch history | |
| const keysToRemove: string[] = []; | |
| for (let i = 0; i < localStorage.length; i++) { | |
| const key = localStorage.key(i); | |
| if (key && (key.startsWith('movie-progress-') || key.startsWith('playback-'))) { | |
| keysToRemove.push(key); | |
| } | |
| } | |
| // Remove the keys | |
| keysToRemove.forEach(key => localStorage.removeItem(key)); | |
| // Update state | |
| setWatchHistory([]); | |
| toast({ | |
| title: "Watch History Cleared", | |
| description: "Your watch history has been successfully cleared.", | |
| }); | |
| }; | |
| const exportUserData = () => { | |
| try { | |
| const userData = { | |
| watchHistory: {}, | |
| myList: {} | |
| }; | |
| // Export all localStorage data | |
| for (let i = 0; i < localStorage.length; i++) { | |
| const key = localStorage.key(i); | |
| if (!key) continue; | |
| if (key.startsWith('movie-progress-') || key.startsWith('playback-')) { | |
| userData.watchHistory[key] = JSON.parse(localStorage.getItem(key) || '{}'); | |
| } | |
| if (key === 'myList') { | |
| userData.myList = JSON.parse(localStorage.getItem(key) || '[]'); | |
| } | |
| } | |
| // Create downloadable JSON | |
| const dataStr = JSON.stringify(userData, null, 2); | |
| const blob = new Blob([dataStr], { type: 'application/json' }); | |
| const url = URL.createObjectURL(blob); | |
| // Create temporary link and trigger download | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `streamflix-user-data-${new Date().toISOString().slice(0, 10)}.json`; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| URL.revokeObjectURL(url); | |
| toast({ | |
| title: "Export Successful", | |
| description: "Your data has been exported successfully.", | |
| }); | |
| } catch (error) { | |
| console.error('Error exporting user data:', error); | |
| toast({ | |
| title: "Export Failed", | |
| description: "There was an error exporting your data.", | |
| variant: "destructive" | |
| }); | |
| } | |
| }; | |
| const renderWatchHistoryItems = (): ContentItem[] => { | |
| return watchHistory.map(item => ({ | |
| type: item.type, | |
| title: item.title, | |
| image: undefined // ContentCard will fetch the image | |
| })); | |
| }; | |
| return ( | |
| <div className="container mx-auto px-4 py-8"> | |
| <PageHeader title="Your Profile" subtitle="Manage your preferences and data" /> | |
| <div className="mt-8"> | |
| <Tabs defaultValue={activeTab} onValueChange={setActiveTab}> | |
| <TabsList> | |
| <TabsTrigger value="history">Watch History</TabsTrigger> | |
| <TabsTrigger value="mylist">My List</TabsTrigger> | |
| <TabsTrigger value="settings">Settings</TabsTrigger> | |
| </TabsList> | |
| <TabsContent value="history" className="pt-6"> | |
| <div className="flex justify-between items-center mb-6"> | |
| <h2 className="text-xl font-bold">Watch History</h2> | |
| {watchHistory.length > 0 && ( | |
| <Button | |
| variant="destructive" | |
| onClick={clearWatchHistory} | |
| className="flex items-center gap-2" | |
| > | |
| <Trash2 size={16} /> | |
| <span>Clear History</span> | |
| </Button> | |
| )} | |
| </div> | |
| {watchHistory.length === 0 ? ( | |
| <div className="text-center py-12"> | |
| <p className="text-gray-400">You have no watch history yet.</p> | |
| <p className="text-sm text-gray-500 mt-2">Start watching movies and shows to build your history.</p> | |
| </div> | |
| ) : ( | |
| <ContentGrid items={renderWatchHistoryItems()} /> | |
| )} | |
| </TabsContent> | |
| <TabsContent value="mylist" className="pt-6"> | |
| <div className="flex justify-between items-center mb-6"> | |
| <h2 className="text-xl font-bold">My List</h2> | |
| </div> | |
| {myListItems.length === 0 ? ( | |
| <div className="text-center py-12"> | |
| <p className="text-gray-400">You haven't added anything to your list yet.</p> | |
| <p className="text-sm text-gray-500 mt-2">Browse content and click the "+" icon to add titles to your list.</p> | |
| </div> | |
| ) : ( | |
| <ContentGrid items={myListItems} /> | |
| )} | |
| </TabsContent> | |
| <TabsContent value="settings" className="pt-6"> | |
| <div className="space-y-6"> | |
| <div> | |
| <h2 className="text-xl font-bold mb-4">Data Management</h2> | |
| <div className="grid gap-4 md:grid-cols-2"> | |
| <div className="bg-card rounded-lg p-4 border"> | |
| <h3 className="text-lg font-medium mb-2">Export Your Data</h3> | |
| <p className="text-sm text-gray-400 mb-4">Download your watch history and list data as a JSON file.</p> | |
| <Button | |
| onClick={exportUserData} | |
| className="flex items-center gap-2" | |
| > | |
| <DownloadCloud size={16} /> | |
| <span>Export Data</span> | |
| </Button> | |
| </div> | |
| <div className="bg-card rounded-lg p-4 border"> | |
| <h3 className="text-lg font-medium mb-2">Import Your Data</h3> | |
| <p className="text-sm text-gray-400 mb-4">Restore previously exported data (coming soon)</p> | |
| <Button | |
| disabled | |
| variant="outline" | |
| className="flex items-center gap-2 opacity-50" | |
| > | |
| <Upload size={16} /> | |
| <span>Import Data</span> | |
| </Button> | |
| </div> | |
| </div> | |
| </div> | |
| <div> | |
| <h2 className="text-xl font-bold mb-4">Account Settings</h2> | |
| <p className="text-gray-400">Account management features coming soon.</p> | |
| </div> | |
| </div> | |
| </TabsContent> | |
| </Tabs> | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| export default ProfilePage; | |