From eec51f787c3100402e3280b805c88ca4cbf93d71 Mon Sep 17 00:00:00 2001 From: YagUber Date: Mon, 13 Apr 2026 18:08:07 -0500 Subject: [PATCH] fix(settings): extract language picker and resolve undefined display on first launch --- src/constants/locales.ts | 54 +++--- src/screens/SettingsScreen.tsx | 172 +------------------ src/screens/settings/LanguageSettingItem.tsx | 151 ++++++++++++++++ 3 files changed, 182 insertions(+), 195 deletions(-) create mode 100644 src/screens/settings/LanguageSettingItem.tsx diff --git a/src/constants/locales.ts b/src/constants/locales.ts index 905d12ee..648e8281 100644 --- a/src/constants/locales.ts +++ b/src/constants/locales.ts @@ -1,29 +1,29 @@ export const LOCALES = [ - { code: 'en', key: 'english' }, - { code: 'pt-BR', key: 'portuguese_br' }, - { code: 'pt-PT', key: 'portuguese_pt' }, - { code: 'de', key: 'german' }, - { code: 'ar', key: 'arabic' }, - { code: 'fr', key: 'french' }, - { code: 'it', key: 'italian' }, - { code: 'es', key: 'spanish' }, - { code: 'hr', key: 'croatian' }, - { code: 'zh-CN', key: 'chinese' }, - { code: 'hi', key: 'hindi' }, - { code: 'sr', key: 'serbian' }, - { code: 'he', key: 'hebrew' }, - { code: 'bg', key: 'bulgarian' }, - { code: 'pl', key: 'polish' }, - { code: 'cs', key: 'czech' }, - { code: 'tr', key: 'turkish' }, - { code: 'sl', key: 'slovenian' }, - { code: 'mk', key: 'macedonian' }, - { code: 'ru', key: 'russian' }, - { code: 'fil', key: 'filipino' }, - { code: 'nl-NL', key: 'dutch_nl' }, - { code: 'ro', key: 'romanian' }, - { code: 'sq', key: 'albanian' }, - { code: 'ca', key: 'catalan' }, - { code: 'vi', key: 'vietnamese' }, - { code: 'ja', key: 'japanese' } + { code: 'en', key: 'english', name: 'English' }, + { code: 'pt-BR', key: 'portuguese_br', name: 'Português (BR)' }, + { code: 'pt-PT', key: 'portuguese_pt', name: 'Português (PT)' }, + { code: 'de', key: 'german', name: 'Deutsch' }, + { code: 'ar', key: 'arabic', name: 'العربية' }, + { code: 'fr', key: 'french', name: 'Français' }, + { code: 'it', key: 'italian', name: 'Italiano' }, + { code: 'es', key: 'spanish', name: 'Español' }, + { code: 'hr', key: 'croatian', name: 'Hrvatski' }, + { code: 'zh-CN', key: 'chinese', name: '中文(简体)' }, + { code: 'hi', key: 'hindi', name: 'हिन्दी' }, + { code: 'sr', key: 'serbian', name: 'Srpski' }, + { code: 'he', key: 'hebrew', name: 'עברית' }, + { code: 'bg', key: 'bulgarian', name: 'Български' }, + { code: 'pl', key: 'polish', name: 'Polski' }, + { code: 'cs', key: 'czech', name: 'Čeština' }, + { code: 'tr', key: 'turkish', name: 'Türkçe' }, + { code: 'sl', key: 'slovenian', name: 'Slovenščina' }, + { code: 'mk', key: 'macedonian', name: 'Македонски' }, + { code: 'ru', key: 'russian', name: 'Русский' }, + { code: 'fil', key: 'filipino', name: 'Filipino' }, + { code: 'nl-NL', key: 'dutch_nl', name: 'Nederlands' }, + { code: 'ro', key: 'romanian', name: 'Română' }, + { code: 'sq', key: 'albanian', name: 'Shqip' }, + { code: 'ca', key: 'catalan', name: 'Català' }, + { code: 'vi', key: 'vietnamese', name: 'Tiếng Việt' }, + { code: 'ja', key: 'japanese', name: '日本語' }, ]; diff --git a/src/screens/SettingsScreen.tsx b/src/screens/SettingsScreen.tsx index eee4857a..f87e2d1b 100644 --- a/src/screens/SettingsScreen.tsx +++ b/src/screens/SettingsScreen.tsx @@ -14,7 +14,7 @@ import { FlatList, Image, } from 'react-native'; -import { BottomSheetModal, BottomSheetView, BottomSheetBackdrop, BottomSheetScrollView } from '@gorhom/bottom-sheet'; +import { BottomSheetModal, BottomSheetBackdrop } from '@gorhom/bottom-sheet'; import { useTranslation } from 'react-i18next'; import { mmkvStorage } from '../services/mmkvStorage'; import { useNavigation } from '@react-navigation/native'; @@ -46,8 +46,7 @@ import { IntegrationsSettingsContent } from './settings/IntegrationsSettingsScre import { AboutSettingsContent, AboutFooter } from './settings/AboutSettingsScreen'; import { PrivacySettingsContent } from './settings/PrivacySettingsScreen'; import { SettingsCard, SettingItem, ChevronRight, CustomSwitch } from './settings/SettingsComponents'; -import { useBottomSheetBackHandler } from '../hooks/useBottomSheetBackHandler'; -import { LOCALES } from '../constants/locales'; +import { LanguageSettingItem } from './settings/LanguageSettingItem'; import { useSimklIntegration } from '../hooks/useSimklIntegration'; import SimklIcon from '../components/icons/SimklIcon'; @@ -156,8 +155,6 @@ const SettingsScreen: React.FC = () => { ]; const { settings, updateSetting } = useSettings(); const [hasUpdateBadge, setHasUpdateBadge] = useState(false); - const languageSheetRef = useRef(null); - const { onChange, onDismiss } = useBottomSheetBackHandler(); const insets = useSafeAreaInsets(); // Render backdrop for bottom sheet @@ -439,15 +436,7 @@ const SettingsScreen: React.FC = () => { return ( <> - l.code === i18n.language)?.key}`)} - icon="globe" - renderControl={() => } - onPress={() => languageSheetRef.current?.present()} - isLast={true} - isTablet={isTablet} - /> + @@ -637,64 +626,6 @@ const SettingsScreen: React.FC = () => { onClose={() => setAlertVisible(false)} /> - - - - {t('settings.select_language')} - - languageSheetRef.current?.dismiss()}> - - - - - { - LOCALES.sort((a, b) => a.key.localeCompare(b.key)).map(l => - { - i18n.changeLanguage(l.code); - languageSheetRef.current?.dismiss(); - }} - > - - {t(`settings.${l.key}`)} - - {i18n.language === l.code && ( - - )} - - ) - } - - ); } @@ -772,14 +703,7 @@ const SettingsScreen: React.FC = () => { (settingsConfig?.categories?.['playback']?.visible !== false) ) && ( - l.code === i18n.language)?.key}`) - } - icon="globe" - renderControl={() => } - onPress={() => languageSheetRef.current?.present()} - /> + {(settingsConfig?.categories?.['content']?.visible !== false) && ( { onClose={() => setAlertVisible(false)} /> - - - - {t('settings.select_language')} - - languageSheetRef.current?.dismiss()}> - - - - - { - LOCALES.sort((a, b) => a.key.localeCompare(b.key)).map(l => - { - i18n.changeLanguage(l.code); - languageSheetRef.current?.dismiss(); - }} - > - - {t(`settings.${l.key}`)} - - {i18n.language === l.code && ( - - )} - - ) - } - - ); }; @@ -1043,36 +909,6 @@ const styles = StyleSheet.create({ actionSheetContent: { flex: 1, }, - bottomSheetHeader: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - paddingHorizontal: 20, - paddingVertical: 16, - borderBottomWidth: 1, - borderBottomColor: 'rgba(255,255,255,0.1)', - }, - bottomSheetTitle: { - fontSize: 18, - fontWeight: '600', - }, - bottomSheetContent: { - paddingHorizontal: 16, - paddingTop: 8, - paddingBottom: 24, - }, - languageOption: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - paddingVertical: 14, - paddingHorizontal: 16, - borderRadius: 8, - marginBottom: 8, - }, - languageText: { - fontSize: 16, - }, // Mobile styles contentContainer: { flex: 1, diff --git a/src/screens/settings/LanguageSettingItem.tsx b/src/screens/settings/LanguageSettingItem.tsx new file mode 100644 index 00000000..a167a08e --- /dev/null +++ b/src/screens/settings/LanguageSettingItem.tsx @@ -0,0 +1,151 @@ +import React, { useCallback, useRef } from 'react'; +import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'; +import { BottomSheetBackdrop, BottomSheetModal, BottomSheetScrollView } from '@gorhom/bottom-sheet'; +import { Feather } from '@expo/vector-icons'; +import { useTranslation } from 'react-i18next'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { useTheme } from '../../contexts/ThemeContext'; +import { useBottomSheetBackHandler } from '../../hooks/useBottomSheetBackHandler'; +import { ChevronRight, SettingItem } from './SettingsComponents'; +import { LOCALES } from '../../constants/locales'; + +const SORTED_LOCALES = [...LOCALES].sort((a, b) => a.name.localeCompare(b.name)); + +interface LanguageSettingItemProps { + isTablet?: boolean; + isLast?: boolean; +} + +export const LanguageSettingItem: React.FC = ({ isTablet = false, isLast = false }) => { + const { t, i18n } = useTranslation(); + const { currentTheme } = useTheme(); + const insets = useSafeAreaInsets(); + const sheetRef = useRef(null); + const { onChange, onDismiss } = useBottomSheetBackHandler(); + + const currentLocale = + LOCALES.find(l => l.code === i18n.language) ?? + LOCALES.find(l => l.code === i18n.language?.split('-')[0]); + + const renderBackdrop = useCallback( + (props: any) => ( + + ), + [] + ); + + return ( + <> + } + onPress={() => sheetRef.current?.present()} + isLast={isLast} + isTablet={isTablet} + /> + + + + + {t('settings.select_language')} + + sheetRef.current?.dismiss()}> + + + + + {SORTED_LOCALES.map(l => { + const isSelected = i18n.language === l.code || + i18n.language?.split('-')[0] === l.code; + return ( + { + i18n.changeLanguage(l.code); + sheetRef.current?.dismiss(); + }} + > + + {l.name} + + + {l.code.toUpperCase()} + + {isSelected && ( + + )} + + ); + })} + + + + ); +}; + +const styles = StyleSheet.create({ + sheetHeader: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingHorizontal: 20, + paddingVertical: 16, + borderBottomWidth: 1, + borderBottomColor: 'rgba(255,255,255,0.1)', + }, + sheetTitle: { + fontSize: 18, + fontWeight: '600', + }, + sheetContent: { + paddingHorizontal: 16, + paddingTop: 8, + paddingBottom: 24, + }, + languageOption: { + flexDirection: 'row', + alignItems: 'center', + paddingVertical: 14, + paddingHorizontal: 16, + borderRadius: 8, + marginBottom: 8, + }, + languageName: { + flex: 1, + fontSize: 16, + }, + languageCode: { + fontSize: 12, + marginRight: 8, + }, +});