mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-04-21 16:51:57 +00:00
PlaybackSettings IntroDB Localization Patch
This commit is contained in:
parent
46e5b469dd
commit
e3a09164e6
3 changed files with 759 additions and 604 deletions
|
|
@ -697,7 +697,8 @@
|
|||
"media": "MEDIA",
|
||||
"notifications": "NOTIFICATIONS",
|
||||
"testing": "TESTING",
|
||||
"danger_zone": "DANGER ZONE"
|
||||
"danger_zone": "DANGER ZONE",
|
||||
"introdb_contribution": "INTRODB CONTRIBUTION"
|
||||
},
|
||||
"items": {
|
||||
"legal": "Legal & Disclaimer",
|
||||
|
|
@ -758,7 +759,12 @@
|
|||
"reset_campaigns": "Reset Campaigns",
|
||||
"reset_campaigns_desc": "Clear campaign impressions",
|
||||
"clear_all_data": "Clear All Data",
|
||||
"clear_all_data_desc": "Reset all settings and cached data"
|
||||
"clear_all_data_desc": "Reset all settings and cached data",
|
||||
"enable_intro_submission": "Enable Intro Submission",
|
||||
"enable_intro_submission_desc": "Contribute timestamps to the community",
|
||||
"introdb_api_key": "INTRODB API KEY",
|
||||
"introdb_key_placeholder": "Enter your API Key",
|
||||
"api_key_cleared": "API Key Cleared"
|
||||
},
|
||||
"options": {
|
||||
"horizontal": "Horizontal",
|
||||
|
|
@ -906,10 +912,10 @@
|
|||
"confirm_remove_msg": "Are you sure you want to remove your OpenRouter API key? This will disable AI chat features.",
|
||||
"success_removed": "API key removed successfully",
|
||||
"error_remove": "Failed to remove API key",
|
||||
"model":"Model",
|
||||
"using":"Using",
|
||||
"free_routing":"(free automatic routing)",
|
||||
"paid_model":"Use a custom OpenRouter model ID (useful for paid plans)."
|
||||
"model": "Model",
|
||||
"using": "Using",
|
||||
"free_routing": "(free automatic routing)",
|
||||
"paid_model": "Use a custom OpenRouter model ID (useful for paid plans)."
|
||||
},
|
||||
"catalog_settings": {
|
||||
"title": "Catalogs",
|
||||
|
|
|
|||
|
|
@ -696,7 +696,8 @@
|
|||
"media": "MEDIA",
|
||||
"notifications": "NOTIFICHE",
|
||||
"testing": "TEST",
|
||||
"danger_zone": "ZONA PERICOLOSA"
|
||||
"danger_zone": "ZONA PERICOLOSA",
|
||||
"introdb_contribution": "CONTRIBUTI INTRODB"
|
||||
},
|
||||
"items": {
|
||||
"legal": "Note Legali & Disclaimer",
|
||||
|
|
@ -757,7 +758,12 @@
|
|||
"reset_campaigns": "Ripristina Campagne",
|
||||
"reset_campaigns_desc": "Cancella le impressioni delle campagne",
|
||||
"clear_all_data": "Cancella tutti i dati",
|
||||
"clear_all_data_desc": "Ripristina tutte le impostazioni e i dati memorizzati"
|
||||
"clear_all_data_desc": "Ripristina tutte le impostazioni e i dati memorizzati",
|
||||
"enable_intro_submission":"Abilita contributi IntroDB",
|
||||
"enable_intro_submission_desc":"Contribuisci con i tuoi timestamp",
|
||||
"introdb_api_key":"CHIAVE API INTRODB",
|
||||
"introdb_key_placeholder":"Inserisci la tua chiave API",
|
||||
"api_key_cleared":"Chiave API rimossa"
|
||||
},
|
||||
"options": {
|
||||
"horizontal": "Orizzontale",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,22 @@
|
|||
import React, { useState, useCallback, useMemo, useRef, useEffect } from 'react';
|
||||
import { View, StyleSheet, ScrollView, StatusBar, Platform, Text, TouchableOpacity, Dimensions, TextInput, ActivityIndicator } from 'react-native';
|
||||
import React, {
|
||||
useState,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useRef,
|
||||
useEffect,
|
||||
} from 'react';
|
||||
import {
|
||||
View,
|
||||
StyleSheet,
|
||||
ScrollView,
|
||||
StatusBar,
|
||||
Platform,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
Dimensions,
|
||||
TextInput,
|
||||
ActivityIndicator,
|
||||
} from 'react-native';
|
||||
import { useNavigation, useFocusEffect } from '@react-navigation/native';
|
||||
import { NavigationProp } from '@react-navigation/native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
|
@ -7,10 +24,19 @@ import { useTheme } from '../../contexts/ThemeContext';
|
|||
import { useSettings } from '../../hooks/useSettings';
|
||||
import { RootStackParamList } from '../../navigation/AppNavigator';
|
||||
import ScreenHeader from '../../components/common/ScreenHeader';
|
||||
import { SettingsCard, SettingItem, CustomSwitch, ChevronRight } from './SettingsComponents';
|
||||
import {
|
||||
SettingsCard,
|
||||
SettingItem,
|
||||
CustomSwitch,
|
||||
ChevronRight,
|
||||
} from './SettingsComponents';
|
||||
import { useRealtimeConfig } from '../../hooks/useRealtimeConfig';
|
||||
import { MaterialIcons } from '@expo/vector-icons';
|
||||
import { BottomSheetModal, BottomSheetScrollView, BottomSheetBackdrop } from '@gorhom/bottom-sheet';
|
||||
import {
|
||||
BottomSheetModal,
|
||||
BottomSheetScrollView,
|
||||
BottomSheetBackdrop,
|
||||
} from '@gorhom/bottom-sheet';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SvgXml } from 'react-native-svg';
|
||||
import { toastService } from '../../services/toastService';
|
||||
|
|
@ -66,9 +92,21 @@ const AVAILABLE_LANGUAGES = [
|
|||
];
|
||||
|
||||
const SUBTITLE_SOURCE_OPTIONS = [
|
||||
{ value: 'internal', label: 'Internal First', description: 'Prefer embedded subtitles, then external' },
|
||||
{ value: 'external', label: 'External First', description: 'Prefer addon subtitles, then embedded' },
|
||||
{ value: 'any', label: 'Any Available', description: 'Use first available subtitle track' },
|
||||
{
|
||||
value: 'internal',
|
||||
label: 'Internal First',
|
||||
description: 'Prefer embedded subtitles, then external',
|
||||
},
|
||||
{
|
||||
value: 'external',
|
||||
label: 'External First',
|
||||
description: 'Prefer addon subtitles, then embedded',
|
||||
},
|
||||
{
|
||||
value: 'any',
|
||||
label: 'Any Available',
|
||||
description: 'Use first available subtitle track',
|
||||
},
|
||||
];
|
||||
|
||||
// Props for the reusable content component
|
||||
|
|
@ -80,7 +118,9 @@ interface PlaybackSettingsContentProps {
|
|||
* Reusable PlaybackSettingsContent component
|
||||
* Can be used inline (tablets) or wrapped in a screen (mobile)
|
||||
*/
|
||||
export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = ({ isTablet = false }) => {
|
||||
export const PlaybackSettingsContent: React.FC<
|
||||
PlaybackSettingsContentProps
|
||||
> = ({ isTablet = false }) => {
|
||||
const navigation = useNavigation<NavigationProp<RootStackParamList>>();
|
||||
const { currentTheme } = useTheme();
|
||||
const { settings, updateSetting } = useSettings();
|
||||
|
|
@ -107,7 +147,9 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
|
|||
const handleApiKeySubmit = async () => {
|
||||
if (!apiKeyInput.trim()) {
|
||||
updateSetting('introDbApiKey', '');
|
||||
toastService.success(t('settings.items.api_key_cleared', { defaultValue: 'API Key Cleared' }));
|
||||
toastService.success(
|
||||
t('settings.items.api_key_cleared'),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -119,9 +161,13 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
|
|||
|
||||
if (isValid) {
|
||||
updateSetting('introDbApiKey', apiKeyInput);
|
||||
toastService.success(t('settings.items.api_key_saved', { defaultValue: 'API Key Saved' }));
|
||||
toastService.success(
|
||||
t('settings.items.api_key_saved', { defaultValue: 'API Key Saved' }),
|
||||
);
|
||||
} else {
|
||||
toastService.error(t('settings.items.api_key_invalid', { defaultValue: 'Invalid API Key' }));
|
||||
toastService.error(
|
||||
t('settings.items.api_key_invalid', { defaultValue: 'Invalid API Key' }),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -135,8 +181,14 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
|
|||
// Map known classes from the IntroDB logo to equivalent inline attributes
|
||||
xml = xml.replace(/class="cls-4"/g, 'fill="url(#linear-gradient)"');
|
||||
xml = xml.replace(/class="cls-3"/g, 'fill="#141414" opacity=".38"');
|
||||
xml = xml.replace(/class="cls-1"/g, 'fill="url(#linear-gradient-2)" opacity=".53"');
|
||||
xml = xml.replace(/class="cls-2"/g, 'fill="url(#linear-gradient-3)" opacity=".53"');
|
||||
xml = xml.replace(
|
||||
/class="cls-1"/g,
|
||||
'fill="url(#linear-gradient-2)" opacity=".53"',
|
||||
);
|
||||
xml = xml.replace(
|
||||
/class="cls-2"/g,
|
||||
'fill="url(#linear-gradient-3)" opacity=".53"',
|
||||
);
|
||||
// Remove the <style> block to avoid unsupported CSS
|
||||
xml = xml.replace(/<style>[\s\S]*?<\/style>/, '');
|
||||
if (!cancelled) setIntroDbLogoXml(xml);
|
||||
|
|
@ -153,7 +205,11 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
|
|||
const introDbLogoIcon = introDbLogoXml ? (
|
||||
<SvgXml xml={introDbLogoXml} width={28} height={18} />
|
||||
) : (
|
||||
<MaterialIcons name="skip-next" size={18} color={currentTheme.colors.primary} />
|
||||
<MaterialIcons
|
||||
name="skip-next"
|
||||
size={18}
|
||||
color={currentTheme.colors.primary}
|
||||
/>
|
||||
);
|
||||
|
||||
// Bottom sheet refs
|
||||
|
|
@ -192,11 +248,11 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
|
|||
};
|
||||
|
||||
const hasVisibleItems = (itemIds: string[]) => {
|
||||
return itemIds.some(id => isItemVisible(id));
|
||||
return itemIds.some((id) => isItemVisible(id));
|
||||
};
|
||||
|
||||
const getLanguageName = (code: string) => {
|
||||
const lang = AVAILABLE_LANGUAGES.find(l => l.code === code);
|
||||
const lang = AVAILABLE_LANGUAGES.find((l) => l.code === code);
|
||||
return lang ? lang.name : code.toUpperCase();
|
||||
};
|
||||
|
||||
|
|
@ -217,7 +273,7 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
|
|||
opacity={0.5}
|
||||
/>
|
||||
),
|
||||
[]
|
||||
[],
|
||||
);
|
||||
|
||||
const handleSelectAudioLanguage = (code: string) => {
|
||||
|
|
@ -230,7 +286,9 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
|
|||
subtitleLanguageSheetRef.current?.dismiss();
|
||||
};
|
||||
|
||||
const handleSelectSubtitleSource = (value: 'internal' | 'external' | 'any') => {
|
||||
const handleSelectSubtitleSource = (
|
||||
value: 'internal' | 'external' | 'any',
|
||||
) => {
|
||||
updateSetting('subtitleSourcePreference', value);
|
||||
subtitleSourceSheetRef.current?.dismiss();
|
||||
};
|
||||
|
|
@ -238,13 +296,22 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
|
|||
return (
|
||||
<>
|
||||
{hasVisibleItems(['video_player']) && (
|
||||
<SettingsCard title={t('settings.sections.video_player')} isTablet={isTablet}>
|
||||
<SettingsCard
|
||||
title={t('settings.sections.video_player')}
|
||||
isTablet={isTablet}
|
||||
>
|
||||
{isItemVisible('video_player') && (
|
||||
<SettingItem
|
||||
title={t('settings.items.video_player')}
|
||||
description={Platform.OS === 'ios'
|
||||
? (settings?.preferredPlayer === 'internal' ? t('settings.items.built_in') : settings?.preferredPlayer?.toUpperCase() || t('settings.items.built_in'))
|
||||
: (settings?.useExternalPlayer ? t('settings.items.external') : t('settings.items.built_in'))
|
||||
description={
|
||||
Platform.OS === 'ios'
|
||||
? settings?.preferredPlayer === 'internal'
|
||||
? t('settings.items.built_in')
|
||||
: settings?.preferredPlayer?.toUpperCase() ||
|
||||
t('settings.items.built_in')
|
||||
: settings?.useExternalPlayer
|
||||
? t('settings.items.external')
|
||||
: t('settings.items.built_in')
|
||||
}
|
||||
icon="play-circle"
|
||||
renderControl={() => <ChevronRight />}
|
||||
|
|
@ -256,10 +323,17 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
|
|||
</SettingsCard>
|
||||
)}
|
||||
|
||||
<SettingsCard title={t('player.section_playback', { defaultValue: 'Playback' })} isTablet={isTablet}>
|
||||
<SettingsCard
|
||||
title={t('player.section_playback', { defaultValue: 'Playback' })}
|
||||
isTablet={isTablet}
|
||||
>
|
||||
<SettingItem
|
||||
title={t('player.skip_intro_settings_title', { defaultValue: 'Skip Intro' })}
|
||||
description={t('player.powered_by_introdb', { defaultValue: 'Powered by IntroDB' })}
|
||||
title={t('player.skip_intro_settings_title', {
|
||||
defaultValue: 'Skip Intro',
|
||||
})}
|
||||
description={t('player.powered_by_introdb', {
|
||||
defaultValue: 'Powered by IntroDB',
|
||||
})}
|
||||
customIcon={introDbLogoIcon}
|
||||
renderControl={() => (
|
||||
<CustomSwitch
|
||||
|
|
@ -273,10 +347,13 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
|
|||
</SettingsCard>
|
||||
|
||||
{/* IntroDB Contribution Section */}
|
||||
<SettingsCard title={t('settings.sections.introdb_contribution', { defaultValue: 'IntroDB Contribution' })} isTablet={isTablet}>
|
||||
<SettingsCard
|
||||
title={t('settings.sections.introdb_contribution')}
|
||||
isTablet={isTablet}
|
||||
>
|
||||
<SettingItem
|
||||
title={t('settings.items.enable_intro_submission', { defaultValue: 'Enable Intro Submission' })}
|
||||
description={t('settings.items.enable_intro_submission_desc', { defaultValue: 'Contribute timestamps to the community' })}
|
||||
title={t('settings.items.enable_intro_submission')}
|
||||
description={t('settings.items.enable_intro_submission_desc')}
|
||||
icon="flag"
|
||||
renderControl={() => (
|
||||
<CustomSwitch
|
||||
|
|
@ -291,14 +368,17 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
|
|||
{settings?.introSubmitEnabled && (
|
||||
<View style={styles.inputContainer}>
|
||||
<Text style={styles.inputLabel}>
|
||||
{t('settings.items.introdb_api_key', { defaultValue: 'INTRODB API KEY' })}
|
||||
{t('settings.items.introdb_api_key')}
|
||||
</Text>
|
||||
<View style={styles.apiKeyRow}>
|
||||
<TextInput
|
||||
style={[styles.input, { flex: 1, marginRight: 10, color: currentTheme.colors.highEmphasis }]}
|
||||
style={[
|
||||
styles.input,
|
||||
{ flex: 1, marginRight: 10, color: currentTheme.colors.highEmphasis },
|
||||
]}
|
||||
value={apiKeyInput}
|
||||
onChangeText={setApiKeyInput}
|
||||
placeholder="Enter your API key"
|
||||
placeholder={t('settings.items.introdb_key_placeholder')}
|
||||
placeholderTextColor={currentTheme.colors.mediumEmphasis}
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
|
|
@ -321,7 +401,10 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
|
|||
</SettingsCard>
|
||||
|
||||
{/* Audio & Subtitle Preferences */}
|
||||
<SettingsCard title={t('settings.sections.audio_subtitles')} isTablet={isTablet}>
|
||||
<SettingsCard
|
||||
title={t('settings.sections.audio_subtitles')}
|
||||
isTablet={isTablet}
|
||||
>
|
||||
<SettingItem
|
||||
title={t('settings.items.preferred_audio')}
|
||||
description={getLanguageName(settings?.preferredAudioLanguage || 'en')}
|
||||
|
|
@ -340,7 +423,9 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
|
|||
/>
|
||||
<SettingItem
|
||||
title={t('settings.items.subtitle_source')}
|
||||
description={getSourceLabel(settings?.subtitleSourcePreference || 'internal')}
|
||||
description={getSourceLabel(
|
||||
settings?.subtitleSourcePreference || 'internal',
|
||||
)}
|
||||
icon="layers"
|
||||
renderControl={() => <ChevronRight />}
|
||||
onPress={openSubtitleSourceSheet}
|
||||
|
|
@ -353,7 +438,9 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
|
|||
renderControl={() => (
|
||||
<CustomSwitch
|
||||
value={settings?.enableSubtitleAutoSelect ?? true}
|
||||
onValueChange={(value) => updateSetting('enableSubtitleAutoSelect', value)}
|
||||
onValueChange={(value) =>
|
||||
updateSetting('enableSubtitleAutoSelect', value)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
isLast
|
||||
|
|
@ -396,7 +483,10 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
|
|||
)}
|
||||
|
||||
{hasVisibleItems(['notifications']) && (
|
||||
<SettingsCard title={t('settings.sections.notifications')} isTablet={isTablet}>
|
||||
<SettingsCard
|
||||
title={t('settings.sections.notifications')}
|
||||
isTablet={isTablet}
|
||||
>
|
||||
{isItemVisible('notifications') && (
|
||||
<SettingItem
|
||||
title={t('settings.items.notifications')}
|
||||
|
|
@ -423,28 +513,38 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
|
|||
handleIndicatorStyle={{ backgroundColor: 'rgba(255,255,255,0.3)' }}
|
||||
>
|
||||
<View style={styles.sheetHeader}>
|
||||
<Text style={styles.sheetTitle}>{t('settings.items.preferred_audio')}</Text>
|
||||
<Text style={styles.sheetTitle}>
|
||||
{t('settings.items.preferred_audio')}
|
||||
</Text>
|
||||
</View>
|
||||
<BottomSheetScrollView contentContainerStyle={styles.sheetContent}>
|
||||
{AVAILABLE_LANGUAGES.map((lang) => {
|
||||
const isSelected = lang.code === (settings?.preferredAudioLanguage || 'en');
|
||||
const isSelected =
|
||||
lang.code === (settings?.preferredAudioLanguage || 'en');
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={lang.code}
|
||||
style={[
|
||||
styles.languageItem,
|
||||
isSelected && { backgroundColor: currentTheme.colors.primary + '20' }
|
||||
isSelected && { backgroundColor: currentTheme.colors.primary + '20' },
|
||||
]}
|
||||
onPress={() => handleSelectAudioLanguage(lang.code)}
|
||||
>
|
||||
<Text style={[styles.languageName, { color: isSelected ? currentTheme.colors.primary : '#fff' }]}>
|
||||
<Text
|
||||
style={[
|
||||
styles.languageName,
|
||||
{ color: isSelected ? currentTheme.colors.primary : '#fff' },
|
||||
]}
|
||||
>
|
||||
{lang.name}
|
||||
</Text>
|
||||
<Text style={styles.languageCode}>
|
||||
{lang.code.toUpperCase()}
|
||||
</Text>
|
||||
<Text style={styles.languageCode}>{lang.code.toUpperCase()}</Text>
|
||||
{isSelected && (
|
||||
<MaterialIcons name="check" size={20} color={currentTheme.colors.primary} />
|
||||
<MaterialIcons
|
||||
name="check"
|
||||
size={20}
|
||||
color={currentTheme.colors.primary}
|
||||
/>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
|
@ -464,28 +564,38 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
|
|||
handleIndicatorStyle={{ backgroundColor: 'rgba(255,255,255,0.3)' }}
|
||||
>
|
||||
<View style={styles.sheetHeader}>
|
||||
<Text style={styles.sheetTitle}>{t('settings.items.preferred_subtitle')}</Text>
|
||||
<Text style={styles.sheetTitle}>
|
||||
{t('settings.items.preferred_subtitle')}
|
||||
</Text>
|
||||
</View>
|
||||
<BottomSheetScrollView contentContainerStyle={styles.sheetContent}>
|
||||
{AVAILABLE_LANGUAGES.map((lang) => {
|
||||
const isSelected = lang.code === (settings?.preferredSubtitleLanguage || 'en');
|
||||
const isSelected =
|
||||
lang.code === (settings?.preferredSubtitleLanguage || 'en');
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={lang.code}
|
||||
style={[
|
||||
styles.languageItem,
|
||||
isSelected && { backgroundColor: currentTheme.colors.primary + '20' }
|
||||
isSelected && { backgroundColor: currentTheme.colors.primary + '20' },
|
||||
]}
|
||||
onPress={() => handleSelectSubtitleLanguage(lang.code)}
|
||||
>
|
||||
<Text style={[styles.languageName, { color: isSelected ? currentTheme.colors.primary : '#fff' }]}>
|
||||
<Text
|
||||
style={[
|
||||
styles.languageName,
|
||||
{ color: isSelected ? currentTheme.colors.primary : '#fff' },
|
||||
]}
|
||||
>
|
||||
{lang.name}
|
||||
</Text>
|
||||
<Text style={styles.languageCode}>
|
||||
{lang.code.toUpperCase()}
|
||||
</Text>
|
||||
<Text style={styles.languageCode}>{lang.code.toUpperCase()}</Text>
|
||||
{isSelected && (
|
||||
<MaterialIcons name="check" size={20} color={currentTheme.colors.primary} />
|
||||
<MaterialIcons
|
||||
name="check"
|
||||
size={20}
|
||||
color={currentTheme.colors.primary}
|
||||
/>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
|
@ -505,32 +615,53 @@ export const PlaybackSettingsContent: React.FC<PlaybackSettingsContentProps> = (
|
|||
handleIndicatorStyle={{ backgroundColor: 'rgba(255,255,255,0.3)' }}
|
||||
>
|
||||
<View style={styles.sheetHeader}>
|
||||
<Text style={styles.sheetTitle}>{t('settings.items.subtitle_source')}</Text>
|
||||
<Text style={styles.sheetTitle}>
|
||||
{t('settings.items.subtitle_source')}
|
||||
</Text>
|
||||
</View>
|
||||
<BottomSheetScrollView contentContainerStyle={styles.sheetContent}>
|
||||
{SUBTITLE_SOURCE_OPTIONS.map((option) => {
|
||||
const isSelected = option.value === (settings?.subtitleSourcePreference || 'internal');
|
||||
const isSelected =
|
||||
option.value === (settings?.subtitleSourcePreference || 'internal');
|
||||
return (
|
||||
<TouchableOpacity
|
||||
key={option.value}
|
||||
style={[
|
||||
styles.sourceItem,
|
||||
isSelected && { backgroundColor: currentTheme.colors.primary + '20', borderColor: currentTheme.colors.primary }
|
||||
isSelected && {
|
||||
backgroundColor: currentTheme.colors.primary + '20',
|
||||
borderColor: currentTheme.colors.primary,
|
||||
},
|
||||
]}
|
||||
onPress={() => handleSelectSubtitleSource(option.value as 'internal' | 'external' | 'any')}
|
||||
onPress={() =>
|
||||
handleSelectSubtitleSource(
|
||||
option.value as 'internal' | 'external' | 'any',
|
||||
)
|
||||
}
|
||||
>
|
||||
<View style={styles.sourceItemContent}>
|
||||
<Text style={[styles.sourceLabel, { color: isSelected ? currentTheme.colors.primary : '#fff' }]}>
|
||||
<Text
|
||||
style={[
|
||||
styles.sourceLabel,
|
||||
{ color: isSelected ? currentTheme.colors.primary : '#fff' },
|
||||
]}
|
||||
>
|
||||
{getSourceLabel(option.value)}
|
||||
</Text>
|
||||
<Text style={styles.sourceDescription}>
|
||||
{option.value === 'internal' && t('settings.options.internal_first_desc')}
|
||||
{option.value === 'external' && t('settings.options.external_first_desc')}
|
||||
{option.value === 'internal' &&
|
||||
t('settings.options.internal_first_desc')}
|
||||
{option.value === 'external' &&
|
||||
t('settings.options.external_first_desc')}
|
||||
{option.value === 'any' && t('settings.options.any_available_desc')}
|
||||
</Text>
|
||||
</View>
|
||||
{isSelected && (
|
||||
<MaterialIcons name="check" size={20} color={currentTheme.colors.primary} />
|
||||
<MaterialIcons
|
||||
name="check"
|
||||
size={20}
|
||||
color={currentTheme.colors.primary}
|
||||
/>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
|
@ -553,14 +684,26 @@ const PlaybackSettingsScreen: React.FC = () => {
|
|||
const screenIsTablet = width >= 768;
|
||||
|
||||
return (
|
||||
<View style={[styles.container, { backgroundColor: currentTheme.colors.darkBackground }]}>
|
||||
<View
|
||||
style={[
|
||||
styles.container,
|
||||
{ backgroundColor: currentTheme.colors.darkBackground },
|
||||
]}
|
||||
>
|
||||
<StatusBar barStyle="light-content" />
|
||||
<ScreenHeader title={t('settings.playback')} showBackButton onBackPress={() => navigation.goBack()} />
|
||||
<ScreenHeader
|
||||
title={t('settings.playback')}
|
||||
showBackButton
|
||||
onBackPress={() => navigation.goBack()}
|
||||
/>
|
||||
|
||||
<ScrollView
|
||||
style={styles.scrollView}
|
||||
showsVerticalScrollIndicator={false}
|
||||
contentContainerStyle={[styles.scrollContent, { paddingBottom: insets.bottom + 24 }]}
|
||||
contentContainerStyle={[
|
||||
styles.scrollContent,
|
||||
{ paddingBottom: insets.bottom + 24 },
|
||||
]}
|
||||
>
|
||||
<PlaybackSettingsContent isTablet={screenIsTablet} />
|
||||
</ScrollView>
|
||||
|
|
|
|||
Loading…
Reference in a new issue