mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-17 07:21:58 +00:00
fix(settings): extract language picker and resolve undefined display on first launch
This commit is contained in:
parent
cbc9fc4fa6
commit
eec51f787c
3 changed files with 182 additions and 195 deletions
|
|
@ -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: '日本語' },
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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<BottomSheetModal>(null);
|
||||
const { onChange, onDismiss } = useBottomSheetBackHandler();
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
// Render backdrop for bottom sheet
|
||||
|
|
@ -439,15 +436,7 @@ const SettingsScreen: React.FC = () => {
|
|||
return (
|
||||
<>
|
||||
<SettingsCard title="GENERAL" isTablet={isTablet}>
|
||||
<SettingItem
|
||||
title={t('settings.language')}
|
||||
description={t(`settings.${LOCALES.find(l => l.code === i18n.language)?.key}`)}
|
||||
icon="globe"
|
||||
renderControl={() => <ChevronRight />}
|
||||
onPress={() => languageSheetRef.current?.present()}
|
||||
isLast={true}
|
||||
isTablet={isTablet}
|
||||
/>
|
||||
<LanguageSettingItem isTablet={isTablet} isLast />
|
||||
</SettingsCard>
|
||||
<AppearanceSettingsContent isTablet={isTablet} />
|
||||
</>
|
||||
|
|
@ -637,64 +626,6 @@ const SettingsScreen: React.FC = () => {
|
|||
onClose={() => setAlertVisible(false)}
|
||||
/>
|
||||
|
||||
<BottomSheetModal
|
||||
ref={languageSheetRef}
|
||||
index={0}
|
||||
snapPoints={['65%']}
|
||||
enablePanDownToClose={true}
|
||||
backdropComponent={renderBackdrop}
|
||||
backgroundStyle={{
|
||||
backgroundColor: currentTheme.colors.darkGray || '#0A0C0C',
|
||||
borderTopLeftRadius: 16,
|
||||
borderTopRightRadius: 16,
|
||||
}}
|
||||
handleIndicatorStyle={{
|
||||
backgroundColor: currentTheme.colors.mediumGray,
|
||||
width: 40,
|
||||
}}
|
||||
onChange={onChange(languageSheetRef)}
|
||||
onDismiss={onDismiss(languageSheetRef)}
|
||||
>
|
||||
<View style={[styles.bottomSheetHeader, { backgroundColor: currentTheme.colors.darkGray || '#0A0C0C' }]}>
|
||||
<Text style={[styles.bottomSheetTitle, { color: currentTheme.colors.white }]}>
|
||||
{t('settings.select_language')}
|
||||
</Text>
|
||||
<TouchableOpacity onPress={() => languageSheetRef.current?.dismiss()}>
|
||||
<Feather name="x" size={24} color={currentTheme.colors.lightGray} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<BottomSheetScrollView
|
||||
style={{ backgroundColor: currentTheme.colors.darkGray || '#0A0C0C' }}
|
||||
contentContainerStyle={[styles.bottomSheetContent, { paddingBottom: insets.bottom + 16 }]}
|
||||
>
|
||||
{
|
||||
LOCALES.sort((a, b) => a.key.localeCompare(b.key)).map(l =>
|
||||
<TouchableOpacity
|
||||
key={l.key}
|
||||
style={[
|
||||
styles.languageOption,
|
||||
i18n.language === l.code && { backgroundColor: currentTheme.colors.primary + '20' }
|
||||
]}
|
||||
onPress={() => {
|
||||
i18n.changeLanguage(l.code);
|
||||
languageSheetRef.current?.dismiss();
|
||||
}}
|
||||
>
|
||||
<Text style={[
|
||||
styles.languageText,
|
||||
{ color: currentTheme.colors.highEmphasis },
|
||||
i18n.language === l.code && { color: currentTheme.colors.primary, fontWeight: 'bold' }
|
||||
]}>
|
||||
{t(`settings.${l.key}`)}
|
||||
</Text>
|
||||
{i18n.language === l.code && (
|
||||
<Feather name="check" size={20} color={currentTheme.colors.primary} />
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
)
|
||||
}
|
||||
</BottomSheetScrollView>
|
||||
</BottomSheetModal>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
|
@ -772,14 +703,7 @@ const SettingsScreen: React.FC = () => {
|
|||
(settingsConfig?.categories?.['playback']?.visible !== false)
|
||||
) && (
|
||||
<SettingsCard title="GENERAL">
|
||||
<SettingItem
|
||||
title={t('settings.language')}
|
||||
description={t(`settings.${LOCALES.find(l => l.code === i18n.language)?.key}`)
|
||||
}
|
||||
icon="globe"
|
||||
renderControl={() => <ChevronRight />}
|
||||
onPress={() => languageSheetRef.current?.present()}
|
||||
/>
|
||||
<LanguageSettingItem />
|
||||
{(settingsConfig?.categories?.['content']?.visible !== false) && (
|
||||
<SettingItem
|
||||
title={t('settings.content_discovery')}
|
||||
|
|
@ -974,64 +898,6 @@ const SettingsScreen: React.FC = () => {
|
|||
onClose={() => setAlertVisible(false)}
|
||||
/>
|
||||
|
||||
<BottomSheetModal
|
||||
ref={languageSheetRef}
|
||||
index={0}
|
||||
snapPoints={['65%']}
|
||||
enablePanDownToClose={true}
|
||||
backdropComponent={renderBackdrop}
|
||||
backgroundStyle={{
|
||||
backgroundColor: currentTheme.colors.darkGray || '#0A0C0C',
|
||||
borderTopLeftRadius: 16,
|
||||
borderTopRightRadius: 16,
|
||||
}}
|
||||
handleIndicatorStyle={{
|
||||
backgroundColor: currentTheme.colors.mediumGray,
|
||||
width: 40,
|
||||
}}
|
||||
onChange={onChange(languageSheetRef)}
|
||||
onDismiss={onDismiss(languageSheetRef)}
|
||||
>
|
||||
<View style={[styles.bottomSheetHeader, { backgroundColor: currentTheme.colors.darkGray || '#0A0C0C' }]}>
|
||||
<Text style={[styles.bottomSheetTitle, { color: currentTheme.colors.white }]}>
|
||||
{t('settings.select_language')}
|
||||
</Text>
|
||||
<TouchableOpacity onPress={() => languageSheetRef.current?.dismiss()}>
|
||||
<Feather name="x" size={24} color={currentTheme.colors.lightGray} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<BottomSheetScrollView
|
||||
style={{ backgroundColor: currentTheme.colors.darkGray || '#0A0C0C' }}
|
||||
contentContainerStyle={[styles.bottomSheetContent, { paddingBottom: insets.bottom + 16 }]}
|
||||
>
|
||||
{
|
||||
LOCALES.sort((a, b) => a.key.localeCompare(b.key)).map(l =>
|
||||
<TouchableOpacity
|
||||
key={l.key}
|
||||
style={[
|
||||
styles.languageOption,
|
||||
i18n.language === l.code && { backgroundColor: currentTheme.colors.primary + '20' }
|
||||
]}
|
||||
onPress={() => {
|
||||
i18n.changeLanguage(l.code);
|
||||
languageSheetRef.current?.dismiss();
|
||||
}}
|
||||
>
|
||||
<Text style={[
|
||||
styles.languageText,
|
||||
{ color: currentTheme.colors.highEmphasis },
|
||||
i18n.language === l.code && { color: currentTheme.colors.primary, fontWeight: 'bold' }
|
||||
]}>
|
||||
{t(`settings.${l.key}`)}
|
||||
</Text>
|
||||
{i18n.language === l.code && (
|
||||
<Feather name="check" size={20} color={currentTheme.colors.primary} />
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
)
|
||||
}
|
||||
</BottomSheetScrollView>
|
||||
</BottomSheetModal>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
151
src/screens/settings/LanguageSettingItem.tsx
Normal file
151
src/screens/settings/LanguageSettingItem.tsx
Normal file
|
|
@ -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<LanguageSettingItemProps> = ({ isTablet = false, isLast = false }) => {
|
||||
const { t, i18n } = useTranslation();
|
||||
const { currentTheme } = useTheme();
|
||||
const insets = useSafeAreaInsets();
|
||||
const sheetRef = useRef<BottomSheetModal>(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) => (
|
||||
<BottomSheetBackdrop {...props} disappearsOnIndex={-1} appearsOnIndex={0} opacity={0.5} />
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingItem
|
||||
title={t('settings.language')}
|
||||
description={currentLocale?.name}
|
||||
icon="globe"
|
||||
renderControl={() => <ChevronRight />}
|
||||
onPress={() => sheetRef.current?.present()}
|
||||
isLast={isLast}
|
||||
isTablet={isTablet}
|
||||
/>
|
||||
|
||||
<BottomSheetModal
|
||||
ref={sheetRef}
|
||||
index={0}
|
||||
snapPoints={['65%']}
|
||||
enablePanDownToClose
|
||||
backdropComponent={renderBackdrop}
|
||||
backgroundStyle={{
|
||||
backgroundColor: currentTheme.colors.darkGray || '#0A0C0C',
|
||||
borderTopLeftRadius: 16,
|
||||
borderTopRightRadius: 16,
|
||||
}}
|
||||
handleIndicatorStyle={{
|
||||
backgroundColor: currentTheme.colors.mediumGray,
|
||||
width: 40,
|
||||
}}
|
||||
onChange={onChange(sheetRef)}
|
||||
onDismiss={onDismiss(sheetRef)}
|
||||
>
|
||||
<View style={[styles.sheetHeader, { backgroundColor: currentTheme.colors.darkGray || '#0A0C0C' }]}>
|
||||
<Text style={[styles.sheetTitle, { color: currentTheme.colors.white }]}>
|
||||
{t('settings.select_language')}
|
||||
</Text>
|
||||
<TouchableOpacity onPress={() => sheetRef.current?.dismiss()}>
|
||||
<Feather name="x" size={24} color={currentTheme.colors.lightGray} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<BottomSheetScrollView
|
||||
style={{ backgroundColor: currentTheme.colors.darkGray || '#0A0C0C' }}
|
||||
contentContainerStyle={[styles.sheetContent, { paddingBottom: insets.bottom + 16 }]}
|
||||
>
|
||||
{SORTED_LOCALES.map(l => {
|
||||
const isSelected = i18n.language === l.code ||
|
||||
i18n.language?.split('-')[0] === l.code;
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={l.code}
|
||||
style={[
|
||||
styles.languageOption,
|
||||
isSelected && { backgroundColor: currentTheme.colors.primary + '20' },
|
||||
]}
|
||||
onPress={() => {
|
||||
i18n.changeLanguage(l.code);
|
||||
sheetRef.current?.dismiss();
|
||||
}}
|
||||
>
|
||||
<Text style={[
|
||||
styles.languageName,
|
||||
{ color: isSelected ? currentTheme.colors.primary : currentTheme.colors.highEmphasis },
|
||||
isSelected && { fontWeight: 'bold' },
|
||||
]}>
|
||||
{l.name}
|
||||
</Text>
|
||||
<Text style={[styles.languageCode, { color: currentTheme.colors.mediumEmphasis }]}>
|
||||
{l.code.toUpperCase()}
|
||||
</Text>
|
||||
{isSelected && (
|
||||
<Feather name="check" size={20} color={currentTheme.colors.primary} />
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
})}
|
||||
</BottomSheetScrollView>
|
||||
</BottomSheetModal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
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,
|
||||
},
|
||||
});
|
||||
Loading…
Reference in a new issue