mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-11 04:21:42 +00:00
Merge branch 'tapframe:main' into feature/ani-skip
This commit is contained in:
commit
5166dbd446
10 changed files with 1755 additions and 311 deletions
|
|
@ -25,6 +25,7 @@ class KSPlayerView: UIView {
|
||||||
@objc var onEnd: RCTDirectEventBlock?
|
@objc var onEnd: RCTDirectEventBlock?
|
||||||
@objc var onError: RCTDirectEventBlock?
|
@objc var onError: RCTDirectEventBlock?
|
||||||
@objc var onBufferingProgress: RCTDirectEventBlock?
|
@objc var onBufferingProgress: RCTDirectEventBlock?
|
||||||
|
@objc var onExitFullscreen: RCTDirectEventBlock?
|
||||||
|
|
||||||
// Property setters that React Native will call
|
// Property setters that React Native will call
|
||||||
@objc var source: NSDictionary? {
|
@objc var source: NSDictionary? {
|
||||||
|
|
@ -313,6 +314,22 @@ class KSPlayerView: UIView {
|
||||||
} else {
|
} else {
|
||||||
playerView.play()
|
playerView.play()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var keyCommands: [UIKeyCommand]? {
|
||||||
|
return [
|
||||||
|
UIKeyCommand(
|
||||||
|
input: UIKeyCommand.inputEscape,
|
||||||
|
modifierFlags: [],
|
||||||
|
action: #selector(handleEscapeKey),
|
||||||
|
discoverabilityTitle: "Exit Fullscreen"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func handleEscapeKey() {
|
||||||
|
print("KSPlayerView: ESC pressed")
|
||||||
|
sendEvent("onExitFullscreen", [:])
|
||||||
}
|
}
|
||||||
|
|
||||||
func setVolume(_ volume: Float) {
|
func setVolume(_ volume: Float) {
|
||||||
|
|
@ -979,3 +996,8 @@ extension KSPlayerView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension IOSVideoPlayerView {
|
||||||
|
@objc func handleEscapeKey() {
|
||||||
|
self.next?.perform(#selector(handleEscapeKey))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -601,6 +601,7 @@
|
||||||
"arabic": "العربية",
|
"arabic": "العربية",
|
||||||
"spanish": "الإسبانية",
|
"spanish": "الإسبانية",
|
||||||
"french": "الفرنسية",
|
"french": "الفرنسية",
|
||||||
|
"italian": "الإيطالية",
|
||||||
"account": "الحساب",
|
"account": "الحساب",
|
||||||
"content_discovery": "المحتوى والاكتشاف",
|
"content_discovery": "المحتوى والاكتشاف",
|
||||||
"appearance": "المظهر",
|
"appearance": "المظهر",
|
||||||
|
|
@ -1192,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": "إعدادات"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -601,6 +601,7 @@
|
||||||
"arabic": "Arabic",
|
"arabic": "Arabic",
|
||||||
"spanish": "Spanish",
|
"spanish": "Spanish",
|
||||||
"french": "French",
|
"french": "French",
|
||||||
|
"italian": "Italian",
|
||||||
"account": "Account",
|
"account": "Account",
|
||||||
"content_discovery": "Content & Discovery",
|
"content_discovery": "Content & Discovery",
|
||||||
"appearance": "Appearance",
|
"appearance": "Appearance",
|
||||||
|
|
@ -1192,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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -601,6 +601,7 @@
|
||||||
"arabic": "Árabe",
|
"arabic": "Árabe",
|
||||||
"spanish": "Español",
|
"spanish": "Español",
|
||||||
"french": "Francés",
|
"french": "Francés",
|
||||||
|
"italian": "Italiano",
|
||||||
"account": "Cuenta",
|
"account": "Cuenta",
|
||||||
"content_discovery": "Contenido y descubrimiento",
|
"content_discovery": "Contenido y descubrimiento",
|
||||||
"appearance": "Apariencia",
|
"appearance": "Apariencia",
|
||||||
|
|
@ -1192,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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -601,6 +601,7 @@
|
||||||
"arabic": "Arabe",
|
"arabic": "Arabe",
|
||||||
"spanish": "Espagnol",
|
"spanish": "Espagnol",
|
||||||
"french": "Français",
|
"french": "Français",
|
||||||
|
"italian": "Italien",
|
||||||
"account": "Compte",
|
"account": "Compte",
|
||||||
"content_discovery": "Contenu et découverte",
|
"content_discovery": "Contenu et découverte",
|
||||||
"appearance": "Apparence",
|
"appearance": "Apparence",
|
||||||
|
|
@ -1192,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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
1227
src/i18n/locales/it.json
Normal file
1227
src/i18n/locales/it.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -579,6 +579,7 @@
|
||||||
"arabic": "Árabe",
|
"arabic": "Árabe",
|
||||||
"spanish": "Espanhol",
|
"spanish": "Espanhol",
|
||||||
"french": "Francês",
|
"french": "Francês",
|
||||||
|
"italian": "Italiano",
|
||||||
"account": "Conta",
|
"account": "Conta",
|
||||||
"content_discovery": "Conteúdo e Descoberta",
|
"content_discovery": "Conteúdo e Descoberta",
|
||||||
"appearance": "Aparência",
|
"appearance": "Aparência",
|
||||||
|
|
@ -1158,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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ import pt from './locales/pt.json';
|
||||||
import ar from './locales/ar.json';
|
import ar from './locales/ar.json';
|
||||||
import es from './locales/es.json';
|
import es from './locales/es.json';
|
||||||
import fr from './locales/fr.json';
|
import fr from './locales/fr.json';
|
||||||
|
import it from './locales/it.json';
|
||||||
|
|
||||||
export const resources = {
|
export const resources = {
|
||||||
en: { translation: en },
|
en: { translation: en },
|
||||||
|
|
@ -10,4 +11,5 @@ export const resources = {
|
||||||
ar: { translation: ar },
|
ar: { translation: ar },
|
||||||
es: { translation: es },
|
es: { translation: es },
|
||||||
fr: { translation: fr },
|
fr: { translation: fr },
|
||||||
|
it: { translation: it },
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -601,11 +601,12 @@ const SettingsScreen: React.FC = () => {
|
||||||
<SettingItem
|
<SettingItem
|
||||||
title={t('settings.language')}
|
title={t('settings.language')}
|
||||||
description={
|
description={
|
||||||
i18n.language === 'pt' ? t('settings.portuguese') :
|
i18n.language === 'pt' ? t('settings.portuguese') :
|
||||||
i18n.language === 'ar' ? t('settings.arabic') :
|
i18n.language === 'ar' ? t('settings.arabic') :
|
||||||
i18n.language === 'es' ? t('settings.spanish') :
|
i18n.language === 'es' ? t('settings.spanish') :
|
||||||
i18n.language === 'fr' ? t('settings.french') :
|
i18n.language === 'fr' ? t('settings.french') :
|
||||||
t('settings.english')
|
i18n.language === 'it' ? t('settings.italian') :
|
||||||
|
t('settings.english')
|
||||||
}
|
}
|
||||||
icon="globe"
|
icon="globe"
|
||||||
renderControl={() => <ChevronRight />}
|
renderControl={() => <ChevronRight />}
|
||||||
|
|
@ -855,116 +856,138 @@ const SettingsScreen: React.FC = () => {
|
||||||
style={{ backgroundColor: currentTheme.colors.darkGray || '#0A0C0C' }}
|
style={{ backgroundColor: currentTheme.colors.darkGray || '#0A0C0C' }}
|
||||||
contentContainerStyle={styles.bottomSheetContent}
|
contentContainerStyle={styles.bottomSheetContent}
|
||||||
>
|
>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[
|
style={[
|
||||||
styles.languageOption,
|
styles.languageOption,
|
||||||
i18n.language === 'en' && { backgroundColor: currentTheme.colors.primary + '20' }
|
i18n.language === 'en' && { backgroundColor: currentTheme.colors.primary + '20' }
|
||||||
]}
|
]}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
i18n.changeLanguage('en');
|
i18n.changeLanguage('en');
|
||||||
languageSheetRef.current?.close();
|
languageSheetRef.current?.close();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text style={[
|
<Text style={[
|
||||||
styles.languageText,
|
styles.languageText,
|
||||||
{ color: currentTheme.colors.highEmphasis },
|
{ color: currentTheme.colors.highEmphasis },
|
||||||
i18n.language === 'en' && { color: currentTheme.colors.primary, fontWeight: 'bold' }
|
i18n.language === 'en' && { color: currentTheme.colors.primary, fontWeight: 'bold' }
|
||||||
]}>
|
]}>
|
||||||
{t('settings.english')}
|
{t('settings.english')}
|
||||||
</Text>
|
</Text>
|
||||||
{i18n.language === 'en' && (
|
{i18n.language === 'en' && (
|
||||||
<Feather name="check" size={20} color={currentTheme.colors.primary} />
|
<Feather name="check" size={20} color={currentTheme.colors.primary} />
|
||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[
|
style={[
|
||||||
styles.languageOption,
|
styles.languageOption,
|
||||||
i18n.language === 'pt' && { backgroundColor: currentTheme.colors.primary + '20' }
|
i18n.language === 'pt' && { backgroundColor: currentTheme.colors.primary + '20' }
|
||||||
]}
|
]}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
i18n.changeLanguage('pt');
|
i18n.changeLanguage('pt');
|
||||||
languageSheetRef.current?.close();
|
languageSheetRef.current?.close();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text style={[
|
<Text style={[
|
||||||
styles.languageText,
|
styles.languageText,
|
||||||
{ color: currentTheme.colors.highEmphasis },
|
{ color: currentTheme.colors.highEmphasis },
|
||||||
i18n.language === 'pt' && { color: currentTheme.colors.primary, fontWeight: 'bold' }
|
i18n.language === 'pt' && { color: currentTheme.colors.primary, fontWeight: 'bold' }
|
||||||
]}>
|
]}>
|
||||||
{t('settings.portuguese')}
|
{t('settings.portuguese')}
|
||||||
</Text>
|
</Text>
|
||||||
{i18n.language === 'pt' && (
|
{i18n.language === 'pt' && (
|
||||||
<Feather name="check" size={20} color={currentTheme.colors.primary} />
|
<Feather name="check" size={20} color={currentTheme.colors.primary} />
|
||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[
|
style={[
|
||||||
styles.languageOption,
|
styles.languageOption,
|
||||||
i18n.language === 'ar' && { backgroundColor: currentTheme.colors.primary + '20' }
|
i18n.language === 'ar' && { backgroundColor: currentTheme.colors.primary + '20' }
|
||||||
]}
|
]}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
i18n.changeLanguage('ar');
|
i18n.changeLanguage('ar');
|
||||||
languageSheetRef.current?.close();
|
languageSheetRef.current?.close();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text style={[
|
<Text style={[
|
||||||
styles.languageText,
|
styles.languageText,
|
||||||
{ color: currentTheme.colors.highEmphasis },
|
{ color: currentTheme.colors.highEmphasis },
|
||||||
i18n.language === 'ar' && { color: currentTheme.colors.primary, fontWeight: 'bold' }
|
i18n.language === 'ar' && { color: currentTheme.colors.primary, fontWeight: 'bold' }
|
||||||
]}>
|
]}>
|
||||||
{t('settings.arabic')}
|
{t('settings.arabic')}
|
||||||
</Text>
|
</Text>
|
||||||
{i18n.language === 'ar' && (
|
{i18n.language === 'ar' && (
|
||||||
<Feather name="check" size={20} color={currentTheme.colors.primary} />
|
<Feather name="check" size={20} color={currentTheme.colors.primary} />
|
||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[
|
style={[
|
||||||
styles.languageOption,
|
styles.languageOption,
|
||||||
i18n.language === 'es' && { backgroundColor: currentTheme.colors.primary + '20' }
|
i18n.language === 'es' && { backgroundColor: currentTheme.colors.primary + '20' }
|
||||||
]}
|
]}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
i18n.changeLanguage('es');
|
i18n.changeLanguage('es');
|
||||||
languageSheetRef.current?.close();
|
languageSheetRef.current?.close();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text style={[
|
<Text style={[
|
||||||
styles.languageText,
|
styles.languageText,
|
||||||
{ color: currentTheme.colors.highEmphasis },
|
{ color: currentTheme.colors.highEmphasis },
|
||||||
i18n.language === 'es' && { color: currentTheme.colors.primary, fontWeight: 'bold' }
|
i18n.language === 'es' && { color: currentTheme.colors.primary, fontWeight: 'bold' }
|
||||||
]}>
|
]}>
|
||||||
{t('settings.spanish')}
|
{t('settings.spanish')}
|
||||||
</Text>
|
</Text>
|
||||||
{i18n.language === 'es' && (
|
{i18n.language === 'es' && (
|
||||||
<Feather name="check" size={20} color={currentTheme.colors.primary} />
|
<Feather name="check" size={20} color={currentTheme.colors.primary} />
|
||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[
|
style={[
|
||||||
styles.languageOption,
|
styles.languageOption,
|
||||||
i18n.language === 'fr' && { backgroundColor: currentTheme.colors.primary + '20' }
|
i18n.language === 'fr' && { backgroundColor: currentTheme.colors.primary + '20' }
|
||||||
]}
|
]}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
i18n.changeLanguage('fr');
|
i18n.changeLanguage('fr');
|
||||||
languageSheetRef.current?.close();
|
languageSheetRef.current?.close();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text style={[
|
<Text style={[
|
||||||
styles.languageText,
|
styles.languageText,
|
||||||
{ color: currentTheme.colors.highEmphasis },
|
{ color: currentTheme.colors.highEmphasis },
|
||||||
i18n.language === 'fr' && { color: currentTheme.colors.primary, fontWeight: 'bold' }
|
i18n.language === 'fr' && { color: currentTheme.colors.primary, fontWeight: 'bold' }
|
||||||
]}>
|
]}>
|
||||||
{t('settings.french')}
|
{t('settings.french')}
|
||||||
</Text>
|
</Text>
|
||||||
{i18n.language === 'fr' && (
|
{i18n.language === 'fr' && (
|
||||||
<Feather name="check" size={20} color={currentTheme.colors.primary} />
|
<Feather name="check" size={20} color={currentTheme.colors.primary} />
|
||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</ScrollView>
|
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[
|
||||||
|
styles.languageOption,
|
||||||
|
i18n.language === 'it' && { backgroundColor: currentTheme.colors.primary + '20' }
|
||||||
|
]}
|
||||||
|
onPress={() => {
|
||||||
|
i18n.changeLanguage('it');
|
||||||
|
languageSheetRef.current?.close();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text style={[
|
||||||
|
styles.languageText,
|
||||||
|
{ color: currentTheme.colors.highEmphasis },
|
||||||
|
i18n.language === 'it' && { color: currentTheme.colors.primary, fontWeight: 'bold' }
|
||||||
|
]}>
|
||||||
|
{t('settings.italian')}
|
||||||
|
</Text>
|
||||||
|
{i18n.language === 'it' && (
|
||||||
|
<Feather name="check" size={20} color={currentTheme.colors.primary} />
|
||||||
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
</ScrollView>
|
||||||
</BottomSheetView>
|
</BottomSheetView>
|
||||||
</BottomSheetModal>
|
</BottomSheetModal>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue