Stability improvement on catalogsection

This commit is contained in:
tapframe 2025-09-29 14:55:22 +05:30
parent f86e6256a7
commit ebb7d4cec6
2 changed files with 8 additions and 25 deletions

View file

@ -1,4 +1,4 @@
import React, { useCallback, useMemo, useRef, useState } from 'react'; import React, { useCallback, useMemo, useRef } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, Platform, Dimensions } from 'react-native'; import { View, Text, StyleSheet, TouchableOpacity, Platform, Dimensions } from 'react-native';
import { FlashList } from '@shopify/flash-list'; import { FlashList } from '@shopify/flash-list';
import { NavigationProp, useNavigation } from '@react-navigation/native'; import { NavigationProp, useNavigation } from '@react-navigation/native';
@ -56,32 +56,19 @@ const POSTER_WIDTH = posterLayout.posterWidth;
const CatalogSection = ({ catalog }: CatalogSectionProps) => { const CatalogSection = ({ catalog }: CatalogSectionProps) => {
const navigation = useNavigation<NavigationProp<RootStackParamList>>(); const navigation = useNavigation<NavigationProp<RootStackParamList>>();
const { currentTheme } = useTheme(); const { currentTheme } = useTheme();
// Simplified visibility tracking - just load all images immediately for better performance
const [hasLoaded, setHasLoaded] = useState(false);
// Load all images after a short delay to prevent blocking initial render
React.useEffect(() => {
const timer = setTimeout(() => {
setHasLoaded(true);
}, 100);
return () => clearTimeout(timer);
}, []);
const handleContentPress = useCallback((id: string, type: string) => { const handleContentPress = useCallback((id: string, type: string) => {
navigation.navigate('Metadata', { id, type, addonId: catalog.addon }); navigation.navigate('Metadata', { id, type, addonId: catalog.addon });
}, [navigation, catalog.addon]); }, [navigation, catalog.addon]);
const renderContentItem = useCallback(({ item, index }: { item: StreamingContent, index: number }) => { const renderContentItem = useCallback(({ item }: { item: StreamingContent, index: number }) => {
// Load images immediately for better scrolling performance
return ( return (
<ContentItem <ContentItem
item={item} item={item}
onPress={handleContentPress} onPress={handleContentPress}
shouldLoadImage={hasLoaded}
deferMs={index * 10} // Small stagger to prevent blocking
/> />
); );
}, [handleContentPress, hasLoaded]); }, [handleContentPress]);
// Memoize the ItemSeparatorComponent to prevent re-creation // Memoize the ItemSeparatorComponent to prevent re-creation
const ItemSeparator = useCallback(() => <View style={{ width: 8 }} />, []); const ItemSeparator = useCallback(() => <View style={{ width: 8 }} />, []);

View file

@ -152,15 +152,10 @@ const ContentItem = ({ item, onPress, shouldLoadImage: shouldLoadImageProp, defe
return item.poster; return item.poster;
}, [item.poster, retryCount, item.id]); }, [item.poster, retryCount, item.id]);
// Smoothly fade in content when settings are ready // Avoid strong fade animations that can appear as flicker on mount/scroll
useEffect(() => { useEffect(() => {
if (isLoaded) { if (isLoaded) {
fadeInOpacity.setValue(0); fadeInOpacity.setValue(1);
Animated.timing(fadeInOpacity, {
toValue: 1,
duration: 180,
useNativeDriver: true,
}).start();
} }
}, [isLoaded, fadeInOpacity]); }, [isLoaded, fadeInOpacity]);
@ -196,14 +191,14 @@ const ContentItem = ({ item, onPress, shouldLoadImage: shouldLoadImageProp, defe
delayLongPress={300} delayLongPress={300}
> >
<View ref={itemRef} style={[styles.contentItemContainer, { borderRadius: posterRadius }] }> <View ref={itemRef} style={[styles.contentItemContainer, { borderRadius: posterRadius }] }>
{/* Always load image for horizontal scrolling to prevent blank posters */} {/* Image with lightweight placeholder to reduce flicker */}
{item.poster ? ( {item.poster ? (
<ExpoImage <ExpoImage
source={{ uri: optimizedPosterUrl }} source={{ uri: optimizedPosterUrl }}
style={[styles.poster, { backgroundColor: currentTheme.colors.elevation1, borderRadius: posterRadius }]} style={[styles.poster, { backgroundColor: currentTheme.colors.elevation1, borderRadius: posterRadius }]}
contentFit="cover" contentFit="cover"
cachePolicy={Platform.OS === 'android' ? 'disk' : 'memory-disk'} cachePolicy={Platform.OS === 'android' ? 'disk' : 'memory-disk'}
transition={100} // Faster transition for scrolling transition={0}
allowDownscaling allowDownscaling
priority="normal" // Normal priority for horizontal scrolling priority="normal" // Normal priority for horizontal scrolling
onLoad={() => { onLoad={() => {
@ -222,6 +217,7 @@ const ContentItem = ({ item, onPress, shouldLoadImage: shouldLoadImageProp, defe
setImageLoaded(false); setImageLoaded(false);
}} }}
recyclingKey={item.id} // Add recycling key for better performance recyclingKey={item.id} // Add recycling key for better performance
placeholder={PLACEHOLDER_BLURHASH}
/> />
) : ( ) : (
// Show placeholder for items without posters // Show placeholder for items without posters