|
|
|
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(); |
|
|
|
|
|
useEffect(() => { |
|
const loadWatchHistory = () => { |
|
try { |
|
const history: WatchHistoryItem[] = []; |
|
|
|
|
|
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 |
|
}); |
|
} |
|
} |
|
|
|
|
|
if (key?.startsWith('playback-')) { |
|
const showTitle = key.replace('playback-', ''); |
|
const showData = JSON.parse(localStorage.getItem(key) || '{}'); |
|
|
|
let lastEpisodeDate = ''; |
|
let lastEpisodeProgress = 0; |
|
let anyEpisodeCompleted = false; |
|
|
|
|
|
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 |
|
}); |
|
} |
|
} |
|
} |
|
|
|
|
|
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(); |
|
}, []); |
|
|
|
|
|
useEffect(() => { |
|
const loadMyList = async () => { |
|
try { |
|
const items = await getAllFromMyList(); |
|
const contentItems: ContentItem[] = items.map(item => ({ |
|
type: item.type, |
|
title: item.title, |
|
image: undefined |
|
})); |
|
setMyListItems(contentItems); |
|
} catch (error) { |
|
console.error('Error loading my list:', error); |
|
} |
|
}; |
|
|
|
loadMyList(); |
|
}, []); |
|
|
|
const clearWatchHistory = () => { |
|
|
|
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); |
|
} |
|
} |
|
|
|
|
|
keysToRemove.forEach(key => localStorage.removeItem(key)); |
|
|
|
|
|
setWatchHistory([]); |
|
|
|
toast({ |
|
title: "Watch History Cleared", |
|
description: "Your watch history has been successfully cleared.", |
|
}); |
|
}; |
|
|
|
const exportUserData = () => { |
|
try { |
|
const userData = { |
|
watchHistory: {}, |
|
myList: {} |
|
}; |
|
|
|
|
|
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) || '[]'); |
|
} |
|
} |
|
|
|
|
|
const dataStr = JSON.stringify(userData, null, 2); |
|
const blob = new Blob([dataStr], { type: 'application/json' }); |
|
const url = URL.createObjectURL(blob); |
|
|
|
|
|
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 |
|
})); |
|
}; |
|
|
|
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; |
|
|