From 5804959ddfbe8d228f652a0afa55208e898fa480 Mon Sep 17 00:00:00 2001 From: tapframe Date: Fri, 19 Dec 2025 23:38:33 +0530 Subject: [PATCH] alert orientation fix --- src/components/AnnouncementOverlay.tsx | 1 + src/components/home/DropUpMenu.tsx | 5 +- src/components/metadata/SeriesContent.tsx | 1 + src/components/metadata/TrailersSection.tsx | 57 +++--- src/components/player/AndroidVideoPlayer.tsx | 126 +++++++------ src/screens/AddonsScreen.tsx | 1 + src/screens/CatalogSettingsScreen.tsx | 1 + src/screens/PluginsScreen.tsx | 2 + src/screens/ProfilesScreen.tsx | 47 ++--- src/screens/StreamsScreen.tsx | 38 +++- src/screens/TMDBSettingsScreen.tsx | 177 ++++++++++--------- 11 files changed, 242 insertions(+), 214 deletions(-) diff --git a/src/components/AnnouncementOverlay.tsx b/src/components/AnnouncementOverlay.tsx index 4a352e2..9579327 100644 --- a/src/components/AnnouncementOverlay.tsx +++ b/src/components/AnnouncementOverlay.tsx @@ -101,6 +101,7 @@ const AnnouncementOverlay: React.FC = ({ transparent animationType="none" statusBarTranslucent + supportedOrientations={['portrait', 'landscape']} onRequestClose={handleClose} > diff --git a/src/components/home/DropUpMenu.tsx b/src/components/home/DropUpMenu.tsx index 3ee59ed..41054ea 100644 --- a/src/components/home/DropUpMenu.tsx +++ b/src/components/home/DropUpMenu.tsx @@ -98,7 +98,7 @@ export const DropUpMenu = ({ visible, onClose, item, onOptionSelect, isSaved: is const isWatched = !!isWatchedProp; const inTraktWatchlist = isAuthenticated && isInWatchlist(item.id, item.type as 'movie' | 'show'); const inTraktCollection = isAuthenticated && isInCollection(item.id, item.type as 'movie' | 'show'); - + let menuOptions = [ { icon: 'bookmark', @@ -152,6 +152,7 @@ export const DropUpMenu = ({ visible, onClose, item, onOptionSelect, isSaved: is visible={visible} transparent animationType="none" + supportedOrientations={['portrait', 'landscape']} onRequestClose={onClose} > @@ -162,7 +163,7 @@ export const DropUpMenu = ({ visible, onClose, item, onOptionSelect, isSaved: is = ({ animationType="fade" onRequestClose={closeEpisodeActionMenu} statusBarTranslucent + supportedOrientations={['portrait', 'landscape']} > = memo(({ // Enhanced responsive sizing for tablets and TV screens const deviceWidth = Dimensions.get('window').width; const deviceHeight = Dimensions.get('window').height; - + // Determine device type based on width const getDeviceType = useCallback(() => { if (deviceWidth >= BREAKPOINTS.tv) return 'tv'; @@ -82,13 +82,13 @@ const TrailersSection: React.FC = memo(({ if (deviceWidth >= BREAKPOINTS.tablet) return 'tablet'; return 'phone'; }, [deviceWidth]); - + const deviceType = getDeviceType(); const isTablet = deviceType === 'tablet'; const isLargeTablet = deviceType === 'largeTablet'; const isTV = deviceType === 'tv'; const isLargeScreen = isTablet || isLargeTablet || isTV; - + // Enhanced spacing and padding const horizontalPadding = useMemo(() => { switch (deviceType) { @@ -102,7 +102,7 @@ const TrailersSection: React.FC = memo(({ return 16; // phone } }, [deviceType]); - + // Enhanced trailer card sizing const trailerCardWidth = useMemo(() => { switch (deviceType) { @@ -116,7 +116,7 @@ const TrailersSection: React.FC = memo(({ return 200; // phone } }, [deviceType]); - + const trailerCardSpacing = useMemo(() => { switch (deviceType) { case 'tv': @@ -293,7 +293,7 @@ const TrailersSection: React.FC = memo(({ // Auto-select the first available category, preferring "Trailer" const availableCategories = Object.keys(categorized); const preferredCategory = availableCategories.includes('Trailer') ? 'Trailer' : - availableCategories.includes('Teaser') ? 'Teaser' : availableCategories[0]; + availableCategories.includes('Teaser') ? 'Teaser' : availableCategories[0]; setSelectedCategory(preferredCategory); } } catch (err) { @@ -379,7 +379,7 @@ const TrailersSection: React.FC = memo(({ } catch (error) { logger.warn('TrailersSection', 'Error pausing hero trailer:', error); } - + setSelectedTrailer(trailer); setModalVisible(true); }; @@ -499,15 +499,15 @@ const TrailersSection: React.FC = memo(({ return ( {/* Enhanced Header with Category Selector */} = memo(({ {trailerCategories.length > 0 && selectedCategory && ( = memo(({ > = memo(({ visible={dropdownVisible} transparent={true} animationType="fade" + supportedOrientations={['portrait', 'landscape']} onRequestClose={() => setDropdownVisible(false)} > = memo(({ > = memo(({ color={currentTheme.colors.primary} /> - + {formatTrailerType(category)} = memo(({ = memo(({ {trailer.displayName || trailer.name} { // Helper to get dynamic volume icon const getVolumeIcon = (value: number) => { - if (value === 0) return 'volume-off'; - if (value < 0.3) return 'volume-mute'; - if (value < 0.6) return 'volume-down'; - return 'volume-up'; + if (value === 0) return 'volume-off'; + if (value < 0.3) return 'volume-mute'; + if (value < 0.6) return 'volume-down'; + return 'volume-up'; }; // Helper to get dynamic brightness icon @@ -3432,8 +3432,6 @@ const AndroidVideoPlayer: React.FC = () => { buffered={buffered} formatTime={formatTime} playerBackend={useVLC ? 'VLC' : 'ExoPlayer'} - nextLoadingTitle={nextLoadingTitle} - controlsFixedOffset={Math.min(Dimensions.get('window').width, Dimensions.get('window').height) >= 768 ? 120 : 100} /> {/* Combined Volume & Brightness Gesture Indicator - NEW PILL STYLE (No Bar) */} @@ -3441,45 +3439,45 @@ const AndroidVideoPlayer: React.FC = () => { {/* Dynamic Icon */} - + style={[ + localStyles.iconWrapper, + { + // Conditional Background Color Logic + backgroundColor: gestureControls.showVolumeOverlay && volume === 0 + ? 'rgba(242, 184, 181)' + : 'rgba(59, 59, 59)' + } + ]} + > + {/* Text Label: Shows "Muted" or percentage */} - - {/* Conditional Text Content Logic */} - {gestureControls.showVolumeOverlay && volume === 0 - ? "Muted" // Display "Muted" when volume is 0 - : `${Math.round((gestureControls.showVolumeOverlay ? volume : brightness) * 100)}%` // Display percentage otherwise - } - + + {/* Conditional Text Content Logic */} + {gestureControls.showVolumeOverlay && volume === 0 + ? "Muted" // Display "Muted" when volume is 0 + : `${Math.round((gestureControls.showVolumeOverlay ? volume : brightness) * 100)}%` // Display percentage otherwise + } + )} @@ -4067,32 +4065,32 @@ const AndroidVideoPlayer: React.FC = () => { // New styles for the gesture indicator const localStyles = StyleSheet.create({ gestureIndicatorContainer: { - position: 'absolute', - top: '4%', // Adjust this for vertical position - alignSelf: 'center', // Adjust this for horizontal position - flexDirection: 'row', - alignItems: 'center', - backgroundColor: 'rgba(25, 25, 25)', // Dark pill background - borderRadius: 70, - paddingHorizontal: 15, - paddingVertical: 15, - zIndex: 2000, // Very high z-index to ensure visibility - minWidth: 120, // Adjusted min width since bar is removed + position: 'absolute', + top: '4%', // Adjust this for vertical position + alignSelf: 'center', // Adjust this for horizontal position + flexDirection: 'row', + alignItems: 'center', + backgroundColor: 'rgba(25, 25, 25)', // Dark pill background + borderRadius: 70, + paddingHorizontal: 15, + paddingVertical: 15, + zIndex: 2000, // Very high z-index to ensure visibility + minWidth: 120, // Adjusted min width since bar is removed }, iconWrapper: { - borderRadius: 50, // Makes it a perfect circle (set to a high number) - width: 40, // Define the diameter of the circle - height: 40, // Define the diameter of the circle - justifyContent: 'center', - alignItems: 'center', - marginRight: 12, // Margin to separate icon circle from percentage text + borderRadius: 50, // Makes it a perfect circle (set to a high number) + width: 40, // Define the diameter of the circle + height: 40, // Define the diameter of the circle + justifyContent: 'center', + alignItems: 'center', + marginRight: 12, // Margin to separate icon circle from percentage text }, gestureText: { - color: '#FFFFFF', - fontSize: 18, - fontWeight: 'normal', - minWidth: 35, - textAlign: 'right', + color: '#FFFFFF', + fontSize: 18, + fontWeight: 'normal', + minWidth: 35, + textAlign: 'right', }, }); diff --git a/src/screens/AddonsScreen.tsx b/src/screens/AddonsScreen.tsx index a81b4eb..85528a5 100644 --- a/src/screens/AddonsScreen.tsx +++ b/src/screens/AddonsScreen.tsx @@ -1413,6 +1413,7 @@ const AddonsScreen = () => { visible={showConfirmModal} transparent animationType="fade" + supportedOrientations={['portrait', 'landscape']} onRequestClose={() => { setShowConfirmModal(false); setAddonDetails(null); diff --git a/src/screens/CatalogSettingsScreen.tsx b/src/screens/CatalogSettingsScreen.tsx index 1b5acc7..3c15dfe 100644 --- a/src/screens/CatalogSettingsScreen.tsx +++ b/src/screens/CatalogSettingsScreen.tsx @@ -685,6 +685,7 @@ const CatalogSettingsScreen = () => { animationType="fade" transparent={true} visible={isRenameModalVisible} + supportedOrientations={['portrait', 'landscape']} onRequestClose={() => { setIsRenameModalVisible(false); setCatalogToRename(null); diff --git a/src/screens/PluginsScreen.tsx b/src/screens/PluginsScreen.tsx index f7e6cd1..f0bf46d 100644 --- a/src/screens/PluginsScreen.tsx +++ b/src/screens/PluginsScreen.tsx @@ -1946,6 +1946,7 @@ const PluginsScreen: React.FC = () => { visible={showHelpModal} transparent={true} animationType="fade" + supportedOrientations={['portrait', 'landscape']} onRequestClose={() => setShowHelpModal(false)} > @@ -1978,6 +1979,7 @@ const PluginsScreen: React.FC = () => { visible={showAddRepositoryModal} transparent={true} animationType="fade" + supportedOrientations={['portrait', 'landscape']} onRequestClose={() => setShowAddRepositoryModal(false)} > diff --git a/src/screens/ProfilesScreen.tsx b/src/screens/ProfilesScreen.tsx index f97e623..56a7dbb 100644 --- a/src/screens/ProfilesScreen.tsx +++ b/src/screens/ProfilesScreen.tsx @@ -33,7 +33,7 @@ const ProfilesScreen: React.FC = () => { const navigation = useNavigation(); const { currentTheme } = useTheme(); const { isAuthenticated, userProfile, refreshAuthStatus } = useTraktContext(); - + const [profiles, setProfiles] = useState([]); const [showAddModal, setShowAddModal] = useState(false); const [newProfileName, setNewProfileName] = useState(''); @@ -52,7 +52,7 @@ const ProfilesScreen: React.FC = () => { ) => { setAlertTitle(title); setAlertMessage(message); - setAlertActions(actions && actions.length > 0 ? actions : [{ label: 'OK', onPress: () => {} }]); + setAlertActions(actions && actions.length > 0 ? actions : [{ label: 'OK', onPress: () => { } }]); setAlertVisible(true); }; @@ -92,7 +92,7 @@ const ProfilesScreen: React.FC = () => { } }); }); - + return unsubscribe; }, [navigation, refreshAuthStatus, isAuthenticated, loadProfiles]); @@ -112,7 +112,7 @@ const ProfilesScreen: React.FC = () => { navigation.goBack(); return; } - + loadProfiles(); }, [isAuthenticated, loadProfiles, navigation]); @@ -141,7 +141,7 @@ const ProfilesScreen: React.FC = () => { ...profile, isActive: profile.id === id })); - + setProfiles(updatedProfiles); saveProfiles(updatedProfiles); }, [profiles, saveProfiles]); @@ -164,14 +164,14 @@ const ProfilesScreen: React.FC = () => { 'Delete Profile', 'Are you sure you want to delete this profile? This action cannot be undone.', [ - { label: 'Cancel', onPress: () => {} }, - { - label: 'Delete', + { label: 'Cancel', onPress: () => { } }, + { + label: 'Delete', onPress: () => { const updatedProfiles = profiles.filter(profile => profile.id !== id); setProfiles(updatedProfiles); saveProfiles(updatedProfiles); - } + } } ] ); @@ -183,10 +183,10 @@ const ProfilesScreen: React.FC = () => { const renderItem = ({ item }: { item: Profile }) => ( - { onPress={() => handleSelectProfile(item.id)} > - @@ -211,7 +211,7 @@ const ProfilesScreen: React.FC = () => { )} {!item.isActive && ( - handleDeleteProfile(item.id)} > @@ -225,7 +225,7 @@ const ProfilesScreen: React.FC = () => { return ( - + { visible={showAddModal} transparent animationType="fade" + supportedOrientations={['portrait', 'landscape']} onRequestClose={() => setShowAddModal(false)} > @@ -288,11 +289,11 @@ const ProfilesScreen: React.FC = () => { Create New Profile - + { onChangeText={setNewProfileName} autoFocus /> - + - { setNewProfileName(''); @@ -315,9 +316,9 @@ const ProfilesScreen: React.FC = () => { > Cancel - { const { showSuccess, showInfo } = useToast(); // Add dimension listener and tablet detection + // Use a ref to track previous dimensions to avoid unnecessary re-renders const [dimensions, setDimensions] = useState(Dimensions.get('window')); + const prevDimensionsRef = useRef({ width: dimensions.width, height: dimensions.height }); useEffect(() => { const subscription = Dimensions.addEventListener('change', ({ window }) => { - setDimensions(window); + // Only update state if dimensions actually changed (with 1px tolerance) + const widthChanged = Math.abs(window.width - prevDimensionsRef.current.width) > 1; + const heightChanged = Math.abs(window.height - prevDimensionsRef.current.height) > 1; + + if (widthChanged || heightChanged) { + prevDimensionsRef.current = { width: window.width, height: window.height }; + setDimensions(window); + } }); return () => subscription?.remove(); }, []); + // Memoize tablet detection to prevent recalculation on every render const deviceWidth = dimensions.width; - const isTablet = deviceWidth >= 768; + const isTablet = useMemo(() => deviceWidth >= 768, [deviceWidth]); // Add refs to prevent excessive updates and duplicate loads const isMounted = useRef(true); @@ -303,6 +313,9 @@ export const StreamsScreen = () => { }, []); // Monitor streams loading and update available providers immediately + // Use a ref to track the previous providers to avoid unnecessary state updates + const prevProvidersRef = useRef>(new Set()); + useEffect(() => { // Skip processing if component is unmounting if (!isMounted.current) return; @@ -317,14 +330,21 @@ export const StreamsScreen = () => { if (providersWithStreams.length > 0) { logger.log(`📊 Providers with streams: ${providersWithStreams.join(', ')}`); - const providersWithStreamsSet = new Set(providersWithStreams); - // Only update if we have new providers, don't remove existing ones during loading - setAvailableProviders(prevProviders => { - const newProviders = new Set([...prevProviders, ...providersWithStreamsSet]); - if (__DEV__) console.log('[StreamsScreen] availableProviders ->', Array.from(newProviders)); - return newProviders; - }); + // Check if we actually have new providers before triggering state update + const hasNewProviders = providersWithStreams.some( + provider => !prevProvidersRef.current.has(provider) + ); + + if (hasNewProviders) { + setAvailableProviders(prevProviders => { + const newProviders = new Set([...prevProviders, ...providersWithStreams]); + // Update ref to track current providers + prevProvidersRef.current = newProviders; + if (__DEV__) console.log('[StreamsScreen] availableProviders ->', Array.from(newProviders)); + return newProviders; + }); + } } // Update loading states for individual providers diff --git a/src/screens/TMDBSettingsScreen.tsx b/src/screens/TMDBSettingsScreen.tsx index e39e93a..bef2ad1 100644 --- a/src/screens/TMDBSettingsScreen.tsx +++ b/src/screens/TMDBSettingsScreen.tsx @@ -36,27 +36,27 @@ const TMDB_API_KEY = '439c478a771f35c05022f9feabcca01c'; // Define example shows with their IMDB IDs and TMDB IDs const EXAMPLE_SHOWS = [ - { - name: 'Breaking Bad', - imdbId: 'tt0903747', + { + name: 'Breaking Bad', + imdbId: 'tt0903747', tmdbId: '1396', type: 'tv' as const }, - { - name: 'Friends', - imdbId: 'tt0108778', + { + name: 'Friends', + imdbId: 'tt0108778', tmdbId: '1668', type: 'tv' as const }, - { - name: 'Stranger Things', - imdbId: 'tt4574334', + { + name: 'Stranger Things', + imdbId: 'tt4574334', tmdbId: '66732', type: 'tv' as const }, - { - name: 'Avatar', - imdbId: 'tt0499549', + { + name: 'Avatar', + imdbId: 'tt0499549', tmdbId: '19995', type: 'movie' as const }, @@ -82,7 +82,7 @@ const TMDBSettingsScreen = () => { const { settings, updateSetting } = useSettings(); const [languagePickerVisible, setLanguagePickerVisible] = useState(false); const [languageSearch, setLanguageSearch] = useState(''); - + // Logo preview state const [selectedShow, setSelectedShow] = useState(EXAMPLE_SHOWS[0]); const [tmdbLogo, setTmdbLogo] = useState(null); @@ -126,7 +126,7 @@ const TMDBSettingsScreen = () => { try { const keys = await mmkvStorage.getAllKeys(); const tmdbKeys = keys.filter(key => key.startsWith('tmdb_cache_')); - + let totalSize = 0; for (const key of tmdbKeys) { const value = mmkvStorage.getString(key); @@ -134,7 +134,7 @@ const TMDBSettingsScreen = () => { totalSize += value.length; } } - + // Convert to KB/MB let sizeStr = ''; if (totalSize < 1024) { @@ -144,7 +144,7 @@ const TMDBSettingsScreen = () => { } else { sizeStr = `${(totalSize / (1024 * 1024)).toFixed(2)} MB`; } - + setCacheSize(sizeStr); } catch (error) { logger.error('[TMDBSettingsScreen] Error calculating cache size:', error); @@ -187,17 +187,17 @@ const TMDBSettingsScreen = () => { mmkvStorage.getItem(TMDB_API_KEY_STORAGE_KEY), mmkvStorage.getItem(USE_CUSTOM_TMDB_API_KEY) ]); - + logger.log('[TMDBSettingsScreen] API key status:', savedKey ? 'Found' : 'Not found'); logger.log('[TMDBSettingsScreen] Use custom API setting:', savedUseCustomKey); - + if (savedKey) { setApiKey(savedKey); setIsKeySet(true); } else { setIsKeySet(false); } - + setUseCustomKey(savedUseCustomKey === 'true'); } catch (error) { logger.error('[TMDBSettingsScreen] Failed to load settings:', error); @@ -212,7 +212,7 @@ const TMDBSettingsScreen = () => { const saveApiKey = async () => { logger.log('[TMDBSettingsScreen] Starting API key save'); Keyboard.dismiss(); - + try { const trimmedKey = apiKey.trim(); if (!trimmedKey) { @@ -299,27 +299,27 @@ const TMDBSettingsScreen = () => { try { await mmkvStorage.setItem(USE_CUSTOM_TMDB_API_KEY, value ? 'true' : 'false'); setUseCustomKey(value); - + if (!value) { // If switching to built-in key, show confirmation logger.log('[TMDBSettingsScreen] Switching to built-in API key'); - setTestResult({ - success: true, - message: 'Now using the built-in TMDb API key.' + setTestResult({ + success: true, + message: 'Now using the built-in TMDb API key.' }); } else if (apiKey && isKeySet) { // If switching to custom key and we have a key logger.log('[TMDBSettingsScreen] Switching to custom API key'); - setTestResult({ - success: true, - message: 'Now using your custom TMDb API key.' + setTestResult({ + success: true, + message: 'Now using your custom TMDb API key.' }); } else { // If switching to custom key but don't have a key yet logger.log('[TMDBSettingsScreen] No custom key available yet'); - setTestResult({ - success: false, - message: 'Please enter and save your custom TMDb API key.' + setTestResult({ + success: false, + message: 'Please enter and save your custom TMDb API key.' }); } } catch (error) { @@ -355,27 +355,27 @@ const TMDBSettingsScreen = () => { setLoadingLogos(true); setTmdbLogo(null); setTmdbBanner(null); - + try { const tmdbId = show.tmdbId; const contentType = show.type; - + logger.log(`[TMDBSettingsScreen] Fetching ${show.name} with TMDB ID: ${tmdbId}`); - + const preferredTmdbLanguage = settings.tmdbLanguagePreference || 'en'; - + const apiKey = TMDB_API_KEY; const endpoint = contentType === 'tv' ? 'tv' : 'movie'; const response = await fetch(`https://api.themoviedb.org/3/${endpoint}/${tmdbId}/images?api_key=${apiKey}`); const imagesData = await response.json(); - + if (imagesData.logos && imagesData.logos.length > 0) { let logoPath: string | null = null; let logoLanguage = preferredTmdbLanguage; - + // Try to find logo in preferred language const preferredLogo = imagesData.logos.find((logo: { iso_639_1: string; file_path: string }) => logo.iso_639_1 === preferredTmdbLanguage); - + if (preferredLogo) { logoPath = preferredLogo.file_path; logoLanguage = preferredTmdbLanguage; @@ -383,7 +383,7 @@ const TMDBSettingsScreen = () => { } else { // Fallback to English const englishLogo = imagesData.logos.find((logo: { iso_639_1: string; file_path: string }) => logo.iso_639_1 === 'en'); - + if (englishLogo) { logoPath = englishLogo.file_path; logoLanguage = 'en'; @@ -395,7 +395,7 @@ const TMDBSettingsScreen = () => { setIsPreviewFallback(true); } } - + if (logoPath) { setTmdbLogo(`https://image.tmdb.org/t/p/original${logoPath}`); setPreviewLanguage(logoLanguage); @@ -407,7 +407,7 @@ const TMDBSettingsScreen = () => { setPreviewLanguage(''); setIsPreviewFallback(false); } - + // Get TMDB banner (backdrop) if (imagesData.backdrops && imagesData.backdrops.length > 0) { const backdropPath = imagesData.backdrops[0].file_path; @@ -415,7 +415,7 @@ const TMDBSettingsScreen = () => { } else { const detailsResponse = await fetch(`https://api.themoviedb.org/3/${endpoint}/${tmdbId}?api_key=${apiKey}`); const details = await detailsResponse.json(); - + if (details.backdrop_path) { setTmdbBanner(`https://image.tmdb.org/t/p/original${details.backdrop_path}`); } @@ -444,17 +444,17 @@ const TMDBSettingsScreen = () => { ); } - + return ( - {logo && ( - { if (__DEV__) console.error('Error loading selected show:', e); } }; - + loadSelectedShow(); }, []); @@ -512,7 +512,7 @@ const TMDBSettingsScreen = () => { } return ( - + @@ -520,7 +520,7 @@ const TMDBSettingsScreen = () => { style={styles.backButton} onPress={() => navigation.goBack()} > - + Settings @@ -602,7 +602,7 @@ const TMDBSettingsScreen = () => { {/* Logo Preview */} - + Logo Preview Preview shows how localized logos will appear in the selected language. @@ -610,8 +610,8 @@ const TMDBSettingsScreen = () => { {/* Show selector */} Example: - { onPress={() => handleShowSelect(show)} activeOpacity={0.7} > - { {/* Cache Management Section */} - + Cache Size @@ -828,6 +828,7 @@ const TMDBSettingsScreen = () => { visible={languagePickerVisible} transparent animationType="slide" + supportedOrientations={['portrait', 'landscape']} onRequestClose={() => setLanguagePickerVisible(false)} > setLanguagePickerVisible(false)}> @@ -955,42 +956,42 @@ const TMDBSettingsScreen = () => { return ( <> {filteredLanguages.map(({ code, label, native }) => ( - { updateSetting('tmdbLanguagePreference', code); setLanguagePickerVisible(false); }} - style={[ - styles.languageItem, - settings.tmdbLanguagePreference === code && styles.selectedLanguageItem - ]} - activeOpacity={0.7} - > - - - - {native} - - - {label} • {code.toUpperCase()} - - - {settings.tmdbLanguagePreference === code && ( - - - - )} - + { updateSetting('tmdbLanguagePreference', code); setLanguagePickerVisible(false); }} + style={[ + styles.languageItem, + settings.tmdbLanguagePreference === code && styles.selectedLanguageItem + ]} + activeOpacity={0.7} + > + + + + {native} + + + {label} • {code.toUpperCase()} + + + {settings.tmdbLanguagePreference === code && ( + + + + )} + ))} {languageSearch.length > 0 && filteredLanguages.length === 0 && (