added localization to themescreen.

This commit is contained in:
tapframe 2026-01-07 12:44:42 +05:30
parent 9b2b619121
commit a85698b009
7 changed files with 395 additions and 200 deletions

View file

@ -1193,5 +1193,35 @@
"repo_format_hint": "التنسيق: https://raw.githubusercontent.com/username/repo/refs/heads/branch", "repo_format_hint": "التنسيق: https://raw.githubusercontent.com/username/repo/refs/heads/branch",
"cancel": "إلغاء", "cancel": "إلغاء",
"add": "إضافة" "add": "إضافة"
},
"theme": {
"title": "سمات التطبيق",
"select_theme": "اختر السمة",
"create_custom": "إنشاء سمة مخصصة",
"options": "خيارات",
"use_dominant_color": "استخدام اللون المهيمن من العمل الفني",
"categories": {
"all": "كل السمات",
"dark": "سمات داكنة",
"colorful": "ملونة",
"custom": "سماتي"
},
"editor": {
"theme_name_placeholder": "اسم السمة",
"save": "حفظ",
"primary": "أساسي",
"secondary": "ثانوي",
"background": "خلفية",
"invalid_name_title": "اسم غير صالح",
"invalid_name_msg": "يرجى إدخال اسم سمة صالح"
},
"alerts": {
"delete_title": "حذف السمة",
"delete_msg": "هل أنت متأكد أنك تريد حذف \"{{name}}\"؟",
"ok": "حسناً",
"delete": "حذف",
"cancel": "إلغاء",
"back": "إعدادات"
}
} }
} }

View file

@ -1193,5 +1193,35 @@
"repo_format_hint": "Format: https://raw.githubusercontent.com/username/repo/refs/heads/branch", "repo_format_hint": "Format: https://raw.githubusercontent.com/username/repo/refs/heads/branch",
"cancel": "Cancel", "cancel": "Cancel",
"add": "Add" "add": "Add"
},
"theme": {
"title": "App Themes",
"select_theme": "SELECT THEME",
"create_custom": "Create Custom Theme",
"options": "OPTIONS",
"use_dominant_color": "Use Dominant Color from Artwork",
"categories": {
"all": "All Themes",
"dark": "Dark Themes",
"colorful": "Colorful",
"custom": "My Themes"
},
"editor": {
"theme_name_placeholder": "Theme name",
"save": "Save",
"primary": "Primary",
"secondary": "Secondary",
"background": "Background",
"invalid_name_title": "Invalid Name",
"invalid_name_msg": "Please enter a valid theme name"
},
"alerts": {
"delete_title": "Delete Theme",
"delete_msg": "Are you sure you want to delete \"{{name}}\"?",
"ok": "OK",
"delete": "Delete",
"cancel": "Cancel",
"back": "Settings"
}
} }
} }

View file

@ -1193,5 +1193,35 @@
"repo_format_hint": "Formato: https://raw.githubusercontent.com/usuario/repo/rama", "repo_format_hint": "Formato: https://raw.githubusercontent.com/usuario/repo/rama",
"cancel": "Cancelar", "cancel": "Cancelar",
"add": "Añadir" "add": "Añadir"
},
"theme": {
"title": "Temas de la App",
"select_theme": "SELECCIONAR TEMA",
"create_custom": "Crear Tema Personalizado",
"options": "OPCIONES",
"use_dominant_color": "Usar Color Dominante del Arte",
"categories": {
"all": "Todos los Temas",
"dark": "Temas Oscuros",
"colorful": "Coloridos",
"custom": "Mis Temas"
},
"editor": {
"theme_name_placeholder": "Nombre del tema",
"save": "Guardar",
"primary": "Primario",
"secondary": "Secundario",
"background": "Fondo",
"invalid_name_title": "Nombre Inválido",
"invalid_name_msg": "Por favor ingresa un nombre válido"
},
"alerts": {
"delete_title": "Eliminar Tema",
"delete_msg": "¿Estás seguro de que quieres eliminar \"{{name}}\"?",
"ok": "OK",
"delete": "Eliminar",
"cancel": "Cancelar",
"back": "Ajustes"
}
} }
} }

