From 6906ad99b7dc8abab7934a416195b8d0a181aeeb Mon Sep 17 00:00:00 2001 From: tapframe Date: Wed, 31 Dec 2025 01:15:04 +0530 Subject: [PATCH] updated to gorhom bottom sheet --- .../settings/PlaybackSettingsScreen.tsx | 385 +++++++++--------- 1 file changed, 195 insertions(+), 190 deletions(-) diff --git a/src/screens/settings/PlaybackSettingsScreen.tsx b/src/screens/settings/PlaybackSettingsScreen.tsx index a8e7609..892aa07 100644 --- a/src/screens/settings/PlaybackSettingsScreen.tsx +++ b/src/screens/settings/PlaybackSettingsScreen.tsx @@ -1,5 +1,5 @@ -import React, { useState, useCallback } from 'react'; -import { View, StyleSheet, ScrollView, StatusBar, Platform, Text, TouchableOpacity, Modal, FlatList } from 'react-native'; +import React, { useState, useCallback, useMemo, useRef } from 'react'; +import { View, StyleSheet, ScrollView, StatusBar, Platform, Text, TouchableOpacity } from 'react-native'; import { useNavigation, useFocusEffect } from '@react-navigation/native'; import { NavigationProp } from '@react-navigation/native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; @@ -10,6 +10,7 @@ import ScreenHeader from '../../components/common/ScreenHeader'; 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'; // Available languages for audio/subtitle selection const AVAILABLE_LANGUAGES = [ @@ -53,145 +54,6 @@ const SUBTITLE_SOURCE_OPTIONS = [ { value: 'any', label: 'Any Available', description: 'Use first available subtitle track' }, ]; -interface LanguagePickerModalProps { - visible: boolean; - onClose: () => void; - selectedLanguage: string; - onSelectLanguage: (code: string) => void; - title: string; -} - -const LanguagePickerModal: React.FC = ({ - visible, - onClose, - selectedLanguage, - onSelectLanguage, - title, -}) => { - const { currentTheme } = useTheme(); - const insets = useSafeAreaInsets(); - - const renderItem = ({ item }: { item: { code: string; name: string } }) => { - const isSelected = item.code === selectedLanguage; - return ( - { - onSelectLanguage(item.code); - onClose(); - }} - > - - {item.name} - - - {item.code.toUpperCase()} - - {isSelected && ( - - )} - - ); - }; - - return ( - - - - - {title} - - - - - item.code} - showsVerticalScrollIndicator={false} - contentContainerStyle={styles.languageList} - /> - - - - ); -}; - -interface SubtitleSourceModalProps { - visible: boolean; - onClose: () => void; - selectedSource: string; - onSelectSource: (value: 'internal' | 'external' | 'any') => void; -} - -const SubtitleSourceModal: React.FC = ({ - visible, - onClose, - selectedSource, - onSelectSource, -}) => { - const { currentTheme } = useTheme(); - const insets = useSafeAreaInsets(); - - return ( - - - - - Subtitle Source Priority - - - - - - {SUBTITLE_SOURCE_OPTIONS.map((option) => { - const isSelected = option.value === selectedSource; - return ( - { - onSelectSource(option.value as 'internal' | 'external' | 'any'); - onClose(); - }} - > - - - {option.label} - - - {option.description} - - - {isSelected && ( - - )} - - ); - })} - - - - - ); -}; - const PlaybackSettingsScreen: React.FC = () => { const navigation = useNavigation>(); const { currentTheme } = useTheme(); @@ -199,10 +61,33 @@ const PlaybackSettingsScreen: React.FC = () => { const insets = useSafeAreaInsets(); const config = useRealtimeConfig(); - // Modal states - const [showAudioLanguageModal, setShowAudioLanguageModal] = useState(false); - const [showSubtitleLanguageModal, setShowSubtitleLanguageModal] = useState(false); - const [showSubtitleSourceModal, setShowSubtitleSourceModal] = useState(false); + // Bottom sheet refs + const audioLanguageSheetRef = useRef(null); + const subtitleLanguageSheetRef = useRef(null); + const subtitleSourceSheetRef = useRef(null); + + // Snap points + const languageSnapPoints = useMemo(() => ['70%'], []); + const sourceSnapPoints = useMemo(() => ['45%'], []); + + // Handlers to present sheets - ensure only one is open at a time + const openAudioLanguageSheet = useCallback(() => { + subtitleLanguageSheetRef.current?.dismiss(); + subtitleSourceSheetRef.current?.dismiss(); + setTimeout(() => audioLanguageSheetRef.current?.present(), 100); + }, []); + + const openSubtitleLanguageSheet = useCallback(() => { + audioLanguageSheetRef.current?.dismiss(); + subtitleSourceSheetRef.current?.dismiss(); + setTimeout(() => subtitleLanguageSheetRef.current?.present(), 100); + }, []); + + const openSubtitleSourceSheet = useCallback(() => { + audioLanguageSheetRef.current?.dismiss(); + subtitleLanguageSheetRef.current?.dismiss(); + setTimeout(() => subtitleSourceSheetRef.current?.present(), 100); + }, []); const isItemVisible = (itemId: string) => { if (!config?.items) return true; @@ -225,6 +110,34 @@ const PlaybackSettingsScreen: React.FC = () => { return option ? option.label : 'Internal First'; }; + // Render backdrop for bottom sheets + const renderBackdrop = useCallback( + (props: any) => ( + + ), + [] + ); + + const handleSelectAudioLanguage = (code: string) => { + updateSetting('preferredAudioLanguage', code); + audioLanguageSheetRef.current?.dismiss(); + }; + + const handleSelectSubtitleLanguage = (code: string) => { + updateSetting('preferredSubtitleLanguage', code); + subtitleLanguageSheetRef.current?.dismiss(); + }; + + const handleSelectSubtitleSource = (value: 'internal' | 'external' | 'any') => { + updateSetting('subtitleSourcePreference', value); + subtitleSourceSheetRef.current?.dismiss(); + }; + return ( @@ -260,21 +173,21 @@ const PlaybackSettingsScreen: React.FC = () => { description={getLanguageName(settings?.preferredAudioLanguage || 'en')} icon="volume-2" renderControl={() => } - onPress={() => setShowAudioLanguageModal(true)} + onPress={openAudioLanguageSheet} /> } - onPress={() => setShowSubtitleLanguageModal(true)} + onPress={openSubtitleLanguageSheet} /> } - onPress={() => setShowSubtitleSourceModal(true)} + onPress={openSubtitleSourceSheet} /> { )} - {/* Language Picker Modals */} - setShowAudioLanguageModal(false)} - selectedLanguage={settings?.preferredAudioLanguage || 'en'} - onSelectLanguage={(code) => updateSetting('preferredAudioLanguage', code)} - title="Preferred Audio Language" - /> - setShowSubtitleLanguageModal(false)} - selectedLanguage={settings?.preferredSubtitleLanguage || 'en'} - onSelectLanguage={(code) => updateSetting('preferredSubtitleLanguage', code)} - title="Preferred Subtitle Language" - /> - setShowSubtitleSourceModal(false)} - selectedSource={settings?.subtitleSourcePreference || 'internal'} - onSelectSource={(value) => updateSetting('subtitleSourcePreference', value)} - /> + {/* Audio Language Bottom Sheet */} + + + Preferred Audio Language + + + {AVAILABLE_LANGUAGES.map((lang) => { + const isSelected = lang.code === (settings?.preferredAudioLanguage || 'en'); + return ( + handleSelectAudioLanguage(lang.code)} + > + + {lang.name} + + + {lang.code.toUpperCase()} + + {isSelected && ( + + )} + + ); + })} + + + + {/* Subtitle Language Bottom Sheet */} + + + Preferred Subtitle Language + + + {AVAILABLE_LANGUAGES.map((lang) => { + const isSelected = lang.code === (settings?.preferredSubtitleLanguage || 'en'); + return ( + handleSelectSubtitleLanguage(lang.code)} + > + + {lang.name} + + + {lang.code.toUpperCase()} + + {isSelected && ( + + )} + + ); + })} + + + + {/* Subtitle Source Priority Bottom Sheet */} + + + Subtitle Source Priority + + + {SUBTITLE_SOURCE_OPTIONS.map((option) => { + const isSelected = option.value === (settings?.subtitleSourcePreference || 'internal'); + return ( + handleSelectSubtitleSource(option.value as 'internal' | 'external' | 'any')} + > + + + {option.label} + + + {option.description} + + + {isSelected && ( + + )} + + ); + })} + + ); }; @@ -373,35 +389,23 @@ const styles = StyleSheet.create({ scrollContent: { paddingTop: 16, }, - modalOverlay: { - flex: 1, - backgroundColor: 'rgba(0,0,0,0.7)', - justifyContent: 'flex-end', - }, - modalContent: { - borderTopLeftRadius: 20, - borderTopRightRadius: 20, - maxHeight: '70%', - }, - modalHeader: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - padding: 20, + sheetHeader: { + paddingHorizontal: 20, + paddingTop: 12, + paddingBottom: 20, borderBottomWidth: 1, borderBottomColor: 'rgba(255,255,255,0.1)', + marginBottom: 8, }, - modalTitle: { + sheetTitle: { color: '#fff', fontSize: 18, fontWeight: '700', }, - closeButton: { - padding: 4, - }, - languageList: { + sheetContent: { paddingHorizontal: 16, - paddingBottom: 16, + paddingTop: 12, + paddingBottom: 24, }, languageItem: { flexDirection: 'row', @@ -419,6 +423,7 @@ const styles = StyleSheet.create({ languageCode: { fontSize: 12, marginRight: 12, + color: 'rgba(255,255,255,0.5)', }, sourceItem: { flexDirection: 'row',