import React, { useCallback, useState, useEffect } from 'react'; import { View, Text, StyleSheet, TouchableOpacity, Switch, ScrollView, useColorScheme, 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 { colors } from '../styles/colors'; 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 { 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; isDarkMode: boolean; title?: string; } const SettingsCard: React.FC = ({ children, isDarkMode, title }) => ( {title && ( {title.toUpperCase()} )} {children} ); interface SettingItemProps { title: string; description?: string; icon: string; renderControl: () => React.ReactNode; isLast?: boolean; onPress?: () => void; isDarkMode: boolean; badge?: string | number; } const SettingItem: React.FC = ({ title, description, icon, renderControl, isLast = false, onPress, isDarkMode, badge }) => { return ( {title} {description && ( {description} )} {badge && ( {badge} )} {renderControl()} ); }; const SettingsScreen: React.FC = () => { const { settings, updateSetting } = useSettings(); const systemColorScheme = useColorScheme(); const isDarkMode = systemColorScheme === 'dark' || settings.enableDarkMode; const navigation = useNavigation>(); const { lastUpdate } = useCatalogContext(); const { isAuthenticated, userProfile } = useTraktContext(); const insets = useSafeAreaInsets(); // 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); // 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]); 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 ( {/* Fixed position header background to prevent shifts */} {/* Header Section with proper top spacing */} Settings Reset {/* Content Container */} navigation.navigate('TraktSettings')} isLast={true} /> navigation.navigate('Calendar')} isDarkMode={isDarkMode} /> navigation.navigate('NotificationSettings')} isDarkMode={isDarkMode} isLast={true} /> navigation.navigate('Addons')} badge={addonCount} /> navigation.navigate('CatalogSettings')} badge={catalogCount} /> navigation.navigate('HomeScreenSettings')} /> navigation.navigate('MDBListSettings')} /> ( { console.log('Setting logo source preference to Metahub'); updateSetting('logoSourcePreference', 'metahub'); console.log('New logo source preference:', 'metahub'); // Clear any cached logo data in storage try { // This is just to help clear any cached state - the exact implementation may vary AsyncStorage.removeItem('_last_logos_'); } catch (e) { console.error('Error clearing logo cache:', e); } // Show alert that settings have been updated Alert.alert( 'Settings Updated', 'Logo source preference set to Metahub. Changes will apply when you navigate to content.', [{ text: 'OK' }] ); }} > Metahub { console.log('Setting logo source preference to TMDB'); updateSetting('logoSourcePreference', 'tmdb'); console.log('New logo source preference:', 'tmdb'); // Clear any cached logo data in storage try { // This is just to help clear any cached state - the exact implementation may vary AsyncStorage.removeItem('_last_logos_'); } catch (e) { console.error('Error clearing logo cache:', e); } // Show alert that settings have been updated Alert.alert( 'Settings Updated', 'Logo source preference set to TMDB. Changes will apply when you navigate to content.', [{ text: 'OK' }] ); }} > TMDB )} /> 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, }, headerBackground: { position: 'absolute', top: 0, left: 0, right: 0, zIndex: 1, }, contentContainer: { flex: 1, zIndex: 1, width: '100%', }, 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, }, resetButton: { paddingVertical: 8, paddingHorizontal: 12, }, resetButtonText: { fontSize: 16, fontWeight: '600', }, scrollView: { flex: 1, width: '100%', }, scrollContent: { flexGrow: 1, width: '100%', paddingBottom: 32, }, 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, }, settingTitleRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', }, 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: 160, marginRight: 8, }, selectorButton: { flex: 1, justifyContent: 'center', alignItems: 'center', paddingHorizontal: 12, backgroundColor: 'rgba(255,255,255,0.08)', }, selectorButtonActive: { backgroundColor: colors.primary, }, selectorText: { fontSize: 14, fontWeight: '500', color: colors.mediumEmphasis, }, selectorTextActive: { color: colors.white, fontWeight: '600', }, }); export default SettingsScreen;