import React, { useState, useEffect, useMemo, useCallback } from 'react';
import {
View,
Text,
StyleSheet,
FlatList,
TouchableOpacity,
useColorScheme,
useWindowDimensions,
SafeAreaView,
StatusBar,
Animated as RNAnimated,
ActivityIndicator,
Platform,
ScrollView,
} from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { NavigationProp } from '@react-navigation/native';
import { MaterialIcons } from '@expo/vector-icons';
import { Image } from 'expo-image';
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
import { LinearGradient } from 'expo-linear-gradient';
import { catalogService } from '../services/catalogService';
import type { StreamingContent } from '../services/catalogService';
import { RootStackParamList } from '../navigation/AppNavigator';
import { logger } from '../utils/logger';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { useTheme } from '../contexts/ThemeContext';
import { useTraktContext } from '../contexts/TraktContext';
import TraktIcon from '../../assets/rating-icons/trakt.svg';
import { traktService, TraktService } from '../services/traktService';
// Define interfaces for proper typing
interface LibraryItem extends StreamingContent {
progress?: number;
lastWatched?: string;
}
interface TraktDisplayItem {
id: string;
name: string;
type: 'movie' | 'series';
poster: string;
year?: number;
lastWatched?: string;
plays?: number;
rating?: number;
imdbId?: string;
traktId: number;
}
interface TraktFolder {
id: string;
name: string;
icon: keyof typeof MaterialIcons.glyphMap;
description: string;
itemCount: number;
gradient: [string, string];
}
const ANDROID_STATUSBAR_HEIGHT = StatusBar.currentHeight || 0;
const SkeletonLoader = () => {
const pulseAnim = React.useRef(new RNAnimated.Value(0)).current;
const { width } = useWindowDimensions();
const itemWidth = (width - 48) / 2;
const { currentTheme } = useTheme();
React.useEffect(() => {
const pulse = RNAnimated.loop(
RNAnimated.sequence([
RNAnimated.timing(pulseAnim, {
toValue: 1,
duration: 1000,
useNativeDriver: true,
}),
RNAnimated.timing(pulseAnim, {
toValue: 0,
duration: 1000,
useNativeDriver: true,
}),
])
);
pulse.start();
return () => pulse.stop();
}, [pulseAnim]);
const opacity = pulseAnim.interpolate({
inputRange: [0, 1],
outputRange: [0.3, 0.7],
});
const renderSkeletonItem = () => (
);
return (
{[...Array(6)].map((_, index) => (
{renderSkeletonItem()}
))}
);
};
const LibraryScreen = () => {
const navigation = useNavigation>();
const isDarkMode = useColorScheme() === 'dark';
const { width } = useWindowDimensions();
const [loading, setLoading] = useState(true);
const [libraryItems, setLibraryItems] = useState([]);
const [filter, setFilter] = useState<'all' | 'movies' | 'series'>('all');
const [showTraktContent, setShowTraktContent] = useState(false);
const [selectedTraktFolder, setSelectedTraktFolder] = useState(null);
const insets = useSafeAreaInsets();
const { currentTheme } = useTheme();
// Trakt integration
const {
isAuthenticated: traktAuthenticated,
isLoading: traktLoading,
watchedMovies,
watchedShows,
watchlistMovies,
watchlistShows,
collectionMovies,
collectionShows,
continueWatching,
ratedContent,
loadWatchedItems,
loadAllCollections
} = useTraktContext();
// Force consistent status bar settings
useEffect(() => {
const applyStatusBarConfig = () => {
StatusBar.setBarStyle('light-content');
if (Platform.OS === 'android') {
StatusBar.setTranslucent(true);
StatusBar.setBackgroundColor('transparent');
}
};
applyStatusBarConfig();
// Re-apply on focus
const unsubscribe = navigation.addListener('focus', applyStatusBarConfig);
return unsubscribe;
}, [navigation]);
useEffect(() => {
const loadLibrary = async () => {
setLoading(true);
try {
const items = await catalogService.getLibraryItems();
setLibraryItems(items);
} catch (error) {
logger.error('Failed to load library:', error);
} finally {
setLoading(false);
}
};
loadLibrary();
// Subscribe to library updates
const unsubscribe = catalogService.subscribeToLibraryUpdates((items) => {
setLibraryItems(items);
});
return () => {
unsubscribe();
};
}, []);
const filteredItems = libraryItems.filter(item => {
if (filter === 'all') return true;
if (filter === 'movies') return item.type === 'movie';
if (filter === 'series') return item.type === 'series';
return true;
});
// Generate Trakt collection folders
const traktFolders = useMemo((): TraktFolder[] => {
if (!traktAuthenticated) return [];
const folders: TraktFolder[] = [
{
id: 'watched',
name: 'Watched',
icon: 'visibility',
description: 'Your watched content',
itemCount: (watchedMovies?.length || 0) + (watchedShows?.length || 0),
gradient: ['#4CAF50', '#2E7D32']
},
{
id: 'continue-watching',
name: 'Continue Watching',
icon: 'play-circle-outline',
description: 'Resume your progress',
itemCount: continueWatching?.length || 0,
gradient: ['#FF9800', '#F57C00']
},
{
id: 'watchlist',
name: 'Watchlist',
icon: 'bookmark',
description: 'Want to watch',
itemCount: (watchlistMovies?.length || 0) + (watchlistShows?.length || 0),
gradient: ['#2196F3', '#1976D2']
},
{
id: 'collection',
name: 'Collection',
icon: 'library-add',
description: 'Your collection',
itemCount: (collectionMovies?.length || 0) + (collectionShows?.length || 0),
gradient: ['#9C27B0', '#7B1FA2']
},
{
id: 'ratings',
name: 'Rated',
icon: 'star',
description: 'Your ratings',
itemCount: ratedContent?.length || 0,
gradient: ['#FF5722', '#D84315']
}
];
// Only return folders that have content
return folders.filter(folder => folder.itemCount > 0);
}, [traktAuthenticated, watchedMovies, watchedShows, watchlistMovies, watchlistShows, collectionMovies, collectionShows, continueWatching, ratedContent]);
// State for poster URLs (since they're now async)
const [traktPostersMap, setTraktPostersMap] = useState