From c1503f0614f29ba11faa8b87ec3378d8e055c869 Mon Sep 17 00:00:00 2001 From: tapframe Date: Sun, 21 Sep 2025 16:35:36 +0530 Subject: [PATCH] homescreen optimization --- src/screens/HomeScreen.tsx | 48 +++++++++++------- src/screens/StreamsScreen.tsx | 96 ++++++++++++++++++++++++----------- 2 files changed, 95 insertions(+), 49 deletions(-) diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx index 9137b58e..d6b305f8 100644 --- a/src/screens/HomeScreen.tsx +++ b/src/screens/HomeScreen.tsx @@ -15,7 +15,8 @@ import { Image, Modal, Pressable, - Alert + Alert, + InteractionManager } from 'react-native'; import { FlashList } from '@shopify/flash-list'; import { useNavigation, useFocusEffect } from '@react-navigation/native'; @@ -146,8 +147,10 @@ const HomeScreen = () => { stremioService.getInstalledAddonsAsync() ]); - // Set hasAddons state based on whether we have any addons - setHasAddons(addons.length > 0); + // Set hasAddons state based on whether we have any addons - ensure on main thread + InteractionManager.runAfterInteractions(() => { + setHasAddons(addons.length > 0); + }); const catalogSettings = catalogSettingsJson ? JSON.parse(catalogSettingsJson) : {}; @@ -245,23 +248,28 @@ const HomeScreen = () => { items }; - // Update the catalog at its specific position - setCatalogs(prevCatalogs => { - const newCatalogs = [...prevCatalogs]; - newCatalogs[currentIndex] = catalogContent; - return newCatalogs; + // Update the catalog at its specific position - ensure on main thread + InteractionManager.runAfterInteractions(() => { + setCatalogs(prevCatalogs => { + const newCatalogs = [...prevCatalogs]; + newCatalogs[currentIndex] = catalogContent; + return newCatalogs; + }); }); } } catch (error) { if (__DEV__) console.error(`[HomeScreen] Failed to load ${catalog.name} from ${addon.name}:`, error); } finally { - setLoadedCatalogCount(prev => { - const next = prev + 1; - // Exit loading screen as soon as first catalog finishes - if (prev === 0) { - setCatalogsLoading(false); - } - return next; + // Update loading count - ensure on main thread + InteractionManager.runAfterInteractions(() => { + setLoadedCatalogCount(prev => { + const next = prev + 1; + // Exit loading screen as soon as first catalog finishes + if (prev === 0) { + setCatalogsLoading(false); + } + return next; + }); }); } }; @@ -275,14 +283,18 @@ const HomeScreen = () => { totalCatalogsRef.current = catalogIndex; - // Initialize catalogs array with proper length - setCatalogs(new Array(catalogIndex).fill(null)); + // Initialize catalogs array with proper length - ensure on main thread + InteractionManager.runAfterInteractions(() => { + setCatalogs(new Array(catalogIndex).fill(null)); + }); // Start processing the catalog queue processCatalogQueue(); } catch (error) { if (__DEV__) console.error('[HomeScreen] Error in progressive catalog loading:', error); - setCatalogsLoading(false); + InteractionManager.runAfterInteractions(() => { + setCatalogsLoading(false); + }); } }, []); diff --git a/src/screens/StreamsScreen.tsx b/src/screens/StreamsScreen.tsx index 0e1da92d..98b6caa1 100644 --- a/src/screens/StreamsScreen.tsx +++ b/src/screens/StreamsScreen.tsx @@ -80,29 +80,38 @@ const detectMkvViaHead = async (url: string, headers?: Record) = }; // Animated Components -const AnimatedImage = memo(({ - source, - style, - contentFit, - onLoad -}: { - source: { uri: string } | undefined; - style: any; - contentFit: any; +const AnimatedImage = memo(({ + source, + style, + contentFit, + onLoad +}: { + source: { uri: string } | undefined; + style: any; + contentFit: any; onLoad?: () => void; }) => { const opacity = useSharedValue(0); - + const animatedStyle = useAnimatedStyle(() => ({ opacity: opacity.value, })); - + useEffect(() => { if (source?.uri) { opacity.value = withTiming(1, { duration: 300 }); + } else { + opacity.value = 0; } }, [source?.uri]); - + + // Cleanup on unmount + useEffect(() => { + return () => { + opacity.value = 0; + }; + }, []); + return ( { const opacity = useSharedValue(0); const translateY = useSharedValue(20); - + const animatedStyle = useAnimatedStyle(() => ({ opacity: opacity.value, transform: [{ translateY: translateY.value }], })); - + useEffect(() => { opacity.value = withDelay(delay, withTiming(1, { duration: 250 })); translateY.value = withDelay(delay, withTiming(0, { duration: 250 })); + }, [delay]); + + // Cleanup on unmount + useEffect(() => { + return () => { + opacity.value = 0; + translateY.value = 20; + }; }, []); - + return ( {children} @@ -146,28 +163,36 @@ const AnimatedText = memo(({ ); }); -const AnimatedView = memo(({ - children, - style, - delay = 0 -}: { - children: React.ReactNode; - style?: any; +const AnimatedView = memo(({ + children, + style, + delay = 0 +}: { + children: React.ReactNode; + style?: any; delay?: number; }) => { const opacity = useSharedValue(0); const translateY = useSharedValue(20); - + const animatedStyle = useAnimatedStyle(() => ({ opacity: opacity.value, transform: [{ translateY: translateY.value }], })); - + useEffect(() => { opacity.value = withDelay(delay, withTiming(1, { duration: 250 })); translateY.value = withDelay(delay, withTiming(0, { duration: 250 })); + }, [delay]); + + // Cleanup on unmount + useEffect(() => { + return () => { + opacity.value = 0; + translateY.value = 20; + }; }, []); - + return ( {children} @@ -388,6 +413,7 @@ const ProviderFilter = memo(({ initialNumToRender={5} maxToRenderPerBatch={3} windowSize={3} + removeClippedSubviews={true} getItemLayout={(data, index) => ({ length: 100, // Approximate width of each item offset: 100 * index, @@ -1598,6 +1624,9 @@ export const StreamsScreen = () => { useEffect(() => { return () => { isMounted.current = false; + // Clear scraper logo cache to free memory + scraperLogoCache.clear(); + scraperLogoCachePromise = null; }; }, []); @@ -1856,6 +1885,11 @@ export const StreamsScreen = () => { windowSize={3} removeClippedSubviews={true} showsVerticalScrollIndicator={false} + getItemLayout={(data, index) => ({ + length: 78, // Approximate height of StreamCard (68 minHeight + 10 marginBottom) + offset: 78 * index, + index, + })} /> ) : ( // Empty section placeholder