This commit is contained in:
tapframe 2025-12-20 19:37:05 +05:30
commit b2a9708856

View file

@ -37,8 +37,8 @@ import { useTraktContext } from '../contexts/TraktContext';
import TraktIcon from '../../assets/rating-icons/trakt.svg'; import TraktIcon from '../../assets/rating-icons/trakt.svg';
import { traktService, TraktService, TraktImages } from '../services/traktService'; import { traktService, TraktService, TraktImages } from '../services/traktService';
import { TraktLoadingSpinner } from '../components/common/TraktLoadingSpinner'; import { TraktLoadingSpinner } from '../components/common/TraktLoadingSpinner';
import { useSettings } from '../hooks/useSettings';
// Define interfaces for proper typing
interface LibraryItem extends StreamingContent { interface LibraryItem extends StreamingContent {
progress?: number; progress?: number;
lastWatched?: string; lastWatched?: string;
@ -72,10 +72,9 @@ interface TraktFolder {
const ANDROID_STATUSBAR_HEIGHT = StatusBar.currentHeight || 0; const ANDROID_STATUSBAR_HEIGHT = StatusBar.currentHeight || 0;
// Compute responsive grid layout (more columns on tablets)
function getGridLayout(screenWidth: number): { numColumns: number; itemWidth: number } { function getGridLayout(screenWidth: number): { numColumns: number; itemWidth: number } {
const horizontalPadding = 16; // matches listContainer padding (approx) const horizontalPadding = 16;
const gutter = 12; // space between items (via space-between + marginBottom) const gutter = 12;
let numColumns = 3; let numColumns = 3;
if (screenWidth >= 1200) numColumns = 5; if (screenWidth >= 1200) numColumns = 5;
else if (screenWidth >= 1000) numColumns = 4; else if (screenWidth >= 1000) numColumns = 4;
@ -86,7 +85,19 @@ function getGridLayout(screenWidth: number): { numColumns: number; itemWidth: nu
return { numColumns, itemWidth }; return { numColumns, itemWidth };
} }
const TraktItem = React.memo(({ item, width, navigation, currentTheme }: { item: TraktDisplayItem; width: number; navigation: any; currentTheme: any }) => { const TraktItem = React.memo(({
item,
width,
navigation,
currentTheme,
showTitles
}: {
item: TraktDisplayItem;
width: number;
navigation: any;
currentTheme: any;
showTitles: boolean;
}) => {
const [posterUrl, setPosterUrl] = useState<string | null>(null); const [posterUrl, setPosterUrl] = useState<string | null>(null);
useEffect(() => { useEffect(() => {
@ -129,9 +140,11 @@ const TraktItem = React.memo(({ item, width, navigation, currentTheme }: { item:
</View> </View>
)} )}
</View> </View>
<Text style={[styles.cardTitle, { color: currentTheme.colors.mediumEmphasis }]}> {showTitles && (
{item.name} <Text style={[styles.cardTitle, { color: currentTheme.colors.mediumEmphasis }]}>
</Text> {item.name}
</Text>
)}
</View> </View>
</TouchableOpacity> </TouchableOpacity>
); );
@ -184,7 +197,6 @@ const SkeletonLoader = () => {
</View> </View>
); );
// Render enough skeletons for at least two rows
const skeletonCount = numColumns * 2; const skeletonCount = numColumns * 2;
return ( return (
<View style={styles.skeletonContainer}> <View style={styles.skeletonContainer}>
@ -208,13 +220,12 @@ const LibraryScreen = () => {
const [showTraktContent, setShowTraktContent] = useState(false); const [showTraktContent, setShowTraktContent] = useState(false);
const [selectedTraktFolder, setSelectedTraktFolder] = useState<string | null>(null); const [selectedTraktFolder, setSelectedTraktFolder] = useState<string | null>(null);
const { showInfo, showError } = useToast(); const { showInfo, showError } = useToast();
// DropUpMenu state
const [menuVisible, setMenuVisible] = useState(false); const [menuVisible, setMenuVisible] = useState(false);
const [selectedItem, setSelectedItem] = useState<LibraryItem | null>(null); const [selectedItem, setSelectedItem] = useState<LibraryItem | null>(null);
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const { currentTheme } = useTheme(); const { currentTheme } = useTheme();
const { settings } = useSettings();
// Trakt integration
const { const {
isAuthenticated: traktAuthenticated, isAuthenticated: traktAuthenticated,
isLoading: traktLoading, isLoading: traktLoading,
@ -230,7 +241,6 @@ const LibraryScreen = () => {
loadAllCollections loadAllCollections
} = useTraktContext(); } = useTraktContext();
// Force consistent status bar settings
useEffect(() => { useEffect(() => {
const applyStatusBarConfig = () => { const applyStatusBarConfig = () => {
StatusBar.setBarStyle('light-content'); StatusBar.setBarStyle('light-content');
@ -241,30 +251,24 @@ const LibraryScreen = () => {
}; };
applyStatusBarConfig(); applyStatusBarConfig();
// Re-apply on focus
const unsubscribe = navigation.addListener('focus', applyStatusBarConfig); const unsubscribe = navigation.addListener('focus', applyStatusBarConfig);
return unsubscribe; return unsubscribe;
}, [navigation]); }, [navigation]);
// Handle hardware back button and gesture navigation
useEffect(() => { useEffect(() => {
const backAction = () => { const backAction = () => {
if (showTraktContent) { if (showTraktContent) {
if (selectedTraktFolder) { if (selectedTraktFolder) {
// If in a specific folder, go back to folder list
setSelectedTraktFolder(null); setSelectedTraktFolder(null);
} else { } else {
// If in Trakt collections view, go back to main library
setShowTraktContent(false); setShowTraktContent(false);
} }
return true; // Prevent default back behavior return true;
} }
return false; // Allow default back behavior (navigate back) return false;
}; };
const backHandler = BackHandler.addEventListener('hardwareBackPress', backAction); const backHandler = BackHandler.addEventListener('hardwareBackPress', backAction);
return () => backHandler.remove(); return () => backHandler.remove();
}, [showTraktContent, selectedTraktFolder]); }, [showTraktContent, selectedTraktFolder]);
@ -274,16 +278,13 @@ const LibraryScreen = () => {
try { try {
const items = await catalogService.getLibraryItems(); const items = await catalogService.getLibraryItems();
// Sort by date added (most recent first)
const sortedItems = items.sort((a, b) => { const sortedItems = items.sort((a, b) => {
const timeA = (a as any).addedToLibraryAt || 0; const timeA = (a as any).addedToLibraryAt || 0;
const timeB = (b as any).addedToLibraryAt || 0; const timeB = (b as any).addedToLibraryAt || 0;
return timeB - timeA; // Descending order (newest first) return timeB - timeA;
}); });
// Load watched status for each item from AsyncStorage
const updatedItems = await Promise.all(sortedItems.map(async (item) => { const updatedItems = await Promise.all(sortedItems.map(async (item) => {
// Map StreamingContent to LibraryItem shape
const libraryItem: LibraryItem = { const libraryItem: LibraryItem = {
...item, ...item,
gradient: Array.isArray((item as any).gradient) ? (item as any).gradient : ['#222', '#444'], gradient: Array.isArray((item as any).gradient) ? (item as any).gradient : ['#222', '#444'],
@ -306,18 +307,14 @@ const LibraryScreen = () => {
loadLibrary(); loadLibrary();
// Subscribe to library updates
const unsubscribe = catalogService.subscribeToLibraryUpdates(async (items) => { const unsubscribe = catalogService.subscribeToLibraryUpdates(async (items) => {
// Sort by date added (most recent first)
const sortedItems = items.sort((a, b) => { const sortedItems = items.sort((a, b) => {
const timeA = (a as any).addedToLibraryAt || 0; const timeA = (a as any).addedToLibraryAt || 0;
const timeB = (b as any).addedToLibraryAt || 0; const timeB = (b as any).addedToLibraryAt || 0;
return timeB - timeA; // Descending order (newest first) return timeB - timeA;
}); });
// Sync watched status on update
const updatedItems = await Promise.all(sortedItems.map(async (item) => { const updatedItems = await Promise.all(sortedItems.map(async (item) => {
// Map StreamingContent to LibraryItem shape
const libraryItem: LibraryItem = { const libraryItem: LibraryItem = {
...item, ...item,
gradient: Array.isArray((item as any).gradient) ? (item as any).gradient : ['#222', '#444'], gradient: Array.isArray((item as any).gradient) ? (item as any).gradient : ['#222', '#444'],
@ -333,10 +330,7 @@ const LibraryScreen = () => {
setLibraryItems(updatedItems); setLibraryItems(updatedItems);
}); });
// Listen for watched status changes
const watchedSub = DeviceEventEmitter.addListener('watchedStatusChanged', loadLibrary); const watchedSub = DeviceEventEmitter.addListener('watchedStatusChanged', loadLibrary);
// Refresh when screen regains focus
const focusSub = navigation.addListener('focus', loadLibrary); const focusSub = navigation.addListener('focus', loadLibrary);
return () => { return () => {
@ -352,7 +346,6 @@ const LibraryScreen = () => {
return true; return true;
}); });
// Generate Trakt collection folders
const traktFolders = useMemo((): TraktFolder[] => { const traktFolders = useMemo((): TraktFolder[] => {
if (!traktAuthenticated) return []; if (!traktAuthenticated) return [];
@ -389,61 +382,57 @@ const LibraryScreen = () => {
} }
]; ];
// Only return folders that have content
return folders.filter(folder => folder.itemCount > 0); return folders.filter(folder => folder.itemCount > 0);
}, [traktAuthenticated, watchedMovies, watchedShows, watchlistMovies, watchlistShows, collectionMovies, collectionShows, continueWatching, ratedContent]); }, [traktAuthenticated, watchedMovies, watchedShows, watchlistMovies, watchlistShows, collectionMovies, collectionShows, continueWatching, ratedContent]);
const renderItem = ({ item }: { item: LibraryItem }) => { const renderItem = ({ item }: { item: LibraryItem }) => (
const aspectRatio = item.posterShape === 'landscape' ? 16 / 9 : (item.posterShape === 'square' ? 1 : 2 / 3); <TouchableOpacity
style={[styles.itemContainer, { width: itemWidth }]}
return ( onPress={() => navigation.navigate('Metadata', { id: item.id, type: item.type })}
<TouchableOpacity onLongPress={() => {
style={[styles.itemContainer, { width: itemWidth }]} setSelectedItem(item);
onPress={() => navigation.navigate('Metadata', { id: item.id, type: item.type })} setMenuVisible(true);
onLongPress={() => { }}
setSelectedItem(item); activeOpacity={0.7}
setMenuVisible(true); >
}} <View>
activeOpacity={0.7} <View style={[styles.posterContainer, { shadowColor: currentTheme.colors.black }]}>
> <FastImage
<View> source={{ uri: item.poster || 'https://via.placeholder.com/300x450' }}
<View style={[styles.posterContainer, { shadowColor: currentTheme.colors.black, aspectRatio }]}> style={styles.poster}
<FastImage resizeMode={FastImage.resizeMode.cover}
source={{ uri: item.poster || 'https://via.placeholder.com/300x450' }} />
style={styles.poster} {item.watched && (
resizeMode={FastImage.resizeMode.cover} <View style={styles.watchedIndicator}>
/> <MaterialIcons name="check-circle" size={22} color={currentTheme.colors.success || '#4CAF50'} />
{item.watched && ( </View>
<View style={styles.watchedIndicator}> )}
<MaterialIcons name="check-circle" size={22} color={currentTheme.colors.success || '#4CAF50'} /> {item.progress !== undefined && item.progress < 1 && (
</View> <View style={styles.progressBarContainer}>
)} <View
{item.progress !== undefined && item.progress < 1 && ( style={[
<View style={styles.progressBarContainer}> styles.progressBar,
<View { width: `${item.progress * 100}%`, backgroundColor: currentTheme.colors.primary }
style={[ ]}
styles.progressBar, />
{ width: `${item.progress * 100}%`, backgroundColor: currentTheme.colors.primary } </View>
]} )}
/> </View>
</View> {settings.showPosterTitles && (
)}
</View>
<Text style={[styles.cardTitle, { color: currentTheme.colors.mediumEmphasis }]}> <Text style={[styles.cardTitle, { color: currentTheme.colors.mediumEmphasis }]}>
{item.name} {item.name}
</Text> </Text>
</View> )}
</TouchableOpacity> </View>
); </TouchableOpacity>
}; );
// Render individual Trakt collection folder
const renderTraktCollectionFolder = ({ folder }: { folder: TraktFolder }) => ( const renderTraktCollectionFolder = ({ folder }: { folder: TraktFolder }) => (
<TouchableOpacity <TouchableOpacity
style={[styles.itemContainer, { width: itemWidth }]} style={[styles.itemContainer, { width: itemWidth }]}
onPress={() => { onPress={() => {
setSelectedTraktFolder(folder.id); setSelectedTraktFolder(folder.id);
loadAllCollections(); // Load all collections when entering a specific folder loadAllCollections();
}} }}
activeOpacity={0.7} activeOpacity={0.7}
> >
@ -474,8 +463,8 @@ const LibraryScreen = () => {
navigation.navigate('TraktSettings'); navigation.navigate('TraktSettings');
} else { } else {
setShowTraktContent(true); setShowTraktContent(true);
setSelectedTraktFolder(null); // Reset to folder view setSelectedTraktFolder(null);
loadAllCollections(); // Load all collections when opening loadAllCollections();
} }
}} }}
activeOpacity={0.7} activeOpacity={0.7}
@ -494,24 +483,30 @@ const LibraryScreen = () => {
)} )}
</View> </View>
</View> </View>
<Text style={[styles.cardTitle, { color: currentTheme.colors.mediumEmphasis }]}> {settings.showPosterTitles && (
Trakt collections <Text style={[styles.cardTitle, { color: currentTheme.colors.mediumEmphasis }]}>
</Text> Trakt collections
</Text>
)}
</View> </View>
</TouchableOpacity> </TouchableOpacity>
); );
const renderTraktItem = useCallback(({ item }: { item: TraktDisplayItem }) => { const renderTraktItem = useCallback(({ item }: { item: TraktDisplayItem }) => {
return <TraktItem item={item} width={itemWidth} navigation={navigation} currentTheme={currentTheme} />; return <TraktItem
}, [itemWidth, navigation, currentTheme]); item={item}
width={itemWidth}
navigation={navigation}
currentTheme={currentTheme}
showTitles={settings.showPosterTitles}
/>;
}, [itemWidth, navigation, currentTheme, settings.showPosterTitles]);
// Get items for a specific Trakt folder
const getTraktFolderItems = useCallback((folderId: string): TraktDisplayItem[] => { const getTraktFolderItems = useCallback((folderId: string): TraktDisplayItem[] => {
const items: TraktDisplayItem[] = []; const items: TraktDisplayItem[] = [];
switch (folderId) { switch (folderId) {
case 'watched': case 'watched':
// Add watched movies
if (watchedMovies) { if (watchedMovies) {
for (const watchedMovie of watchedMovies) { for (const watchedMovie of watchedMovies) {
const movie = watchedMovie.movie; const movie = watchedMovie.movie;
@ -522,7 +517,7 @@ const LibraryScreen = () => {
type: 'movie', type: 'movie',
poster: 'placeholder', poster: 'placeholder',
year: movie.year, year: movie.year,
lastWatched: watchedMovie.last_watched_at, // Store raw timestamp for sorting lastWatched: watchedMovie.last_watched_at,
plays: watchedMovie.plays, plays: watchedMovie.plays,
imdbId: movie.ids.imdb, imdbId: movie.ids.imdb,
traktId: movie.ids.trakt, traktId: movie.ids.trakt,
@ -531,7 +526,6 @@ const LibraryScreen = () => {
} }
} }
} }
// Add watched shows
if (watchedShows) { if (watchedShows) {
for (const watchedShow of watchedShows) { for (const watchedShow of watchedShows) {
const show = watchedShow.show; const show = watchedShow.show;
@ -542,7 +536,7 @@ const LibraryScreen = () => {
type: 'series', type: 'series',
poster: 'placeholder', poster: 'placeholder',
year: show.year, year: show.year,
lastWatched: watchedShow.last_watched_at, // Store raw timestamp for sorting lastWatched: watchedShow.last_watched_at,
plays: watchedShow.plays, plays: watchedShow.plays,
imdbId: show.ids.imdb, imdbId: show.ids.imdb,
traktId: show.ids.trakt, traktId: show.ids.trakt,
@ -554,7 +548,6 @@ const LibraryScreen = () => {
break; break;
case 'continue-watching': case 'continue-watching':
// Add continue watching items
if (continueWatching) { if (continueWatching) {
for (const item of continueWatching) { for (const item of continueWatching) {
if (item.type === 'movie' && item.movie) { if (item.type === 'movie' && item.movie) {
@ -564,7 +557,7 @@ const LibraryScreen = () => {
type: 'movie', type: 'movie',
poster: 'placeholder', poster: 'placeholder',
year: item.movie.year, year: item.movie.year,
lastWatched: item.paused_at, // Store raw timestamp for sorting lastWatched: item.paused_at,
imdbId: item.movie.ids.imdb, imdbId: item.movie.ids.imdb,
traktId: item.movie.ids.trakt, traktId: item.movie.ids.trakt,
images: item.movie.images, images: item.movie.images,
@ -576,7 +569,7 @@ const LibraryScreen = () => {
type: 'series', type: 'series',
poster: 'placeholder', poster: 'placeholder',
year: item.show.year, year: item.show.year,
lastWatched: item.paused_at, // Store raw timestamp for sorting lastWatched: item.paused_at,
imdbId: item.show.ids.imdb, imdbId: item.show.ids.imdb,
traktId: item.show.ids.trakt, traktId: item.show.ids.trakt,
images: item.show.images, images: item.show.images,
@ -587,7 +580,6 @@ const LibraryScreen = () => {
break; break;
case 'watchlist': case 'watchlist':
// Add watchlist movies
if (watchlistMovies) { if (watchlistMovies) {
for (const watchlistMovie of watchlistMovies) { for (const watchlistMovie of watchlistMovies) {
const movie = watchlistMovie.movie; const movie = watchlistMovie.movie;
@ -598,7 +590,7 @@ const LibraryScreen = () => {
type: 'movie', type: 'movie',
poster: 'placeholder', poster: 'placeholder',
year: movie.year, year: movie.year,
lastWatched: watchlistMovie.listed_at, // Store raw timestamp for sorting lastWatched: watchlistMovie.listed_at,
imdbId: movie.ids.imdb, imdbId: movie.ids.imdb,
traktId: movie.ids.trakt, traktId: movie.ids.trakt,
images: movie.images, images: movie.images,
@ -606,7 +598,6 @@ const LibraryScreen = () => {
} }
} }
} }
// Add watchlist shows
if (watchlistShows) { if (watchlistShows) {
for (const watchlistShow of watchlistShows) { for (const watchlistShow of watchlistShows) {
const show = watchlistShow.show; const show = watchlistShow.show;
@ -617,7 +608,7 @@ const LibraryScreen = () => {
type: 'series', type: 'series',
poster: 'placeholder', poster: 'placeholder',
year: show.year, year: show.year,
lastWatched: watchlistShow.listed_at, // Store raw timestamp for sorting lastWatched: watchlistShow.listed_at,
imdbId: show.ids.imdb, imdbId: show.ids.imdb,
traktId: show.ids.trakt, traktId: show.ids.trakt,
images: show.images, images: show.images,
@ -628,7 +619,6 @@ const LibraryScreen = () => {
break; break;
case 'collection': case 'collection':
// Add collection movies
if (collectionMovies) { if (collectionMovies) {
for (const collectionMovie of collectionMovies) { for (const collectionMovie of collectionMovies) {
const movie = collectionMovie.movie; const movie = collectionMovie.movie;
@ -639,7 +629,7 @@ const LibraryScreen = () => {
type: 'movie', type: 'movie',
poster: 'placeholder', poster: 'placeholder',
year: movie.year, year: movie.year,
lastWatched: collectionMovie.collected_at, // Store raw timestamp for sorting lastWatched: collectionMovie.collected_at,
imdbId: movie.ids.imdb, imdbId: movie.ids.imdb,
traktId: movie.ids.trakt, traktId: movie.ids.trakt,
images: movie.images, images: movie.images,
@ -647,7 +637,6 @@ const LibraryScreen = () => {
} }
} }
} }
// Add collection shows
if (collectionShows) { if (collectionShows) {
for (const collectionShow of collectionShows) { for (const collectionShow of collectionShows) {
const show = collectionShow.show; const show = collectionShow.show;
@ -658,7 +647,7 @@ const LibraryScreen = () => {
type: 'series', type: 'series',
poster: 'placeholder', poster: 'placeholder',
year: show.year, year: show.year,
lastWatched: collectionShow.collected_at, // Store raw timestamp for sorting lastWatched: collectionShow.collected_at,
imdbId: show.ids.imdb, imdbId: show.ids.imdb,
traktId: show.ids.trakt, traktId: show.ids.trakt,
images: show.images, images: show.images,
@ -669,7 +658,6 @@ const LibraryScreen = () => {
break; break;
case 'ratings': case 'ratings':
// Add rated content
if (ratedContent) { if (ratedContent) {
for (const ratedItem of ratedContent) { for (const ratedItem of ratedContent) {
if (ratedItem.movie) { if (ratedItem.movie) {
@ -680,7 +668,7 @@ const LibraryScreen = () => {
type: 'movie', type: 'movie',
poster: 'placeholder', poster: 'placeholder',
year: movie.year, year: movie.year,
lastWatched: ratedItem.rated_at, // Store raw timestamp for sorting lastWatched: ratedItem.rated_at,
rating: ratedItem.rating, rating: ratedItem.rating,
imdbId: movie.ids.imdb, imdbId: movie.ids.imdb,
traktId: movie.ids.trakt, traktId: movie.ids.trakt,
@ -694,7 +682,7 @@ const LibraryScreen = () => {
type: 'series', type: 'series',
poster: 'placeholder', poster: 'placeholder',
year: show.year, year: show.year,
lastWatched: ratedItem.rated_at, // Store raw timestamp for sorting lastWatched: ratedItem.rated_at,
rating: ratedItem.rating, rating: ratedItem.rating,
imdbId: show.ids.imdb, imdbId: show.ids.imdb,
traktId: show.ids.trakt, traktId: show.ids.trakt,
@ -706,7 +694,6 @@ const LibraryScreen = () => {
break; break;
} }
// Sort by last watched/added date (most recent first) using raw timestamps
return items.sort((a, b) => { return items.sort((a, b) => {
const dateA = a.lastWatched ? new Date(a.lastWatched).getTime() : 0; const dateA = a.lastWatched ? new Date(a.lastWatched).getTime() : 0;
const dateB = b.lastWatched ? new Date(b.lastWatched).getTime() : 0; const dateB = b.lastWatched ? new Date(b.lastWatched).getTime() : 0;
@ -719,7 +706,6 @@ const LibraryScreen = () => {
return <TraktLoadingSpinner />; return <TraktLoadingSpinner />;
} }
// If no specific folder is selected, show the folder structure
if (!selectedTraktFolder) { if (!selectedTraktFolder) {
if (traktFolders.length === 0) { if (traktFolders.length === 0) {
return ( return (
@ -745,7 +731,6 @@ const LibraryScreen = () => {
); );
} }
// Show collection folders
return ( return (
<FlashList <FlashList
data={traktFolders} data={traktFolders}
@ -760,7 +745,6 @@ const LibraryScreen = () => {
); );
} }
// Show content for specific folder
const folderItems = getTraktFolderItems(selectedTraktFolder); const folderItems = getTraktFolderItems(selectedTraktFolder);
if (folderItems.length === 0) { if (folderItems.length === 0) {
@ -902,7 +886,6 @@ const LibraryScreen = () => {
); );
}; };
// Tablet detection aligned with navigation tablet logic
const isTablet = useMemo(() => { const isTablet = useMemo(() => {
const smallestDimension = Math.min(width, height); const smallestDimension = Math.min(width, height);
return (Platform.OS === 'ios' ? (Platform as any).isPad === true : smallestDimension >= 768); return (Platform.OS === 'ios' ? (Platform as any).isPad === true : smallestDimension >= 768);
@ -910,7 +893,6 @@ const LibraryScreen = () => {
return ( return (
<View style={[styles.container, { backgroundColor: currentTheme.colors.darkBackground }]}> <View style={[styles.container, { backgroundColor: currentTheme.colors.darkBackground }]}>
{/* ScreenHeader Component */}
<ScreenHeader <ScreenHeader
title={showTraktContent title={showTraktContent
? (selectedTraktFolder ? (selectedTraktFolder
@ -932,7 +914,6 @@ const LibraryScreen = () => {
isTablet={isTablet} isTablet={isTablet}
/> />
{/* Content Container */}
<View style={[styles.contentContainer, { backgroundColor: currentTheme.colors.darkBackground }]}> <View style={[styles.contentContainer, { backgroundColor: currentTheme.colors.darkBackground }]}>
{!showTraktContent && ( {!showTraktContent && (
<View style={styles.filtersContainer}> <View style={styles.filtersContainer}>
@ -945,14 +926,13 @@ const LibraryScreen = () => {
{showTraktContent ? renderTraktContent() : renderContent()} {showTraktContent ? renderTraktContent() : renderContent()}
</View> </View>
{/* DropUpMenu integration */}
{selectedItem && ( {selectedItem && (
<DropUpMenu <DropUpMenu
visible={menuVisible} visible={menuVisible}
onClose={() => setMenuVisible(false)} onClose={() => setMenuVisible(false)}
item={selectedItem} item={selectedItem}
isWatched={!!selectedItem.watched} isWatched={!!selectedItem.watched}
isSaved={true} // Since this is from library, it's always saved isSaved={true}
onOptionSelect={async (option) => { onOptionSelect={async (option) => {
if (!selectedItem) return; if (!selectedItem) return;
switch (option) { switch (option) {
@ -969,12 +949,10 @@ const LibraryScreen = () => {
} }
case 'watched': { case 'watched': {
try { try {
// Use AsyncStorage to store watched status by key
const key = `watched:${selectedItem.type}:${selectedItem.id}`; const key = `watched:${selectedItem.type}:${selectedItem.id}`;
const newWatched = !selectedItem.watched; const newWatched = !selectedItem.watched;
await mmkvStorage.setItem(key, newWatched ? 'true' : 'false'); await mmkvStorage.setItem(key, newWatched ? 'true' : 'false');
showInfo(newWatched ? 'Marked as Watched' : 'Marked as Unwatched', newWatched ? 'Item marked as watched' : 'Item marked as unwatched'); showInfo(newWatched ? 'Marked as Watched' : 'Marked as Unwatched', newWatched ? 'Item marked as watched' : 'Item marked as unwatched');
// Instantly update local state
setLibraryItems(prev => prev.map(item => setLibraryItems(prev => prev.map(item =>
item.id === selectedItem.id && item.type === selectedItem.type item.id === selectedItem.id && item.type === selectedItem.type
? { ...item, watched: newWatched } ? { ...item, watched: newWatched }
@ -1273,7 +1251,7 @@ const styles = StyleSheet.create({
justifyContent: 'center', justifyContent: 'center',
}, },
headerSpacer: { headerSpacer: {
width: 44, // Match the back button width width: 44,
}, },
traktContainer: { traktContainer: {
flex: 1, flex: 1,