diff --git a/src/components/home/CatalogSection.tsx b/src/components/home/CatalogSection.tsx
index cbf6cf63..ffdbf0a1 100644
--- a/src/components/home/CatalogSection.tsx
+++ b/src/components/home/CatalogSection.tsx
@@ -61,11 +61,15 @@ const CatalogSection = ({ catalog }: CatalogSectionProps) => {
navigation.navigate('Metadata', { id, type, addonId: catalog.addon });
}, [navigation, catalog.addon]);
- const renderContentItem = useCallback(({ item }: { item: StreamingContent, index: number }) => {
+ const renderContentItem = useCallback(({ item, index }: { item: StreamingContent, index: number }) => {
+ // Only load images for the first few items eagerly; others defer based on viewability
+ const eager = index < 6;
return (
);
}, [handleContentPress]);
@@ -108,8 +112,7 @@ const CatalogSection = ({ catalog }: CatalogSectionProps) => {
ItemSeparatorComponent={ItemSeparator}
onEndReachedThreshold={0.7}
onEndReached={() => {}}
- scrollEventThrottle={16}
- estimatedItemSize={POSTER_WIDTH + 8}
+ scrollEventThrottle={32}
/>
);
diff --git a/src/components/home/ContentItem.tsx b/src/components/home/ContentItem.tsx
index 3893384b..80331119 100644
--- a/src/components/home/ContentItem.tsx
+++ b/src/components/home/ContentItem.tsx
@@ -10,6 +10,8 @@ import { DropUpMenu } from './DropUpMenu';
interface ContentItemProps {
item: StreamingContent;
onPress: (id: string, type: string) => void;
+ shouldLoadImage?: boolean;
+ deferMs?: number;
}
const { width } = Dimensions.get('window');
@@ -56,12 +58,12 @@ const POSTER_WIDTH = posterLayout.posterWidth;
const PLACEHOLDER_BLURHASH = 'LEHV6nWB2yk8pyo0adR*.7kCMdnj';
-const ContentItem = ({ item, onPress }: ContentItemProps) => {
+const ContentItem = ({ item, onPress, shouldLoadImage: shouldLoadImageProp, deferMs = 0 }: ContentItemProps) => {
const [menuVisible, setMenuVisible] = useState(false);
const [isWatched, setIsWatched] = useState(false);
const [imageLoaded, setImageLoaded] = useState(false);
const [imageError, setImageError] = useState(false);
- const [shouldLoadImage, setShouldLoadImage] = useState(false);
+ const [shouldLoadImageState, setShouldLoadImageState] = useState(false);
const [retryCount, setRetryCount] = useState(0);
const { currentTheme } = useTheme();
const { settings } = useSettings();
@@ -111,14 +113,22 @@ const ContentItem = ({ item, onPress }: ContentItemProps) => {
setMenuVisible(false);
}, []);
- // Lazy load images - only load when likely to be visible
+ // Lazy load images - only load when asked by parent (viewability) or after small defer
useEffect(() => {
+ if (shouldLoadImageProp !== undefined) {
+ if (shouldLoadImageProp) {
+ const t = setTimeout(() => setShouldLoadImageState(true), deferMs);
+ return () => clearTimeout(t);
+ } else {
+ setShouldLoadImageState(false);
+ }
+ return;
+ }
const timer = setTimeout(() => {
- setShouldLoadImage(true);
- }, 50); // Reduced delay for faster loading
-
+ setShouldLoadImageState(true);
+ }, 80);
return () => clearTimeout(timer);
- }, []);
+ }, [shouldLoadImageProp, deferMs]);
// Get optimized poster URL for smaller tiles
const getOptimizedPosterUrl = useCallback((originalUrl: string) => {
@@ -158,12 +168,12 @@ const ContentItem = ({ item, onPress }: ContentItemProps) => {
>
{/* Only load image when shouldLoadImage is true (lazy loading) */}
- {shouldLoadImage && item.poster ? (
+ {(shouldLoadImageProp ?? shouldLoadImageState) && item.poster ? (
{
let catalogIndex = 0;
// Limit concurrent catalog loading to prevent overwhelming the system
- const MAX_CONCURRENT_CATALOGS = 1; // Single catalog at a time to minimize heating
+ const MAX_CONCURRENT_CATALOGS = 1; // Single catalog at a time to minimize heating/memory
let activeCatalogLoads = 0;
const catalogQueue: (() => Promise)[] = [];
@@ -196,8 +196,9 @@ const HomeScreen = () => {
const metas = await stremioService.getCatalog(manifest, catalog.type, catalog.id, 1);
if (metas && metas.length > 0) {
- // Limit items per catalog to reduce memory usage
- const limitedMetas = metas.slice(0, 30);
+ // Aggressively limit items per catalog on Android to reduce memory usage
+ const limit = Platform.OS === 'android' ? 18 : 30;
+ const limitedMetas = metas.slice(0, limit);
const items = limitedMetas.map((meta: any) => ({
id: meta.id,
@@ -217,7 +218,7 @@ const HomeScreen = () => {
certification: meta.certification
}));
- // Skip prefetching to reduce memory pressure
+ // Skip prefetching to reduce memory pressure (keep disabled)
// Resolve custom display name; if custom exists, use as-is
const originalName = catalog.name || catalog.id;
let displayName = await getCatalogDisplayName(addon.id, catalog.type, catalog.id, originalName);
@@ -282,7 +283,7 @@ const HomeScreen = () => {
// Initialize catalogs array with proper length
setCatalogs(new Array(catalogIndex).fill(null));
- // Start processing the catalog queue (parallel fetching continues in background)
+ // Start processing the catalog queue
processCatalogQueue();
} catch (error) {
if (__DEV__) console.error('[HomeScreen] Error in progressive catalog loading:', error);
@@ -650,7 +651,7 @@ const HomeScreen = () => {
const renderListItem = useCallback(({ item, index }: { item: HomeScreenListItem, index: number }) => {
const wrapper = (child: React.ReactNode) => (
-
+
{child}
);