View file

@ -1193,5 +1193,35 @@
"repo_format_hint": "Format : https://raw.githubusercontent.com/utilisateur/repo/refs/heads/branche", "repo_format_hint": "Format : https://raw.githubusercontent.com/utilisateur/repo/refs/heads/branche",
"cancel": "Annuler", "cancel": "Annuler",
"add": "Ajouter" "add": "Ajouter"
},
"theme": {
"title": "Thèmes de l'App",
"select_theme": "SÉLECTIONNER UN THÈME",
"create_custom": "Créer un Thème Personnalisé",
"options": "OPTIONS",
"use_dominant_color": "Utiliser la Couleur Dominante de l'Image",
"categories": {
"all": "Tous les Thèmes",
"dark": "Thèmes Sombres",
"colorful": "Colorés",
"custom": "Mes Thèmes"
},
"editor": {
"theme_name_placeholder": "Nom du thème",
"save": "Enregistrer",
"primary": "Primaire",
"secondary": "Secondaire",
"background": "Arrière-plan",
"invalid_name_title": "Nom Invalide",
"invalid_name_msg": "Veuillez entrer un nom de thème valide"
},
"alerts": {
"delete_title": "Supprimer le Thème",
"delete_msg": "Êtes-vous sûr de vouloir supprimer \"{{name}}\" ?",
"ok": "OK",
"delete": "Supprimer",
"cancel": "Annuler",
"back": "Paramètres"
}
} }
} }

View file

@ -1193,5 +1193,35 @@
"repo_format_hint": "Formato: https://raw.githubusercontent.com/user/repo/refs/heads/branch", "repo_format_hint": "Formato: https://raw.githubusercontent.com/user/repo/refs/heads/branch",
"cancel": "Annulla", "cancel": "Annulla",
"add": "Aggiungi" "add": "Aggiungi"
},
"theme": {
"title": "Temi App",
"select_theme": "SELEZIONA TEMA",
"create_custom": "Crea Tema Personalizado",
"options": "OPZIONI",
"use_dominant_color": "Usa Colore Dominante dalla Copertina",
"categories": {
"all": "Tutti i Temi",
"dark": "Temi Scuri",
"colorful": "Colorati",
"custom": "I Miei Temi"
},
"editor": {
"theme_name_placeholder": "Nome tema",
"save": "Salva",
"primary": "Primario",
"secondary": "Secondario",
"background": "Sfondo",
"invalid_name_title": "Nome Non Valido",
"invalid_name_msg": "Inserisci un nome valido per il tema"
},
"alerts": {
"delete_title": "Elimina Tema",
"delete_msg": "Sei sicuro di voler eliminare \"{{name}}\"?",
"ok": "OK",
"delete": "Elimina",
"cancel": "Annulla",
"back": "Impostazioni"
}
} }
} }

View file

@ -1159,5 +1159,35 @@
"repo_format_hint": "Formato: https://raw.githubusercontent.com/username/repo/refs/heads/branch", "repo_format_hint": "Formato: https://raw.githubusercontent.com/username/repo/refs/heads/branch",
"cancel": "Cancelar", "cancel": "Cancelar",
"add": "Adicionar" "add": "Adicionar"
},
"theme": {
"title": "Temas do App",
"select_theme": "SELECIONAR TEMA",
"create_custom": "Criar Tema Personalizado",
"options": "OPÇÕES",
"use_dominant_color": "Usar Cor Dominante da Arte",
"categories": {
"all": "Todos os Temas",
"dark": "Temas Escuros",
"colorful": "Coloridos",
"custom": "Meus Temas"
},
"editor": {
"theme_name_placeholder": "Nome do tema",
"save": "Salvar",
"primary": "Primário",
"secondary": "Secundário",
"background": "Fundo",
"invalid_name_title": "Nome Inválido",
"invalid_name_msg": "Por favor insira um nome válido"
},
"alerts": {
"delete_title": "Excluir Tema",
"delete_msg": "Tem certeza que deseja excluir \"{{name}}\"?",
"ok": "OK",
"delete": "Excluir",
"cancel": "Cancelar",
"back": "Configurações"
}
} }
} }

