mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-19 00:11:45 +00:00
alert orientation fix
This commit is contained in:
parent
2d5b1263b5
commit
5804959ddf
11 changed files with 242 additions and 214 deletions
|
|
@ -101,6 +101,7 @@ const AnnouncementOverlay: React.FC<AnnouncementOverlayProps> = ({
|
|||
transparent
|
||||
animationType="none"
|
||||
statusBarTranslucent
|
||||
supportedOrientations={['portrait', 'landscape']}
|
||||
onRequestClose={handleClose}
|
||||
>
|
||||
<View style={styles.overlay}>
|
||||
|
|
|
|||
|
|
@ -152,6 +152,7 @@ export const DropUpMenu = ({ visible, onClose, item, onOptionSelect, isSaved: is
|
|||
visible={visible}
|
||||
transparent
|
||||
animationType="none"
|
||||
supportedOrientations={['portrait', 'landscape']}
|
||||
onRequestClose={onClose}
|
||||
>
|
||||
<GestureHandlerRootView style={{ flex: 1 }}>
|
||||
|
|
|
|||
|
|
@ -1660,6 +1660,7 @@ const SeriesContentComponent: React.FC<SeriesContentProps> = ({
|
|||
animationType="fade"
|
||||
onRequestClose={closeEpisodeActionMenu}
|
||||
statusBarTranslucent
|
||||
supportedOrientations={['portrait', 'landscape']}
|
||||
>
|
||||
<Pressable
|
||||
style={{
|
||||
|
|
|
|||
|
|
@ -293,7 +293,7 @@ const TrailersSection: React.FC<TrailersSectionProps> = memo(({
|
|||
// Auto-select the first available category, preferring "Trailer"
|
||||
const availableCategories = Object.keys(categorized);
|
||||
const preferredCategory = availableCategories.includes('Trailer') ? 'Trailer' :
|
||||
availableCategories.includes('Teaser') ? 'Teaser' : availableCategories[0];
|
||||
availableCategories.includes('Teaser') ? 'Teaser' : availableCategories[0];
|
||||
setSelectedCategory(preferredCategory);
|
||||
}
|
||||
} catch (err) {
|
||||
|
|
@ -559,6 +559,7 @@ const TrailersSection: React.FC<TrailersSectionProps> = memo(({
|
|||
visible={dropdownVisible}
|
||||
transparent={true}
|
||||
animationType="fade"
|
||||
supportedOrientations={['portrait', 'landscape']}
|
||||
onRequestClose={() => setDropdownVisible(false)}
|
||||
>
|
||||
<TouchableOpacity
|
||||
|
|
@ -601,13 +602,13 @@ const TrailersSection: React.FC<TrailersSectionProps> = memo(({
|
|||
color={currentTheme.colors.primary}
|
||||
/>
|
||||
</View>
|
||||
<Text style={[
|
||||
styles.dropdownItemText,
|
||||
{
|
||||
color: currentTheme.colors.highEmphasis,
|
||||
fontSize: isTV ? 18 : isLargeTablet ? 17 : isTablet ? 16 : 16
|
||||
}
|
||||
]}>
|
||||
<Text style={[
|
||||
styles.dropdownItemText,
|
||||
{
|
||||
color: currentTheme.colors.highEmphasis,
|
||||
fontSize: isTV ? 18 : isLargeTablet ? 17 : isTablet ? 16 : 16
|
||||
}
|
||||
]}>
|
||||
{formatTrailerType(category)}
|
||||
</Text>
|
||||
<Text style={[
|
||||
|
|
|
|||
|
|
@ -135,10 +135,10 @@ const AndroidVideoPlayer: React.FC = () => {
|
|||
|
||||
// Helper to get dynamic volume icon
|
||||
const getVolumeIcon = (value: number) => {
|
||||
if (value === 0) return 'volume-off';
|
||||
if (value < 0.3) return 'volume-mute';
|
||||
if (value < 0.6) return 'volume-down';
|
||||
return 'volume-up';
|
||||
if (value === 0) return 'volume-off';
|
||||
if (value < 0.3) return 'volume-mute';
|
||||
if (value < 0.6) return 'volume-down';
|
||||
return 'volume-up';
|
||||
};
|
||||
|
||||
// Helper to get dynamic brightness icon
|
||||
|
|
@ -3432,8 +3432,6 @@ const AndroidVideoPlayer: React.FC = () => {
|
|||
buffered={buffered}
|
||||
formatTime={formatTime}
|
||||
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) */}
|
||||
|
|
@ -3441,45 +3439,45 @@ const AndroidVideoPlayer: React.FC = () => {
|
|||
<View style={localStyles.gestureIndicatorContainer}>
|
||||
{/* Dynamic Icon */}
|
||||
<View
|
||||
style={[
|
||||
localStyles.iconWrapper,
|
||||
{
|
||||
// Conditional Background Color Logic
|
||||
backgroundColor: gestureControls.showVolumeOverlay && volume === 0
|
||||
? 'rgba(242, 184, 181)'
|
||||
: 'rgba(59, 59, 59)'
|
||||
}
|
||||
]}
|
||||
>
|
||||
<MaterialIcons
|
||||
name={
|
||||
gestureControls.showVolumeOverlay
|
||||
? getVolumeIcon(volume)
|
||||
: getBrightnessIcon(brightness)
|
||||
}
|
||||
size={24} // Reduced size to fit inside a 32-40px circle better
|
||||
color={
|
||||
gestureControls.showVolumeOverlay && volume === 0
|
||||
? 'rgba(96, 20, 16)' // Bright RED for MUTE icon itself
|
||||
: 'rgba(255, 255, 255)' // White for all other states
|
||||
}
|
||||
/>
|
||||
style={[
|
||||
localStyles.iconWrapper,
|
||||
{
|
||||
// Conditional Background Color Logic
|
||||
backgroundColor: gestureControls.showVolumeOverlay && volume === 0
|
||||
? 'rgba(242, 184, 181)'
|
||||
: 'rgba(59, 59, 59)'
|
||||
}
|
||||
]}
|
||||
>
|
||||
<MaterialIcons
|
||||
name={
|
||||
gestureControls.showVolumeOverlay
|
||||
? getVolumeIcon(volume)
|
||||
: getBrightnessIcon(brightness)
|
||||
}
|
||||
size={24} // Reduced size to fit inside a 32-40px circle better
|
||||
color={
|
||||
gestureControls.showVolumeOverlay && volume === 0
|
||||
? 'rgba(96, 20, 16)' // Bright RED for MUTE icon itself
|
||||
: 'rgba(255, 255, 255)' // White for all other states
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
|
||||
{/* Text Label: Shows "Muted" or percentage */}
|
||||
<Text
|
||||
style={[
|
||||
localStyles.gestureText,
|
||||
// Conditional Text Color Logic
|
||||
gestureControls.showVolumeOverlay && volume === 0 && { color: 'rgba(242, 184, 181)' } // Light RED for "Muted"
|
||||
]}
|
||||
>
|
||||
{/* Conditional Text Content Logic */}
|
||||
{gestureControls.showVolumeOverlay && volume === 0
|
||||
? "Muted" // Display "Muted" when volume is 0
|
||||
: `${Math.round((gestureControls.showVolumeOverlay ? volume : brightness) * 100)}%` // Display percentage otherwise
|
||||
}
|
||||
</Text>
|
||||
<Text
|
||||
style={[
|
||||
localStyles.gestureText,
|
||||
// Conditional Text Color Logic
|
||||
gestureControls.showVolumeOverlay && volume === 0 && { color: 'rgba(242, 184, 181)' } // Light RED for "Muted"
|
||||
]}
|
||||
>
|
||||
{/* Conditional Text Content Logic */}
|
||||
{gestureControls.showVolumeOverlay && volume === 0
|
||||
? "Muted" // Display "Muted" when volume is 0
|
||||
: `${Math.round((gestureControls.showVolumeOverlay ? volume : brightness) * 100)}%` // Display percentage otherwise
|
||||
}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
|
||||
|
|
@ -4067,32 +4065,32 @@ const AndroidVideoPlayer: React.FC = () => {
|
|||
// New styles for the gesture indicator
|
||||
const localStyles = StyleSheet.create({
|
||||
gestureIndicatorContainer: {
|
||||
position: 'absolute',
|
||||
top: '4%', // Adjust this for vertical position
|
||||
alignSelf: 'center', // Adjust this for horizontal position
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'rgba(25, 25, 25)', // Dark pill background
|
||||
borderRadius: 70,
|
||||
paddingHorizontal: 15,
|
||||
paddingVertical: 15,
|
||||
zIndex: 2000, // Very high z-index to ensure visibility
|
||||
minWidth: 120, // Adjusted min width since bar is removed
|
||||
position: 'absolute',
|
||||
top: '4%', // Adjust this for vertical position
|
||||
alignSelf: 'center', // Adjust this for horizontal position
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'rgba(25, 25, 25)', // Dark pill background
|
||||
borderRadius: 70,
|
||||
paddingHorizontal: 15,
|
||||
paddingVertical: 15,
|
||||
zIndex: 2000, // Very high z-index to ensure visibility
|
||||
minWidth: 120, // Adjusted min width since bar is removed
|
||||
},
|
||||
iconWrapper: {
|
||||
borderRadius: 50, // Makes it a perfect circle (set to a high number)
|
||||
width: 40, // Define the diameter of the circle
|
||||
height: 40, // Define the diameter of the circle
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginRight: 12, // Margin to separate icon circle from percentage text
|
||||
borderRadius: 50, // Makes it a perfect circle (set to a high number)
|
||||
width: 40, // Define the diameter of the circle
|
||||
height: 40, // Define the diameter of the circle
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginRight: 12, // Margin to separate icon circle from percentage text
|
||||
},
|
||||
gestureText: {
|
||||
color: '#FFFFFF',
|
||||
fontSize: 18,
|
||||
fontWeight: 'normal',
|
||||
minWidth: 35,
|
||||
textAlign: 'right',
|
||||
color: '#FFFFFF',
|
||||
fontSize: 18,
|
||||
fontWeight: 'normal',
|
||||
minWidth: 35,
|
||||
textAlign: 'right',
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1413,6 +1413,7 @@ const AddonsScreen = () => {
|
|||
visible={showConfirmModal}
|
||||
transparent
|
||||
animationType="fade"
|
||||
supportedOrientations={['portrait', 'landscape']}
|
||||
onRequestClose={() => {
|
||||
setShowConfirmModal(false);
|
||||
setAddonDetails(null);
|
||||
|
|
|
|||
|
|
@ -685,6 +685,7 @@ const CatalogSettingsScreen = () => {
|
|||
animationType="fade"
|
||||
transparent={true}
|
||||
visible={isRenameModalVisible}
|
||||
supportedOrientations={['portrait', 'landscape']}
|
||||
onRequestClose={() => {
|
||||
setIsRenameModalVisible(false);
|
||||
setCatalogToRename(null);
|
||||
|
|
|
|||
|
|
@ -1946,6 +1946,7 @@ const PluginsScreen: React.FC = () => {
|
|||
visible={showHelpModal}
|
||||
transparent={true}
|
||||
animationType="fade"
|
||||
supportedOrientations={['portrait', 'landscape']}
|
||||
onRequestClose={() => setShowHelpModal(false)}
|
||||
>
|
||||
<View style={styles.modalOverlay}>
|
||||
|
|
@ -1978,6 +1979,7 @@ const PluginsScreen: React.FC = () => {
|
|||
visible={showAddRepositoryModal}
|
||||
transparent={true}
|
||||
animationType="fade"
|
||||
supportedOrientations={['portrait', 'landscape']}
|
||||
onRequestClose={() => setShowAddRepositoryModal(false)}
|
||||
>
|
||||
<View style={styles.modalOverlay}>
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ const ProfilesScreen: React.FC = () => {
|
|||
) => {
|
||||
setAlertTitle(title);
|
||||
setAlertMessage(message);
|
||||
setAlertActions(actions && actions.length > 0 ? actions : [{ label: 'OK', onPress: () => {} }]);
|
||||
setAlertActions(actions && actions.length > 0 ? actions : [{ label: 'OK', onPress: () => { } }]);
|
||||
setAlertVisible(true);
|
||||
};
|
||||
|
||||
|
|
@ -164,7 +164,7 @@ const ProfilesScreen: React.FC = () => {
|
|||
'Delete Profile',
|
||||
'Are you sure you want to delete this profile? This action cannot be undone.',
|
||||
[
|
||||
{ label: 'Cancel', onPress: () => {} },
|
||||
{ label: 'Cancel', onPress: () => { } },
|
||||
{
|
||||
label: 'Delete',
|
||||
onPress: () => {
|
||||
|
|
@ -281,6 +281,7 @@ const ProfilesScreen: React.FC = () => {
|
|||
visible={showAddModal}
|
||||
transparent
|
||||
animationType="fade"
|
||||
supportedOrientations={['portrait', 'landscape']}
|
||||
onRequestClose={() => setShowAddModal(false)}
|
||||
>
|
||||
<View style={styles.modalOverlay}>
|
||||
|
|
|
|||
|
|
@ -132,17 +132,27 @@ export const StreamsScreen = () => {
|
|||
const { showSuccess, showInfo } = useToast();
|
||||
|
||||
// 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 prevDimensionsRef = useRef({ width: dimensions.width, height: dimensions.height });
|
||||
|
||||
useEffect(() => {
|
||||
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();
|
||||
}, []);
|
||||
|
||||
// Memoize tablet detection to prevent recalculation on every render
|
||||
const deviceWidth = dimensions.width;
|
||||
const isTablet = deviceWidth >= 768;
|
||||
const isTablet = useMemo(() => deviceWidth >= 768, [deviceWidth]);
|
||||
|
||||
// Add refs to prevent excessive updates and duplicate loads
|
||||
const isMounted = useRef(true);
|
||||
|
|
@ -303,6 +313,9 @@ export const StreamsScreen = () => {
|
|||
}, []);
|
||||
|
||||
// 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(() => {
|
||||
// Skip processing if component is unmounting
|
||||
if (!isMounted.current) return;
|
||||
|
|
@ -317,14 +330,21 @@ export const StreamsScreen = () => {
|
|||
|
||||
if (providersWithStreams.length > 0) {
|
||||
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
|
||||
setAvailableProviders(prevProviders => {
|
||||
const newProviders = new Set([...prevProviders, ...providersWithStreamsSet]);
|
||||
if (__DEV__) console.log('[StreamsScreen] availableProviders ->', Array.from(newProviders));
|
||||
return newProviders;
|
||||
});
|
||||
// Check if we actually have new providers before triggering state update
|
||||
const hasNewProviders = providersWithStreams.some(
|
||||
provider => !prevProvidersRef.current.has(provider)
|
||||
);
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -828,6 +828,7 @@ const TMDBSettingsScreen = () => {
|
|||
visible={languagePickerVisible}
|
||||
transparent
|
||||
animationType="slide"
|
||||
supportedOrientations={['portrait', 'landscape']}
|
||||
onRequestClose={() => setLanguagePickerVisible(false)}
|
||||
>
|
||||
<TouchableWithoutFeedback onPress={() => setLanguagePickerVisible(false)}>
|
||||
|
|
@ -955,42 +956,42 @@ const TMDBSettingsScreen = () => {
|
|||
return (
|
||||
<>
|
||||
{filteredLanguages.map(({ code, label, native }) => (
|
||||
<TouchableOpacity
|
||||
key={code}
|
||||
onPress={() => { updateSetting('tmdbLanguagePreference', code); setLanguagePickerVisible(false); }}
|
||||
style={[
|
||||
styles.languageItem,
|
||||
settings.tmdbLanguagePreference === code && styles.selectedLanguageItem
|
||||
]}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<View style={styles.languageContent}>
|
||||
<View style={styles.languageInfo}>
|
||||
<Text style={[
|
||||
styles.languageName,
|
||||
settings.tmdbLanguagePreference === code && styles.selectedLanguageName,
|
||||
{
|
||||
color: settings.tmdbLanguagePreference === code ? currentTheme.colors.primary : currentTheme.colors.text,
|
||||
}
|
||||
]}>
|
||||
{native}
|
||||
</Text>
|
||||
<Text style={[
|
||||
styles.languageCode,
|
||||
settings.tmdbLanguagePreference === code && styles.selectedLanguageCode,
|
||||
{
|
||||
color: settings.tmdbLanguagePreference === code ? currentTheme.colors.primary : currentTheme.colors.mediumEmphasis,
|
||||
}
|
||||
]}>
|
||||
{label} • {code.toUpperCase()}
|
||||
</Text>
|
||||
</View>
|
||||
{settings.tmdbLanguagePreference === code && (
|
||||
<View style={styles.checkmarkContainer}>
|
||||
<MaterialIcons name="check-circle" size={24} color={currentTheme.colors.primary} />
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
<TouchableOpacity
|
||||
key={code}
|
||||
onPress={() => { updateSetting('tmdbLanguagePreference', code); setLanguagePickerVisible(false); }}
|
||||
style={[
|
||||
styles.languageItem,
|
||||
settings.tmdbLanguagePreference === code && styles.selectedLanguageItem
|
||||
]}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<View style={styles.languageContent}>
|
||||
<View style={styles.languageInfo}>
|
||||
<Text style={[
|
||||
styles.languageName,
|
||||
settings.tmdbLanguagePreference === code && styles.selectedLanguageName,
|
||||
{
|
||||
color: settings.tmdbLanguagePreference === code ? currentTheme.colors.primary : currentTheme.colors.text,
|
||||
}
|
||||
]}>
|
||||
{native}
|
||||
</Text>
|
||||
<Text style={[
|
||||
styles.languageCode,
|
||||
settings.tmdbLanguagePreference === code && styles.selectedLanguageCode,
|
||||
{
|
||||
color: settings.tmdbLanguagePreference === code ? currentTheme.colors.primary : currentTheme.colors.mediumEmphasis,
|
||||
}
|
||||
]}>
|
||||
{label} • {code.toUpperCase()}
|
||||
</Text>
|
||||
</View>
|
||||
{settings.tmdbLanguagePreference === code && (
|
||||
<View style={styles.checkmarkContainer}>
|
||||
<MaterialIcons name="check-circle" size={24} color={currentTheme.colors.primary} />
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
{languageSearch.length > 0 && filteredLanguages.length === 0 && (
|
||||
|
|
|
|||
Loading…
Reference in a new issue