critical bug fixes

This commit is contained in:
tapframe 2025-09-02 21:50:34 +05:30
parent ed82ef2ad1
commit dfb856f441
2 changed files with 150 additions and 81 deletions

View file

@ -7,6 +7,7 @@ import {
TouchableOpacity,
Platform,
InteractionManager,
AppState,
} from 'react-native';
import { MaterialIcons } from '@expo/vector-icons';
@ -703,6 +704,7 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
// Performance optimization: Refs for avoiding re-renders
const interactionComplete = useRef(false);
const [shouldLoadSecondaryData, setShouldLoadSecondaryData] = useState(false);
const appState = useRef(AppState.currentState);
// Image loading state with optimized management
const [imageError, setImageError] = useState(false);
@ -755,9 +757,12 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
// Handle fullscreen toggle
const handleFullscreenToggle = useCallback(async () => {
try {
logger.info('HeroSection', 'Fullscreen button pressed');
if (trailerVideoRef.current) {
// Use the native fullscreen player
await trailerVideoRef.current.presentFullscreenPlayer();
} else {
logger.warn('HeroSection', 'Trailer video ref not available');
}
} catch (error) {
logger.error('HeroSection', 'Error toggling fullscreen:', error);
@ -803,18 +808,32 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
setTrailerPreloaded(false);
try {
const url = await TrailerService.getTrailerUrl(metadata.name, metadata.year);
if (url) {
const bestUrl = TrailerService.getBestFormatUrl(url);
setTrailerUrl(bestUrl);
logger.info('HeroSection', `Trailer URL loaded for ${metadata.name}`);
} else {
logger.info('HeroSection', `No trailer found for ${metadata.name}`);
}
// Use requestIdleCallback or setTimeout to prevent blocking main thread
const fetchWithDelay = () => {
TrailerService.getTrailerUrl(metadata.name, metadata.year)
.then(url => {
if (url) {
const bestUrl = TrailerService.getBestFormatUrl(url);
setTrailerUrl(bestUrl);
logger.info('HeroSection', `Trailer URL loaded for ${metadata.name}`);
} else {
logger.info('HeroSection', `No trailer found for ${metadata.name}`);
}
})
.catch(error => {
logger.error('HeroSection', 'Error fetching trailer:', error);
setTrailerError(true);
})
.finally(() => {
setTrailerLoading(false);
});
};
// Delay trailer fetch to prevent blocking UI
setTimeout(fetchWithDelay, 100);
} catch (error) {
logger.error('HeroSection', 'Error fetching trailer:', error);
logger.error('HeroSection', 'Error in trailer fetch setup:', error);
setTrailerError(true);
} finally {
setTrailerLoading(false);
}
};
@ -982,18 +1001,50 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
return localWatched;
}, [watchProgress, isTraktAuthenticated]);
// App state management to prevent background ANR
useEffect(() => {
const handleAppStateChange = (nextAppState: any) => {
if (appState.current.match(/inactive|background/) && nextAppState === 'active') {
// App came to foreground
logger.info('HeroSection', 'App came to foreground');
} else if (appState.current === 'active' && nextAppState.match(/inactive|background/)) {
// App going to background - pause heavy operations
logger.info('HeroSection', 'App going to background - pausing operations');
setTrailerPlaying(false);
}
appState.current = nextAppState;
};
const subscription = AppState.addEventListener('change', handleAppStateChange);
return () => subscription?.remove();
}, []);
// Memory management and cleanup
useEffect(() => {
return () => {
// Reset animation values on unmount
imageOpacity.value = 1;
imageLoadOpacity.value = 0;
shimmerOpacity.value = 0.3;
interactionComplete.current = false;
// Reset animation values on unmount to prevent memory leaks
try {
imageOpacity.value = 1;
imageLoadOpacity.value = 0;
shimmerOpacity.value = 0.3;
trailerOpacity.value = 0;
thumbnailOpacity.value = 1;
actionButtonsOpacity.value = 1;
titleCardTranslateY.value = 0;
genreOpacity.value = 1;
watchProgressOpacity.value = 1;
buttonsOpacity.value = 1;
buttonsTranslateY.value = 0;
logoOpacity.value = 1;
heroOpacity.value = 1;
heroHeight.value = height * 0.6;
} catch (error) {
logger.error('HeroSection', 'Error cleaning up animation values:', error);
}
// Cleanup on unmount
interactionComplete.current = false;
};
}, []);
}, [imageOpacity, imageLoadOpacity, shimmerOpacity, trailerOpacity, thumbnailOpacity, actionButtonsOpacity, titleCardTranslateY, genreOpacity, watchProgressOpacity, buttonsOpacity, buttonsTranslateY, logoOpacity, heroOpacity, heroHeight]);
// Development-only performance monitoring
useEffect(() => {
@ -1072,6 +1123,7 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
muted={trailerMuted}
style={styles.absoluteFill}
hideLoadingSpinner={true}
hideControls={true}
onFullscreenToggle={handleFullscreenToggle}
onLoad={handleTrailerReady}
onError={handleTrailerError}
@ -1090,7 +1142,7 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
position: 'absolute',
top: Platform.OS === 'android' ? 40 : 50,
right: width >= 768 ? 32 : 16,
zIndex: 10,
zIndex: 1000,
opacity: trailerOpacity,
flexDirection: 'row',
gap: 8,
@ -1099,6 +1151,8 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
<TouchableOpacity
onPress={handleFullscreenToggle}
activeOpacity={0.7}
onPressIn={(e) => e.stopPropagation()}
onPressOut={(e) => e.stopPropagation()}
style={{
padding: 8,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
@ -1115,6 +1169,7 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
{/* Unmute button */}
<TouchableOpacity
onPress={() => {
logger.info('HeroSection', 'Mute toggle button pressed, current muted state:', trailerMuted);
updateSetting('trailerMuted', !trailerMuted);
if (trailerMuted) {
// When unmuting, hide action buttons, genre, title card, and watch progress
@ -1131,6 +1186,8 @@ const HeroSection: React.FC<HeroSectionProps> = memo(({
}
}}
activeOpacity={0.7}
onPressIn={(e) => e.stopPropagation()}
onPressOut={(e) => e.stopPropagation()}
style={{
padding: 8,
backgroundColor: 'rgba(0, 0, 0, 0.5)',

View file

@ -35,6 +35,7 @@ interface TrailerPlayerProps {
style?: any;
hideLoadingSpinner?: boolean;
onFullscreenToggle?: () => void;
hideControls?: boolean;
}
const TrailerPlayer = React.forwardRef<any, TrailerPlayerProps>(({
@ -49,6 +50,7 @@ const TrailerPlayer = React.forwardRef<any, TrailerPlayerProps>(({
style,
hideLoadingSpinner = false,
onFullscreenToggle,
hideControls = false,
}, ref) => {
const { currentTheme } = useTheme();
const videoRef = useRef<VideoRef>(null);
@ -177,8 +179,16 @@ const TrailerPlayer = React.forwardRef<any, TrailerPlayerProps>(({
if (hideControlsTimeout.current) {
clearTimeout(hideControlsTimeout.current);
}
// Reset all animated values to prevent memory leaks
try {
controlsOpacity.value = 0;
loadingOpacity.value = 0;
playButtonScale.value = 1;
} catch (error) {
logger.error('TrailerPlayer', 'Error cleaning up animation values:', error);
}
};
}, []);
}, [controlsOpacity, loadingOpacity, playButtonScale]);
// Forward the ref to the video element
React.useImperativeHandle(ref, () => ({
@ -269,80 +279,82 @@ const TrailerPlayer = React.forwardRef<any, TrailerPlayerProps>(({
</Animated.View>
)}
{/* Video controls overlay */}
<TouchableOpacity
style={styles.videoOverlay}
onPress={handleVideoPress}
activeOpacity={1}
>
<Animated.View style={[styles.controlsContainer, controlsAnimatedStyle]}>
{/* Top gradient */}
<LinearGradient
colors={['rgba(0,0,0,0.6)', 'transparent']}
style={styles.topGradient}
pointerEvents="none"
/>
{/* Video controls overlay */}
{!hideControls && (
<TouchableOpacity
style={styles.videoOverlay}
onPress={handleVideoPress}
activeOpacity={1}
>
<Animated.View style={[styles.controlsContainer, controlsAnimatedStyle]}>
{/* Top gradient */}
<LinearGradient
colors={['rgba(0,0,0,0.6)', 'transparent']}
style={styles.topGradient}
pointerEvents="none"
/>
{/* Center play/pause button */}
<View style={styles.centerControls}>
<Animated.View style={playButtonAnimatedStyle}>
<TouchableOpacity style={styles.playButton} onPress={handlePlayPause}>
<MaterialIcons
name={isPlaying ? 'pause' : 'play-arrow'}
size={isTablet ? 64 : 48}
color="white"
/>
</TouchableOpacity>
</Animated.View>
</View>
{/* Bottom controls */}
<LinearGradient
colors={['transparent', 'rgba(0,0,0,0.8)']}
style={styles.bottomGradient}
>
<View style={styles.bottomControls}>
{/* Progress bar */}
<View style={styles.progressContainer}>
<View style={styles.progressBar}>
<View
style={[styles.progressFill, { width: `${progressPercentage}%` }]}
/>
</View>
</View>
{/* Control buttons */}
<View style={styles.controlButtons}>
<TouchableOpacity style={styles.controlButton} onPress={handlePlayPause}>
{/* Center play/pause button */}
<View style={styles.centerControls}>
<Animated.View style={playButtonAnimatedStyle}>
<TouchableOpacity style={styles.playButton} onPress={handlePlayPause}>
<MaterialIcons
name={isPlaying ? 'pause' : 'play-arrow'}
size={isTablet ? 32 : 24}
size={isTablet ? 64 : 48}
color="white"
/>
</TouchableOpacity>
<TouchableOpacity style={styles.controlButton} onPress={handleMuteToggle}>
<MaterialIcons
name={isMuted ? 'volume-off' : 'volume-up'}
size={isTablet ? 32 : 24}
color="white"
/>
</TouchableOpacity>
{onFullscreenToggle && (
<TouchableOpacity style={styles.controlButton} onPress={onFullscreenToggle}>
</Animated.View>
</View>
{/* Bottom controls */}
<LinearGradient
colors={['transparent', 'rgba(0,0,0,0.8)']}
style={styles.bottomGradient}
>
<View style={styles.bottomControls}>
{/* Progress bar */}
<View style={styles.progressContainer}>
<View style={styles.progressBar}>
<View
style={[styles.progressFill, { width: `${progressPercentage}%` }]}
/>
</View>
</View>
{/* Control buttons */}
<View style={styles.controlButtons}>
<TouchableOpacity style={styles.controlButton} onPress={handlePlayPause}>
<MaterialIcons
name="fullscreen"
name={isPlaying ? 'pause' : 'play-arrow'}
size={isTablet ? 32 : 24}
color="white"
/>
</TouchableOpacity>
)}
<TouchableOpacity style={styles.controlButton} onPress={handleMuteToggle}>
<MaterialIcons
name={isMuted ? 'volume-off' : 'volume-up'}
size={isTablet ? 32 : 24}
color="white"
/>
</TouchableOpacity>
{onFullscreenToggle && (
<TouchableOpacity style={styles.controlButton} onPress={onFullscreenToggle}>
<MaterialIcons
name="fullscreen"
size={isTablet ? 32 : 24}
color="white"
/>
</TouchableOpacity>
)}
</View>
</View>
</View>
</LinearGradient>
</Animated.View>
</TouchableOpacity>
</LinearGradient>
</Animated.View>
</TouchableOpacity>
)}
</View>
);
});