homescreen optimization

This commit is contained in:
tapframe 2025-09-21 16:35:36 +05:30
parent 52740c26de
commit c1503f0614
2 changed files with 95 additions and 49 deletions

View file

@ -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);
});
}
}, []);

View file

@ -80,29 +80,38 @@ const detectMkvViaHead = async (url: string, headers?: Record<string, string>) =
};
// 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 (
<Animated.View style={[style, animatedStyle]}>
<Image
@ -115,30 +124,38 @@ const AnimatedImage = memo(({
);
});
const AnimatedText = memo(({
children,
style,
const AnimatedText = memo(({
children,
style,
delay = 0,
numberOfLines
}: {
children: React.ReactNode;
style: any;
}: {
children: React.ReactNode;
style: any;
delay?: number;
numberOfLines?: 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 (
<Animated.Text style={[style, animatedStyle]} numberOfLines={numberOfLines}>
{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 (
<Animated.View style={[style, animatedStyle]}>
{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