alert orientation fix

This commit is contained in:
tapframe 2025-12-19 23:38:33 +05:30
parent 2d5b1263b5
commit 5804959ddf
11 changed files with 242 additions and 214 deletions

View file

@ -101,6 +101,7 @@ const AnnouncementOverlay: React.FC<AnnouncementOverlayProps> = ({
transparent transparent
animationType="none" animationType="none"
statusBarTranslucent statusBarTranslucent
supportedOrientations={['portrait', 'landscape']}
onRequestClose={handleClose} onRequestClose={handleClose}
> >
<View style={styles.overlay}> <View style={styles.overlay}>

View file

@ -152,6 +152,7 @@ export const DropUpMenu = ({ visible, onClose, item, onOptionSelect, isSaved: is
visible={visible} visible={visible}
transparent transparent
animationType="none" animationType="none"
supportedOrientations={['portrait', 'landscape']}
onRequestClose={onClose} onRequestClose={onClose}
> >
<GestureHandlerRootView style={{ flex: 1 }}> <GestureHandlerRootView style={{ flex: 1 }}>

View file

@ -1660,6 +1660,7 @@ const SeriesContentComponent: React.FC<SeriesContentProps> = ({
animationType="fade" animationType="fade"
onRequestClose={closeEpisodeActionMenu} onRequestClose={closeEpisodeActionMenu}
statusBarTranslucent statusBarTranslucent
supportedOrientations={['portrait', 'landscape']}
> >
<Pressable <Pressable
style={{ style={{

View file

@ -293,7 +293,7 @@ const TrailersSection: React.FC<TrailersSectionProps> = memo(({
// Auto-select the first available category, preferring "Trailer" // Auto-select the first available category, preferring "Trailer"
const availableCategories = Object.keys(categorized); const availableCategories = Object.keys(categorized);
const preferredCategory = availableCategories.includes('Trailer') ? 'Trailer' : const preferredCategory = availableCategories.includes('Trailer') ? 'Trailer' :
availableCategories.includes('Teaser') ? 'Teaser' : availableCategories[0]; availableCategories.includes('Teaser') ? 'Teaser' : availableCategories[0];
setSelectedCategory(preferredCategory); setSelectedCategory(preferredCategory);
} }
} catch (err) { } catch (err) {
@ -559,6 +559,7 @@ const TrailersSection: React.FC<TrailersSectionProps> = memo(({
visible={dropdownVisible} visible={dropdownVisible}
transparent={true} transparent={true}
animationType="fade" animationType="fade"
supportedOrientations={['portrait', 'landscape']}
onRequestClose={() => setDropdownVisible(false)} onRequestClose={() => setDropdownVisible(false)}
> >
<TouchableOpacity <TouchableOpacity
@ -601,13 +602,13 @@ const TrailersSection: React.FC<TrailersSectionProps> = memo(({
color={currentTheme.colors.primary} color={currentTheme.colors.primary}
/> />
</View> </View>
<Text style={[ <Text style={[
styles.dropdownItemText, styles.dropdownItemText,
{ {
color: currentTheme.colors.highEmphasis, color: currentTheme.colors.highEmphasis,
fontSize: isTV ? 18 : isLargeTablet ? 17 : isTablet ? 16 : 16 fontSize: isTV ? 18 : isLargeTablet ? 17 : isTablet ? 16 : 16
} }
]}> ]}>
{formatTrailerType(category)} {formatTrailerType(category)}
</Text> </Text>
<Text style={[ <Text style={[

View file

@ -135,10 +135,10 @@ const AndroidVideoPlayer: React.FC = () => {
// Helper to get dynamic volume icon // Helper to get dynamic volume icon
const getVolumeIcon = (value: number) => { const getVolumeIcon = (value: number) => {
if (value === 0) return 'volume-off'; if (value === 0) return 'volume-off';
if (value < 0.3) return 'volume-mute'; if (value < 0.3) return 'volume-mute';
if (value < 0.6) return 'volume-down'; if (value < 0.6) return 'volume-down';
return 'volume-up'; return 'volume-up';
}; };
// Helper to get dynamic brightness icon // Helper to get dynamic brightness icon
@ -3432,8 +3432,6 @@ const AndroidVideoPlayer: React.FC = () => {
buffered={buffered} buffered={buffered}
formatTime={formatTime} formatTime={formatTime}
playerBackend={useVLC ? 'VLC' : 'ExoPlayer'} playerBackend={useVLC ? 'VLC' : 'ExoPlayer'}
nextLoadingTitle={nextLoadingTitle}
controlsFixedOffset={Math.min(Dimensions.get('window').width, Dimensions.get('window').height) >= 768 ? 120 : 100}
/> />
{/* Combined Volume & Brightness Gesture Indicator - NEW PILL STYLE (No Bar) */} {/* Combined Volume & Brightness Gesture Indicator - NEW PILL STYLE (No Bar) */}
@ -3441,45 +3439,45 @@ const AndroidVideoPlayer: React.FC = () => {
<View style={localStyles.gestureIndicatorContainer}> <View style={localStyles.gestureIndicatorContainer}>
{/* Dynamic Icon */} {/* Dynamic Icon */}
<View <View
style={[ style={[
localStyles.iconWrapper, localStyles.iconWrapper,
{ {
// Conditional Background Color Logic // Conditional Background Color Logic
backgroundColor: gestureControls.showVolumeOverlay && volume === 0 backgroundColor: gestureControls.showVolumeOverlay && volume === 0
? 'rgba(242, 184, 181)' ? 'rgba(242, 184, 181)'
: 'rgba(59, 59, 59)' : 'rgba(59, 59, 59)'
} }
]} ]}
> >
<MaterialIcons <MaterialIcons
name={ name={
gestureControls.showVolumeOverlay gestureControls.showVolumeOverlay
? getVolumeIcon(volume) ? getVolumeIcon(volume)
: getBrightnessIcon(brightness) : getBrightnessIcon(brightness)
} }
size={24} // Reduced size to fit inside a 32-40px circle better size={24} // Reduced size to fit inside a 32-40px circle better
color={ color={
gestureControls.showVolumeOverlay && volume === 0 gestureControls.showVolumeOverlay && volume === 0
? 'rgba(96, 20, 16)' // Bright RED for MUTE icon itself ? 'rgba(96, 20, 16)' // Bright RED for MUTE icon itself
: 'rgba(255, 255, 255)' // White for all other states : 'rgba(255, 255, 255)' // White for all other states
} }
/> />
</View> </View>
{/* Text Label: Shows "Muted" or percentage */} {/* Text Label: Shows "Muted" or percentage */}
<Text <Text
style={[ style={[
localStyles.gestureText, localStyles.gestureText,
// Conditional Text Color Logic // Conditional Text Color Logic
gestureControls.showVolumeOverlay && volume === 0 && { color: 'rgba(242, 184, 181)' } // Light RED for "Muted" gestureControls.showVolumeOverlay && volume === 0 && { color: 'rgba(242, 184, 181)' } // Light RED for "Muted"
]} ]}
> >
{/* Conditional Text Content Logic */} {/* Conditional Text Content Logic */}
{gestureControls.showVolumeOverlay && volume === 0 {gestureControls.showVolumeOverlay && volume === 0
? "Muted" // Display "Muted" when volume is 0 ? "Muted" // Display "Muted" when volume is 0
: `${Math.round((gestureControls.showVolumeOverlay ? volume : brightness) * 100)}%` // Display percentage otherwise : `${Math.round((gestureControls.showVolumeOverlay ? volume : brightness) * 100)}%` // Display percentage otherwise
} }
</Text> </Text>
</View> </View>
)} )}
@ -4067,32 +4065,32 @@ const AndroidVideoPlayer: React.FC = () => {
// New styles for the gesture indicator // New styles for the gesture indicator
const localStyles = StyleSheet.create({ const localStyles = StyleSheet.create({
gestureIndicatorContainer: { gestureIndicatorContainer: {
position: 'absolute', position: 'absolute',
top: '4%', // Adjust this for vertical position top: '4%', // Adjust this for vertical position
alignSelf: 'center', // Adjust this for horizontal position alignSelf: 'center', // Adjust this for horizontal position
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
backgroundColor: 'rgba(25, 25, 25)', // Dark pill background backgroundColor: 'rgba(25, 25, 25)', // Dark pill background
borderRadius: 70, borderRadius: 70,
paddingHorizontal: 15, paddingHorizontal: 15,
paddingVertical: 15, paddingVertical: 15,
zIndex: 2000, // Very high z-index to ensure visibility zIndex: 2000, // Very high z-index to ensure visibility
minWidth: 120, // Adjusted min width since bar is removed minWidth: 120, // Adjusted min width since bar is removed
}, },
iconWrapper: { iconWrapper: {
borderRadius: 50, // Makes it a perfect circle (set to a high number) borderRadius: 50, // Makes it a perfect circle (set to a high number)
width: 40, // Define the diameter of the circle width: 40, // Define the diameter of the circle
height: 40, // Define the diameter of the circle height: 40, // Define the diameter of the circle
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
marginRight: 12, // Margin to separate icon circle from percentage text marginRight: 12, // Margin to separate icon circle from percentage text
}, },
gestureText: { gestureText: {
color: '#FFFFFF', color: '#FFFFFF',
fontSize: 18, fontSize: 18,
fontWeight: 'normal', fontWeight: 'normal',
minWidth: 35, minWidth: 35,
textAlign: 'right', textAlign: 'right',
}, },
}); });

View file

@ -1413,6 +1413,7 @@ const AddonsScreen = () => {
visible={showConfirmModal} visible={showConfirmModal}
transparent transparent
animationType="fade" animationType="fade"
supportedOrientations={['portrait', 'landscape']}
onRequestClose={() => { onRequestClose={() => {
setShowConfirmModal(false); setShowConfirmModal(false);
setAddonDetails(null); setAddonDetails(null);

View file

@ -685,6 +685,7 @@ const CatalogSettingsScreen = () => {
animationType="fade" animationType="fade"
transparent={true} transparent={true}
visible={isRenameModalVisible} visible={isRenameModalVisible}
supportedOrientations={['portrait', 'landscape']}
onRequestClose={() => { onRequestClose={() => {
setIsRenameModalVisible(false); setIsRenameModalVisible(false);
setCatalogToRename(null); setCatalogToRename(null);

View file

@ -1946,6 +1946,7 @@ const PluginsScreen: React.FC = () => {
visible={showHelpModal} visible={showHelpModal}
transparent={true} transparent={true}
animationType="fade" animationType="fade"
supportedOrientations={['portrait', 'landscape']}
onRequestClose={() => setShowHelpModal(false)} onRequestClose={() => setShowHelpModal(false)}
> >
<View style={styles.modalOverlay}> <View style={styles.modalOverlay}>
@ -1978,6 +1979,7 @@ const PluginsScreen: React.FC = () => {
visible={showAddRepositoryModal} visible={showAddRepositoryModal}
transparent={true} transparent={true}
animationType="fade" animationType="fade"
supportedOrientations={['portrait', 'landscape']}
onRequestClose={() => setShowAddRepositoryModal(false)} onRequestClose={() => setShowAddRepositoryModal(false)}
> >
<View style={styles.modalOverlay}> <View style={styles.modalOverlay}>

View file

@ -52,7 +52,7 @@ const ProfilesScreen: React.FC = () => {
) => { ) => {
setAlertTitle(title); setAlertTitle(title);
setAlertMessage(message); setAlertMessage(message);
setAlertActions(actions && actions.length > 0 ? actions : [{ label: 'OK', onPress: () => {} }]); setAlertActions(actions && actions.length > 0 ? actions : [{ label: 'OK', onPress: () => { } }]);
setAlertVisible(true); setAlertVisible(true);
}; };
@ -164,7 +164,7 @@ const ProfilesScreen: React.FC = () => {
'Delete Profile', 'Delete Profile',
'Are you sure you want to delete this profile? This action cannot be undone.', 'Are you sure you want to delete this profile? This action cannot be undone.',
[ [
{ label: 'Cancel', onPress: () => {} }, { label: 'Cancel', onPress: () => { } },
{ {
label: 'Delete', label: 'Delete',
onPress: () => { onPress: () => {
@ -281,6 +281,7 @@ const ProfilesScreen: React.FC = () => {
visible={showAddModal} visible={showAddModal}
transparent transparent
animationType="fade" animationType="fade"
supportedOrientations={['portrait', 'landscape']}
onRequestClose={() => setShowAddModal(false)} onRequestClose={() => setShowAddModal(false)}
> >
<View style={styles.modalOverlay}> <View style={styles.modalOverlay}>

View file

@ -132,17 +132,27 @@ export const StreamsScreen = () => {
const { showSuccess, showInfo } = useToast(); const { showSuccess, showInfo } = useToast();
// Add dimension listener and tablet detection // Add dimension listener and tablet detection
// Use a ref to track previous dimensions to avoid unnecessary re-renders
const [dimensions, setDimensions] = useState(Dimensions.get('window')); const [dimensions, setDimensions] = useState(Dimensions.get('window'));
const prevDimensionsRef = useRef({ width: dimensions.width, height: dimensions.height });
useEffect(() => { useEffect(() => {
const subscription = Dimensions.addEventListener('change', ({ window }) => { const subscription = Dimensions.addEventListener('change', ({ window }) => {
setDimensions(window); // Only update state if dimensions actually changed (with 1px tolerance)
const widthChanged = Math.abs(window.width - prevDimensionsRef.current.width) > 1;
const heightChanged = Math.abs(window.height - prevDimensionsRef.current.height) > 1;
if (widthChanged || heightChanged) {
prevDimensionsRef.current = { width: window.width, height: window.height };
setDimensions(window);
}
}); });
return () => subscription?.remove(); return () => subscription?.remove();
}, []); }, []);
// Memoize tablet detection to prevent recalculation on every render
const deviceWidth = dimensions.width; const deviceWidth = dimensions.width;
const isTablet = deviceWidth >= 768; const isTablet = useMemo(() => deviceWidth >= 768, [deviceWidth]);
// Add refs to prevent excessive updates and duplicate loads // Add refs to prevent excessive updates and duplicate loads
const isMounted = useRef(true); const isMounted = useRef(true);
@ -303,6 +313,9 @@ export const StreamsScreen = () => {
}, []); }, []);
// Monitor streams loading and update available providers immediately // Monitor streams loading and update available providers immediately
// Use a ref to track the previous providers to avoid unnecessary state updates
const prevProvidersRef = useRef<Set<string>>(new Set());
useEffect(() => { useEffect(() => {
// Skip processing if component is unmounting // Skip processing if component is unmounting
if (!isMounted.current) return; if (!isMounted.current) return;
@ -317,14 +330,21 @@ export const StreamsScreen = () => {
if (providersWithStreams.length > 0) { if (providersWithStreams.length > 0) {
logger.log(`📊 Providers with streams: ${providersWithStreams.join(', ')}`); logger.log(`📊 Providers with streams: ${providersWithStreams.join(', ')}`);
const providersWithStreamsSet = new Set(providersWithStreams);
// Only update if we have new providers, don't remove existing ones during loading // Check if we actually have new providers before triggering state update
setAvailableProviders(prevProviders => { const hasNewProviders = providersWithStreams.some(
const newProviders = new Set([...prevProviders, ...providersWithStreamsSet]); provider => !prevProvidersRef.current.has(provider)
if (__DEV__) console.log('[StreamsScreen] availableProviders ->', Array.from(newProviders)); );
return newProviders;
}); if (hasNewProviders) {
setAvailableProviders(prevProviders => {
const newProviders = new Set([...prevProviders, ...providersWithStreams]);
// Update ref to track current providers
prevProvidersRef.current = newProviders;
if (__DEV__) console.log('[StreamsScreen] availableProviders ->', Array.from(newProviders));
return newProviders;
});
}
} }
// Update loading states for individual providers // Update loading states for individual providers

View file

@ -828,6 +828,7 @@ const TMDBSettingsScreen = () => {
visible={languagePickerVisible} visible={languagePickerVisible}
transparent transparent
animationType="slide" animationType="slide"
supportedOrientations={['portrait', 'landscape']}
onRequestClose={() => setLanguagePickerVisible(false)} onRequestClose={() => setLanguagePickerVisible(false)}
> >
<TouchableWithoutFeedback onPress={() => setLanguagePickerVisible(false)}> <TouchableWithoutFeedback onPress={() => setLanguagePickerVisible(false)}>
@ -955,42 +956,42 @@ const TMDBSettingsScreen = () => {
return ( return (
<> <>
{filteredLanguages.map(({ code, label, native }) => ( {filteredLanguages.map(({ code, label, native }) => (
<TouchableOpacity <TouchableOpacity
key={code} key={code}
onPress={() => { updateSetting('tmdbLanguagePreference', code); setLanguagePickerVisible(false); }} onPress={() => { updateSetting('tmdbLanguagePreference', code); setLanguagePickerVisible(false); }}
style={[ style={[
styles.languageItem, styles.languageItem,
settings.tmdbLanguagePreference === code && styles.selectedLanguageItem settings.tmdbLanguagePreference === code && styles.selectedLanguageItem
]} ]}
activeOpacity={0.7} activeOpacity={0.7}
> >
<View style={styles.languageContent}> <View style={styles.languageContent}>
<View style={styles.languageInfo}> <View style={styles.languageInfo}>
<Text style={[ <Text style={[
styles.languageName, styles.languageName,
settings.tmdbLanguagePreference === code && styles.selectedLanguageName, settings.tmdbLanguagePreference === code && styles.selectedLanguageName,
{ {
color: settings.tmdbLanguagePreference === code ? currentTheme.colors.primary : currentTheme.colors.text, color: settings.tmdbLanguagePreference === code ? currentTheme.colors.primary : currentTheme.colors.text,
} }
]}> ]}>
{native} {native}
</Text> </Text>
<Text style={[ <Text style={[
styles.languageCode, styles.languageCode,
settings.tmdbLanguagePreference === code && styles.selectedLanguageCode, settings.tmdbLanguagePreference === code && styles.selectedLanguageCode,
{ {
color: settings.tmdbLanguagePreference === code ? currentTheme.colors.primary : currentTheme.colors.mediumEmphasis, color: settings.tmdbLanguagePreference === code ? currentTheme.colors.primary : currentTheme.colors.mediumEmphasis,
} }
]}> ]}>
{label} {code.toUpperCase()} {label} {code.toUpperCase()}
</Text> </Text>
</View> </View>
{settings.tmdbLanguagePreference === code && ( {settings.tmdbLanguagePreference === code && (
<View style={styles.checkmarkContainer}> <View style={styles.checkmarkContainer}>
<MaterialIcons name="check-circle" size={24} color={currentTheme.colors.primary} /> <MaterialIcons name="check-circle" size={24} color={currentTheme.colors.primary} />
</View> </View>
)} )}
</View> </View>
</TouchableOpacity> </TouchableOpacity>
))} ))}
{languageSearch.length > 0 && filteredLanguages.length === 0 && ( {languageSearch.length > 0 && filteredLanguages.length === 0 && (