import React, { useCallback, useState, useEffect } from 'react'; import { View, Text, StyleSheet, TouchableOpacity, Switch, ScrollView, SafeAreaView, StatusBar, Alert, Platform, Dimensions, Image } from 'react-native'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { useNavigation } from '@react-navigation/native'; import { NavigationProp } from '@react-navigation/native'; import MaterialIcons from 'react-native-vector-icons/MaterialIcons'; import { Picker } from '@react-native-picker/picker'; import { useSettings, DEFAULT_SETTINGS } from '../hooks/useSettings'; import { RootStackParamList } from '../navigation/AppNavigator'; import { stremioService } from '../services/stremioService'; import { useCatalogContext } from '../contexts/CatalogContext'; import { useTraktContext } from '../contexts/TraktContext'; import { useTheme } from '../contexts/ThemeContext'; import { catalogService, DataSource } from '../services/catalogService'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; const { width } = Dimensions.get('window'); const ANDROID_STATUSBAR_HEIGHT = StatusBar.currentHeight || 0; // Card component with modern style interface SettingsCardProps { children: React.ReactNode; title?: string; } const SettingsCard: React.FC = ({ children, title }) => { const { currentTheme } = useTheme(); return ( {title && ( {title.toUpperCase()} )} {children} ); }; interface SettingItemProps { title: string; description?: string; icon: string; renderControl: () => React.ReactNode; isLast?: boolean; onPress?: () => void; badge?: string | number; } const SettingItem: React.FC = ({ title, description, icon, renderControl, isLast = false, onPress, badge }) => { const { currentTheme } = useTheme(); return ( {title} {description && ( {description} )} {badge && ( {badge} )} {renderControl()} ); }; const SettingsScreen: React.FC = () => { const { settings, updateSetting } = useSettings(); const navigation = useNavigation>(); const { lastUpdate } = useCatalogContext(); const { isAuthenticated, userProfile, refreshAuthStatus } = useTraktContext(); const { currentTheme } = useTheme(); const insets = useSafeAreaInsets(); // Add a useEffect to check authentication status on focus useEffect(() => { // This will reload the Trakt auth status whenever the settings screen is focused const unsubscribe = navigation.addListener('focus', () => { // Force a re-render when returning to this screen // This will reflect the updated isAuthenticated state from the TraktContext // Refresh auth status if (isAuthenticated || userProfile) { // Just to be cautious, log the current state console.log('SettingsScreen focused, refreshing auth status. Current state:', { isAuthenticated, userProfile: userProfile?.username }); } refreshAuthStatus(); }); return unsubscribe; }, [navigation, isAuthenticated, userProfile, refreshAuthStatus]); // States for dynamic content const [addonCount, setAddonCount] = useState(0); const [catalogCount, setCatalogCount] = useState(0); const [mdblistKeySet, setMdblistKeySet] = useState(false); const [discoverDataSource, setDiscoverDataSource] = useState(DataSource.STREMIO_ADDONS); const loadData = useCallback(async () => { try { // Load addon count and get their catalogs const addons = await stremioService.getInstalledAddonsAsync(); setAddonCount(addons.length); // Count total available catalogs let totalCatalogs = 0; addons.forEach(addon => { if (addon.catalogs && addon.catalogs.length > 0) { totalCatalogs += addon.catalogs.length; } }); // Load saved catalog settings const catalogSettingsJson = await AsyncStorage.getItem('catalog_settings'); if (catalogSettingsJson) { const catalogSettings = JSON.parse(catalogSettingsJson); // Filter out _lastUpdate key and count only explicitly disabled catalogs const disabledCount = Object.entries(catalogSettings) .filter(([key, value]) => key !== '_lastUpdate' && value === false) .length; // Since catalogs are enabled by default, subtract disabled ones from total setCatalogCount(totalCatalogs - disabledCount); } else { // If no settings saved, all catalogs are enabled by default setCatalogCount(totalCatalogs); } // Check MDBList API key status const mdblistKey = await AsyncStorage.getItem('mdblist_api_key'); setMdblistKeySet(!!mdblistKey); // Get discover data source preference const dataSource = await catalogService.getDataSourcePreference(); setDiscoverDataSource(dataSource); } catch (error) { console.error('Error loading settings data:', error); } }, []); // Load data initially and when catalogs are updated useEffect(() => { loadData(); }, [loadData, lastUpdate]); // Add focus listener to reload data when screen comes into focus useEffect(() => { const unsubscribe = navigation.addListener('focus', () => { loadData(); }); return unsubscribe; }, [navigation, loadData]); const handleResetSettings = useCallback(() => { Alert.alert( 'Reset Settings', 'Are you sure you want to reset all settings to default values?', [ { text: 'Cancel', style: 'cancel' }, { text: 'Reset', style: 'destructive', onPress: () => { (Object.keys(DEFAULT_SETTINGS) as Array).forEach(key => { updateSetting(key, DEFAULT_SETTINGS[key]); }); } } ] ); }, [updateSetting]); const CustomSwitch = ({ value, onValueChange }: { value: boolean, onValueChange: (value: boolean) => void }) => ( ); const ChevronRight = () => ( ); // Handle data source change const handleDiscoverDataSourceChange = useCallback(async (value: string) => { const dataSource = value as DataSource; setDiscoverDataSource(dataSource); await catalogService.setDataSourcePreference(dataSource); }, []); const headerBaseHeight = Platform.OS === 'android' ? 80 : 60; const topSpacing = Platform.OS === 'android' ? (StatusBar.currentHeight || 0) : insets.top; const headerHeight = headerBaseHeight + topSpacing; return ( Settings navigation.navigate('TraktSettings')} isLast={false} /> {isAuthenticated ? ( navigation.navigate('ProfilesSettings')} isLast={true} /> ) : ( Sign in to use Profiles Create multiple profiles for different users and preferences Separate watchlists Content preferences Personalized recommendations Individual viewing history navigation.navigate('TraktSettings')} > Connect with Trakt )} navigation.navigate('ThemeSettings')} /> ( updateSetting('episodeLayoutStyle', 'vertical')} > Vertical updateSetting('episodeLayoutStyle', 'horizontal')} > Horizontal )} isLast={true} /> navigation.navigate('Calendar')} /> navigation.navigate('NotificationSettings')} isLast={true} /> navigation.navigate('Addons')} badge={addonCount} /> navigation.navigate('CatalogSettings')} badge={catalogCount} /> navigation.navigate('InternalProvidersSettings')} /> navigation.navigate('HomeScreenSettings')} /> navigation.navigate('MDBListSettings')} /> navigation.navigate('LogoSourceSettings')} /> navigation.navigate('TMDBSettings')} isLast={true} /> navigation.navigate('PlayerSettings')} isLast={true} /> ( handleDiscoverDataSourceChange(DataSource.STREMIO_ADDONS)} > Addons handleDiscoverDataSourceChange(DataSource.TMDB)} > TMDB )} /> Version 1.0.0 ); }; const styles = StyleSheet.create({ container: { flex: 1, }, header: { paddingHorizontal: 20, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-end', paddingBottom: 8, backgroundColor: 'transparent', zIndex: 2, }, headerTitle: { fontSize: 32, fontWeight: '800', letterSpacing: 0.3, }, contentContainer: { flex: 1, zIndex: 1, width: '100%', }, scrollView: { flex: 1, width: '100%', }, scrollContent: { flexGrow: 1, width: '100%', paddingBottom: 90, }, cardContainer: { width: '100%', marginBottom: 20, }, cardTitle: { fontSize: 13, fontWeight: '600', letterSpacing: 0.8, marginLeft: 16, marginBottom: 8, }, card: { marginHorizontal: 16, borderRadius: 16, overflow: 'hidden', shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 3, width: undefined, // Let it fill the container width }, settingItem: { flexDirection: 'row', alignItems: 'center', paddingVertical: 12, paddingHorizontal: 16, borderBottomWidth: 0.5, minHeight: 58, width: '100%', }, settingItemBorder: { // Border styling handled directly in the component with borderBottomWidth }, settingIconContainer: { marginRight: 16, width: 36, height: 36, borderRadius: 10, alignItems: 'center', justifyContent: 'center', }, settingContent: { flex: 1, flexDirection: 'row', alignItems: 'center', }, settingTextContainer: { flex: 1, }, settingTitle: { fontSize: 16, fontWeight: '500', marginBottom: 3, }, settingDescription: { fontSize: 14, opacity: 0.8, }, settingControl: { justifyContent: 'center', alignItems: 'center', paddingLeft: 12, }, badge: { height: 22, minWidth: 22, borderRadius: 11, alignItems: 'center', justifyContent: 'center', paddingHorizontal: 6, marginRight: 8, }, badgeText: { color: 'white', fontSize: 12, fontWeight: '600', }, versionContainer: { alignItems: 'center', justifyContent: 'center', marginTop: 10, marginBottom: 20, }, versionText: { fontSize: 14, }, pickerContainer: { flex: 1, }, picker: { flex: 1, }, selectorContainer: { flexDirection: 'row', borderRadius: 8, overflow: 'hidden', height: 36, width: 180, marginRight: 8, }, selectorButton: { flex: 1, justifyContent: 'center', alignItems: 'center', paddingHorizontal: 8, backgroundColor: 'rgba(255,255,255,0.08)', }, selectorText: { fontSize: 13, fontWeight: '500', textAlign: 'center', }, profileLockContainer: { padding: 16, borderRadius: 8, overflow: 'hidden', marginVertical: 8, }, profileLockContent: { flexDirection: 'row', alignItems: 'center', }, profileLockTextContainer: { flex: 1, marginHorizontal: 12, }, profileLockTitle: { fontSize: 16, fontWeight: '600', marginBottom: 4, }, profileLockDescription: { fontSize: 14, opacity: 0.8, }, profileBenefits: { flexDirection: 'row', marginTop: 16, justifyContent: 'space-between', }, benefitCol: { flex: 1, }, benefitItem: { flexDirection: 'row', alignItems: 'center', marginBottom: 8, }, benefitText: { fontSize: 14, marginLeft: 8, }, loginButton: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', borderRadius: 8, paddingVertical: 12, marginTop: 16, }, loginButtonText: { color: '#FFFFFF', fontSize: 16, fontWeight: '600', }, loginButtonIcon: { marginLeft: 8, }, }); export default SettingsScreen;