View file

@ -1,4 +1,5 @@
import React, { useState, useCallback, useEffect, useMemo } from 'react'; import React, { useState, useCallback, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { import {
View, View,
Text, Text,
@ -45,9 +46,9 @@ interface ThemeCardProps {
onDelete?: () => void; onDelete?: () => void;
} }
const ThemeCard: React.FC<ThemeCardProps> = ({ const ThemeCard: React.FC<ThemeCardProps> = ({
theme, theme,
isSelected, isSelected,
onSelect, onSelect,
onEdit, onEdit,
onDelete onDelete
@ -57,10 +58,10 @@ const ThemeCard: React.FC<ThemeCardProps> = ({
style={[ style={[
styles.themeCard, styles.themeCard,
isSelected && styles.selectedThemeCard, isSelected && styles.selectedThemeCard,
{ {
borderColor: isSelected ? theme.colors.primary : 'transparent', borderColor: isSelected ? theme.colors.primary : 'transparent',
backgroundColor: Platform.OS === 'ios' backgroundColor: Platform.OS === 'ios'
? `${theme.colors.darkBackground}60` ? `${theme.colors.darkBackground}60`
: 'rgba(255, 255, 255, 0.07)' : 'rgba(255, 255, 255, 0.07)'
} }
]} ]}
@ -75,26 +76,26 @@ const ThemeCard: React.FC<ThemeCardProps> = ({
<MaterialIcons name="check-circle" size={18} color={theme.colors.primary} /> <MaterialIcons name="check-circle" size={18} color={theme.colors.primary} />
)} )}
</View> </View>
<View style={styles.colorPreviewContainer}> <View style={styles.colorPreviewContainer}>
<View style={[styles.colorPreview, { backgroundColor: theme.colors.primary }, styles.colorPreviewShadow]} /> <View style={[styles.colorPreview, { backgroundColor: theme.colors.primary }, styles.colorPreviewShadow]} />
<View style={[styles.colorPreview, { backgroundColor: theme.colors.secondary }, styles.colorPreviewShadow]} /> <View style={[styles.colorPreview, { backgroundColor: theme.colors.secondary }, styles.colorPreviewShadow]} />
<View style={[styles.colorPreview, { backgroundColor: theme.colors.darkBackground }, styles.colorPreviewShadow]} /> <View style={[styles.colorPreview, { backgroundColor: theme.colors.darkBackground }, styles.colorPreviewShadow]} />
</View> </View>
{theme.isEditable && ( {theme.isEditable && (
<View style={styles.themeCardActions}> <View style={styles.themeCardActions}>
{onEdit && ( {onEdit && (
<TouchableOpacity <TouchableOpacity
style={[styles.themeCardAction, styles.buttonShadow]} style={[styles.themeCardAction, styles.buttonShadow]}
onPress={onEdit} onPress={onEdit}
> >
<MaterialIcons name="edit" size={16} color={theme.colors.primary} /> <MaterialIcons name="edit" size={16} color={theme.colors.primary} />
</TouchableOpacity> </TouchableOpacity>
)} )}
{onDelete && ( {onDelete && (
<TouchableOpacity <TouchableOpacity
style={[styles.themeCardAction, styles.buttonShadow]} style={[styles.themeCardAction, styles.buttonShadow]}
onPress={onDelete} onPress={onDelete}
> >
<MaterialIcons name="delete" size={16} color={theme.colors.error} /> <MaterialIcons name="delete" size={16} color={theme.colors.error} />
@ -114,11 +115,11 @@ interface FilterTabProps {
primaryColor: string; primaryColor: string;
} }
const FilterTab: React.FC<FilterTabProps> = ({ const FilterTab: React.FC<FilterTabProps> = ({
category, category,
isActive, isActive,
onPress, onPress,
primaryColor primaryColor
}) => ( }) => (
<TouchableOpacity <TouchableOpacity
style={[ style={[
@ -128,9 +129,9 @@ const FilterTab: React.FC<FilterTabProps> = ({
]} ]}
onPress={onPress} onPress={onPress}
> >
<Text <Text
style={[ style={[
styles.filterTabText, styles.filterTabText,
isActive && { color: '#FFFFFF' } isActive && { color: '#FFFFFF' }
]} ]}
> >
@ -171,152 +172,157 @@ const ThemeColorEditor: React.FC<ThemeColorEditorProps & {
setAlertActions, setAlertActions,
setAlertVisible setAlertVisible
}) => { }) => {
const [themeName, setThemeName] = useState('Custom Theme'); const { t } = useTranslation();
const [selectedColorKey, setSelectedColorKey] = useState<ColorKey>('primary'); const [themeName, setThemeName] = useState(t('theme.editor.theme_name_placeholder') || 'Custom Theme');
const [themeColors, setThemeColors] = useState({ const [selectedColorKey, setSelectedColorKey] = useState<ColorKey>('primary');
primary: initialColors.primary, const [themeColors, setThemeColors] = useState({
secondary: initialColors.secondary, primary: initialColors.primary,
darkBackground: initialColors.darkBackground, secondary: initialColors.secondary,
}); darkBackground: initialColors.darkBackground,
const handleColorChange = useCallback((color: string) => {
setThemeColors(prev => ({
...prev,
[selectedColorKey]: color,
}));
}, [selectedColorKey]);
const handleSave = () => {
if (!themeName.trim()) {
setAlertTitle('Invalid Name');
setAlertMessage('Please enter a valid theme name');
setAlertActions([{ label: 'OK', onPress: () => {} }]);
setAlertVisible(true);
return;
}
onSave({
...themeColors,
name: themeName
}); });
};
// Compact preview component const handleColorChange = useCallback((color: string) => {
const ThemePreview = () => ( setThemeColors(prev => ({
<View style={[styles.previewContainer, { backgroundColor: themeColors.darkBackground }]}> ...prev,
<View style={styles.previewContent}> [selectedColorKey]: color,
{/* App header */} }));
<View style={styles.previewHeader}> }, [selectedColorKey]);
<View style={styles.previewHeaderTitle} />
<View style={styles.previewIconGroup}> const handleSave = () => {
<View style={styles.previewIcon} /> if (!themeName.trim()) {
<View style={styles.previewIcon} /> if (!themeName.trim()) {
</View> setAlertTitle(t('theme.editor.invalid_name_title'));
</View> setAlertMessage(t('theme.editor.invalid_name_msg'));
setAlertActions([{ label: 'OK', onPress: () => { } }]);
{/* Content area */} setAlertVisible(true);
<View style={styles.previewBody}> return;
{/* Featured content poster */} }
<View style={styles.previewFeatured}> setAlertVisible(true);
<View style={styles.previewPosterGradient} /> return;
<View style={styles.previewTitle} /> }
<View style={styles.previewButtonRow}> onSave({
<View style={[styles.previewPlayButton, { backgroundColor: themeColors.primary }]} /> ...themeColors,
<View style={styles.previewActionButton} /> name: themeName
});
};
// Compact preview component
const ThemePreview = () => (
<View style={[styles.previewContainer, { backgroundColor: themeColors.darkBackground }]}>
<View style={styles.previewContent}>
{/* App header */}
<View style={styles.previewHeader}>
<View style={styles.previewHeaderTitle} />
<View style={styles.previewIconGroup}>
<View style={styles.previewIcon} />
<View style={styles.previewIcon} />
</View> </View>
</View> </View>
{/* Content row */}
<View style={styles.previewSectionHeader}>
<View style={styles.previewSectionTitle} />
</View>
<View style={styles.previewPosterRow}>
<View style={styles.previewPoster} />
<View style={styles.previewPoster} />
<View style={styles.previewPoster} />
</View>
</View>
</View>
</View>
);
return ( {/* Content area */}
<View style={styles.editorContainer}> <View style={styles.previewBody}>
<View style={styles.editorHeader}> {/* Featured content poster */}
<TouchableOpacity <View style={styles.previewFeatured}>
style={styles.editorBackButton} <View style={styles.previewPosterGradient} />
onPress={onCancel} <View style={styles.previewTitle} />
> <View style={styles.previewButtonRow}>
<MaterialIcons name="arrow-back" size={20} color="#FFFFFF" /> <View style={[styles.previewPlayButton, { backgroundColor: themeColors.primary }]} />
</TouchableOpacity> <View style={styles.previewActionButton} />
<TextInput </View>
style={styles.editorTitleInput} </View>
value={themeName}
onChangeText={setThemeName} {/* Content row */}
placeholder="Theme name" <View style={styles.previewSectionHeader}>
placeholderTextColor="rgba(255,255,255,0.5)" <View style={styles.previewSectionTitle} />
/> </View>
<TouchableOpacity <View style={styles.previewPosterRow}>
style={styles.editorSaveButton} <View style={styles.previewPoster} />
onPress={handleSave} <View style={styles.previewPoster} />
> <View style={styles.previewPoster} />
<Text style={styles.saveButtonText}>Save</Text> </View>
</TouchableOpacity>
</View>
<View style={styles.editorBody}>
<View style={styles.colorSectionRow}>
<ThemePreview />
<View style={styles.colorButtonsColumn}>
<TouchableOpacity
style={[
styles.colorSelectorButton,
selectedColorKey === 'primary' && styles.selectedColorButton,
{ backgroundColor: themeColors.primary }
]}
onPress={() => setSelectedColorKey('primary')}
>
<Text style={styles.colorButtonText}>Primary</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.colorSelectorButton,
selectedColorKey === 'secondary' && styles.selectedColorButton,
{ backgroundColor: themeColors.secondary }
]}
onPress={() => setSelectedColorKey('secondary')}
>
<Text style={styles.colorButtonText}>Secondary</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.colorSelectorButton,
selectedColorKey === 'darkBackground' && styles.selectedColorButton,
{ backgroundColor: themeColors.darkBackground }
]}
onPress={() => setSelectedColorKey('darkBackground')}
>
<Text style={styles.colorButtonText}>Background</Text>
</TouchableOpacity>
</View> </View>
</View> </View>
</View>
<View style={styles.colorPickerContainer}> );
<ColorPicker
color={themeColors[selectedColorKey]} return (
onColorChange={handleColorChange} <View style={styles.editorContainer}>
thumbSize={22} <View style={styles.editorHeader}>
sliderSize={22} <TouchableOpacity
noSnap={true} style={styles.editorBackButton}
row={false} onPress={onCancel}
>
<MaterialIcons name="arrow-back" size={20} color="#FFFFFF" />
</TouchableOpacity>
<TextInput
style={styles.editorTitleInput}
value={themeName}
onChangeText={setThemeName}
placeholder={t('theme.editor.theme_name_placeholder')}
placeholderTextColor="rgba(255,255,255,0.5)"
/> />
<TouchableOpacity
style={styles.editorSaveButton}
onPress={handleSave}
>
<Text style={styles.saveButtonText}>{t('theme.editor.save')}</Text>
</TouchableOpacity>
</View>
<View style={styles.editorBody}>
<View style={styles.colorSectionRow}>
<ThemePreview />
<View style={styles.colorButtonsColumn}>
<TouchableOpacity
style={[
styles.colorSelectorButton,
selectedColorKey === 'primary' && styles.selectedColorButton,
{ backgroundColor: themeColors.primary }
]}
onPress={() => setSelectedColorKey('primary')}
>
<Text style={styles.colorButtonText}>{t('theme.editor.primary')}</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.colorSelectorButton,
selectedColorKey === 'secondary' && styles.selectedColorButton,
{ backgroundColor: themeColors.secondary }
]}
onPress={() => setSelectedColorKey('secondary')}
>
<Text style={styles.colorButtonText}>{t('theme.editor.secondary')}</Text>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.colorSelectorButton,
selectedColorKey === 'darkBackground' && styles.selectedColorButton,
{ backgroundColor: themeColors.darkBackground }
]}
onPress={() => setSelectedColorKey('darkBackground')}
>
<Text style={styles.colorButtonText}>{t('theme.editor.background')}</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.colorPickerContainer}>
<ColorPicker
color={themeColors[selectedColorKey]}
onColorChange={handleColorChange}
thumbSize={22}
sliderSize={22}
noSnap={true}
row={false}
/>
</View>
</View> </View>
</View> </View>
</View> );
); };
};
const ThemeScreen: React.FC = () => { const ThemeScreen: React.FC = () => {
const { const {
@ -327,6 +333,7 @@ const ThemeScreen: React.FC = () => {
updateCustomTheme, updateCustomTheme,
deleteCustomTheme deleteCustomTheme
} = useTheme(); } = useTheme();
const { t } = useTranslation();
const navigation = useNavigation<NavigationProp<RootStackParamList>>(); const navigation = useNavigation<NavigationProp<RootStackParamList>>();
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const { settings, updateSetting } = useSettings(); const { settings, updateSetting } = useSettings();
@ -335,7 +342,15 @@ const ThemeScreen: React.FC = () => {
const headerTopPadding = Platform.OS === 'android' const headerTopPadding = Platform.OS === 'android'
? ANDROID_STATUSBAR_HEIGHT + 8 ? ANDROID_STATUSBAR_HEIGHT + 8
: 8; : 8;
// Theme categories for organization
const THEME_CATEGORIES = [
{ id: 'all', name: t('theme.categories.all') },
{ id: 'dark', name: t('theme.categories.dark') },
{ id: 'colorful', name: t('theme.categories.colorful') },
{ id: 'custom', name: t('theme.categories.custom') },
];
const [isEditMode, setIsEditMode] = useState(false); const [isEditMode, setIsEditMode] = useState(false);
const [editingTheme, setEditingTheme] = useState<Theme | null>(null); const [editingTheme, setEditingTheme] = useState<Theme | null>(null);
const [activeFilter, setActiveFilter] = useState('all'); const [activeFilter, setActiveFilter] = useState('all');
@ -352,9 +367,9 @@ const ThemeScreen: React.FC = () => {
StatusBar.setBackgroundColor('transparent'); StatusBar.setBackgroundColor('transparent');
} }
}; };
applyStatusBarConfig(); applyStatusBarConfig();
// Re-apply on focus // Re-apply on focus
const unsubscribe = navigation.addListener('focus', applyStatusBarConfig); const unsubscribe = navigation.addListener('focus', applyStatusBarConfig);
return unsubscribe; return unsubscribe;
@ -365,19 +380,19 @@ const ThemeScreen: React.FC = () => {
switch (activeFilter) { switch (activeFilter) {
case 'dark': case 'dark':
// Themes with darker colors // Themes with darker colors
return availableThemes.filter(theme => return availableThemes.filter(theme =>
!theme.isEditable && !theme.isEditable &&
theme.id !== 'neon' && theme.id !== 'neon' &&
theme.id !== 'retro' theme.id !== 'retro'
); );
case 'colorful': case 'colorful':
// Themes with vibrant colors // Themes with vibrant colors
return availableThemes.filter(theme => return availableThemes.filter(theme =>
!theme.isEditable && !theme.isEditable &&
(theme.id === 'neon' || (theme.id === 'neon' ||
theme.id === 'retro' || theme.id === 'retro' ||
theme.id === 'sunset' || theme.id === 'sunset' ||
theme.id === 'amber') theme.id === 'amber')
); );
case 'custom': case 'custom':
// User's custom themes // User's custom themes
@ -398,18 +413,18 @@ const ThemeScreen: React.FC = () => {
}, []); }, []);
const handleDeleteTheme = useCallback((theme: Theme) => { const handleDeleteTheme = useCallback((theme: Theme) => {
setAlertTitle('Delete Theme'); setAlertTitle(t('theme.alerts.delete_title'));
setAlertMessage(`Are you sure you want to delete "${theme.name}"?`); setAlertMessage(t('theme.alerts.delete_msg', { name: theme.name }));
setAlertActions([ setAlertActions([
{ label: 'Cancel', style: { color: '#888' }, onPress: () => {} }, { label: t('theme.alerts.cancel'), style: { color: '#888' }, onPress: () => { } },
{ {
label: 'Delete', label: t('theme.alerts.delete'),
style: { color: currentTheme.colors.error }, style: { color: currentTheme.colors.error },
onPress: () => deleteCustomTheme(theme.id), onPress: () => deleteCustomTheme(theme.id),
}, },
]); ]);
setAlertVisible(true); setAlertVisible(true);
}, [deleteCustomTheme, currentTheme.colors.error]); }, [deleteCustomTheme, currentTheme.colors.error, t]);
const handleCreateTheme = useCallback(() => { const handleCreateTheme = useCallback(() => {
setEditingTheme(null); setEditingTheme(null);
@ -432,7 +447,7 @@ const ThemeScreen: React.FC = () => {
} else { } else {
// Create new theme // Create new theme
addCustomTheme({ addCustomTheme({
name: themeData.name || 'Custom Theme', name: themeData.name || t('theme.create_custom'),
colors: { colors: {
...currentTheme.colors, ...currentTheme.colors,
primary: themeData.primary, primary: themeData.primary,
@ -441,7 +456,7 @@ const ThemeScreen: React.FC = () => {
} }
}); });
} }
setIsEditMode(false); setIsEditMode(false);
setEditingTheme(null); setEditingTheme(null);
}, [editingTheme, updateCustomTheme, addCustomTheme, currentTheme]); }, [editingTheme, updateCustomTheme, addCustomTheme, currentTheme]);
@ -467,9 +482,9 @@ const ThemeScreen: React.FC = () => {
const ThemeColorEditorWithAlert = (props: any) => { const ThemeColorEditorWithAlert = (props: any) => {
const handleSave = (themeName: string, themeColors: any, onSave: any) => { const handleSave = (themeName: string, themeColors: any, onSave: any) => {
if (!themeName.trim()) { if (!themeName.trim()) {
setAlertTitle('Invalid Name'); setAlertTitle(t('theme.editor.invalid_name_title'));
setAlertMessage('Please enter a valid theme name'); setAlertMessage(t('theme.editor.invalid_name_msg'));
setAlertActions([{ label: 'OK', onPress: () => {} }]); setAlertActions([{ label: 'OK', onPress: () => { } }]);
setAlertVisible(true); setAlertVisible(true);
return false; return false;
} }
@ -503,7 +518,7 @@ const ThemeScreen: React.FC = () => {
return ( return (
<SafeAreaView style={[ <SafeAreaView style={[
styles.container, styles.container,
{ backgroundColor: currentTheme.colors.darkBackground } { backgroundColor: currentTheme.colors.darkBackground }
]}> ]}>
<StatusBar barStyle="light-content" /> <StatusBar barStyle="light-content" />
@ -529,31 +544,31 @@ const ThemeScreen: React.FC = () => {
return ( return (
<SafeAreaView style={[ <SafeAreaView style={[
styles.container, styles.container,
{ backgroundColor: currentTheme.colors.darkBackground } { backgroundColor: currentTheme.colors.darkBackground }
]}> ]}>
<StatusBar barStyle="light-content" /> <StatusBar barStyle="light-content" />
<View style={[styles.header, { paddingTop: headerTopPadding }]}> <View style={[styles.header, { paddingTop: headerTopPadding }]}>
<TouchableOpacity <TouchableOpacity
style={styles.backButton} style={styles.backButton}
onPress={() => navigation.goBack()} onPress={() => navigation.goBack()}
> >
<MaterialIcons name="arrow-back" size={24} color={currentTheme.colors.text} /> <MaterialIcons name="arrow-back" size={24} color={currentTheme.colors.text} />
<Text style={[styles.backText, { color: currentTheme.colors.text }]}> <Text style={[styles.backText, { color: currentTheme.colors.text }]}>
Settings {t('theme.alerts.back') || t('settings.app_settings_label').split(' ')[0] || 'Settings'}
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
<View style={styles.headerActions}> <View style={styles.headerActions}>
{/* Empty for now, but ready for future actions */} {/* Empty for now, but ready for future actions */}
</View> </View>
</View> </View>
<Text style={[styles.headerTitle, { color: currentTheme.colors.text }]}> <Text style={[styles.headerTitle, { color: currentTheme.colors.text }]}>
App Themes {t('theme.title')}
</Text> </Text>
{/* Category filter */} {/* Category filter */}
<View style={styles.filterContainer}> <View style={styles.filterContainer}>
<FlatList <FlatList
@ -572,16 +587,16 @@ const ThemeScreen: React.FC = () => {
contentContainerStyle={styles.filterList} contentContainerStyle={styles.filterList}
/> />
</View> </View>
<ScrollView <ScrollView
style={styles.content} style={styles.content}
contentContainerStyle={styles.contentContainer} contentContainerStyle={styles.contentContainer}
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
> >
<Text style={[styles.sectionTitle, { color: currentTheme.colors.textMuted }]}> <Text style={[styles.sectionTitle, { color: currentTheme.colors.textMuted }]}>
SELECT THEME {t('theme.select_theme')}
</Text> </Text>
<View style={styles.themeGrid}> <View style={styles.themeGrid}>
{filteredThemes.map(theme => ( {filteredThemes.map(theme => (
<ThemeCard <ThemeCard
@ -594,26 +609,26 @@ const ThemeScreen: React.FC = () => {
/> />
))} ))}
</View> </View>
<TouchableOpacity <TouchableOpacity
style={[ style={[
styles.createButton, styles.createButton,
{ backgroundColor: currentTheme.colors.primary }, { backgroundColor: currentTheme.colors.primary },
styles.buttonShadow styles.buttonShadow
]} ]}
onPress={handleCreateTheme} onPress={handleCreateTheme}
> >
<MaterialIcons name="add" size={20} color="#FFFFFF" /> <MaterialIcons name="add" size={20} color="#FFFFFF" />
<Text style={styles.createButtonText}>Create Custom Theme</Text> <Text style={styles.createButtonText}>{t('theme.create_custom')}</Text>
</TouchableOpacity> </TouchableOpacity>
<Text style={[styles.sectionTitle, { color: currentTheme.colors.textMuted, marginTop: 24 }]}> <Text style={[styles.sectionTitle, { color: currentTheme.colors.textMuted, marginTop: 24 }]}>
OPTIONS {t('theme.options')}
</Text> </Text>
<View style={styles.optionRow}> <View style={styles.optionRow}>
<Text style={[styles.optionLabel, { color: currentTheme.colors.text }]}> <Text style={[styles.optionLabel, { color: currentTheme.colors.text }]}>
Use Dominant Color from Artwork {t('theme.use_dominant_color')}
</Text> </Text>
<Switch <Switch
value={settings.useDominantBackgroundColor} value={settings.useDominantBackgroundColor}
@ -631,7 +646,7 @@ const ThemeScreen: React.FC = () => {
actions={alertActions} actions={alertActions}
onClose={() => setAlertVisible(false)} onClose={() => setAlertVisible(false)}
/> />
</SafeAreaView> </SafeAreaView >
); );
}; };
@ -801,7 +816,7 @@ const styles = StyleSheet.create({
optionLabel: { optionLabel: {
fontSize: 14, fontSize: 14,
}, },
// Editor styles // Editor styles
editorContainer: { editorContainer: {
flex: 1, flex: 1,
@ -977,7 +992,7 @@ const styles = StyleSheet.create({
padding: 8, padding: 8,
marginBottom: 10, marginBottom: 10,
}, },
// Legacy styles - keep for backward compatibility // Legacy styles - keep for backward compatibility
editorTitle: { editorTitle: {
fontSize: 18, fontSize: 18,