diff --git a/src/screens/SearchScreen.tsx b/src/screens/SearchScreen.tsx
index fc116c5a..13b623af 100644
--- a/src/screens/SearchScreen.tsx
+++ b/src/screens/SearchScreen.tsx
@@ -381,27 +381,41 @@ const SearchScreen = () => {
} catch {}
const handle = catalogService.startLiveSearch(searchQuery, async (section: AddonSearchResults) => {
- // Append/update this addon section immediately
+ // Append/update this addon section immediately with minimal changes
setResults(prev => {
- const existingIndex = prev.byAddon.findIndex(s => s.addonId === section.addonId);
- let nextByAddon: AddonSearchResults[];
- if (existingIndex >= 0) {
- nextByAddon = prev.byAddon.slice();
- nextByAddon[existingIndex] = section;
- } else {
- nextByAddon = [...prev.byAddon, section];
- }
- // Sort by installation order
const rank = addonOrderRankRef.current;
- nextByAddon = nextByAddon.slice().sort((a, b) => {
- const ra = rank[a.addonId] ?? Number.MAX_SAFE_INTEGER;
- const rb = rank[b.addonId] ?? Number.MAX_SAFE_INTEGER;
- return ra - rb;
- });
+ const getRank = (id: string) => rank[id] ?? Number.MAX_SAFE_INTEGER;
+
+ const existingIndex = prev.byAddon.findIndex(s => s.addonId === section.addonId);
+
+ if (existingIndex >= 0) {
+ // Update existing section in-place (preserve order and other sections)
+ const copy = prev.byAddon.slice();
+ copy[existingIndex] = section;
+ return { byAddon: copy, allResults: prev.allResults };
+ }
+
+ // Insert new section at correct position based on rank
+ const insertRank = getRank(section.addonId);
+ let insertAt = prev.byAddon.length;
+ for (let i = 0; i < prev.byAddon.length; i++) {
+ if (getRank(prev.byAddon[i].addonId) > insertRank) {
+ insertAt = i;
+ break;
+ }
+ }
+
+ const nextByAddon = [
+ ...prev.byAddon.slice(0, insertAt),
+ section,
+ ...prev.byAddon.slice(insertAt)
+ ];
+
// Hide loading overlay once first section arrives
- if (nextByAddon.length === 1) {
+ if (prev.byAddon.length === 0) {
setSearching(false);
}
+
return { byAddon: nextByAddon, allResults: prev.allResults };
});
@@ -601,6 +615,131 @@ const SearchScreen = () => {
return results.byAddon.length > 0;
}, [results]);
+ // Memoized addon section to prevent re-rendering unchanged sections
+ const AddonSection = React.memo(({
+ addonGroup,
+ addonIndex
+ }: {
+ addonGroup: AddonSearchResults;
+ addonIndex: number;
+ }) => {
+ const movieResults = useMemo(() =>
+ addonGroup.results.filter(item => item.type === 'movie'),
+ [addonGroup.results]
+ );
+ const seriesResults = useMemo(() =>
+ addonGroup.results.filter(item => item.type === 'series'),
+ [addonGroup.results]
+ );
+ const otherResults = useMemo(() =>
+ addonGroup.results.filter(item => item.type !== 'movie' && item.type !== 'series'),
+ [addonGroup.results]
+ );
+
+ return (
+
+ {/* Addon Header */}
+
+
+ {addonGroup.addonName}
+
+
+
+ {addonGroup.results.length}
+
+
+
+
+ {/* Movies */}
+ {movieResults.length > 0 && (
+
+
+ Movies ({movieResults.length})
+
+ (
+
+ )}
+ keyExtractor={item => `${addonGroup.addonId}-movie-${item.id}`}
+ horizontal
+ showsHorizontalScrollIndicator={false}
+ contentContainerStyle={styles.horizontalListContent}
+ extraData={refreshFlag}
+ />
+
+ )}
+
+ {/* TV Shows */}
+ {seriesResults.length > 0 && (
+
+
+ TV Shows ({seriesResults.length})
+
+ (
+
+ )}
+ keyExtractor={item => `${addonGroup.addonId}-series-${item.id}`}
+ horizontal
+ showsHorizontalScrollIndicator={false}
+ contentContainerStyle={styles.horizontalListContent}
+ extraData={refreshFlag}
+ />
+
+ )}
+
+ {/* Other types */}
+ {otherResults.length > 0 && (
+
+
+ {otherResults[0].type.charAt(0).toUpperCase() + otherResults[0].type.slice(1)} ({otherResults.length})
+
+ (
+
+ )}
+ keyExtractor={item => `${addonGroup.addonId}-${item.type}-${item.id}`}
+ horizontal
+ showsHorizontalScrollIndicator={false}
+ contentContainerStyle={styles.horizontalListContent}
+ extraData={refreshFlag}
+ />
+
+ )}
+
+ );
+ }, (prev, next) => {
+ // Only re-render if this section's reference changed
+ return prev.addonGroup === next.addonGroup && prev.addonIndex === next.addonIndex;
+ });
+
const headerBaseHeight = Platform.OS === 'android' ? 80 : 60;
const topSpacing = Platform.OS === 'android' ? (StatusBar.currentHeight || 0) : insets.top;
const headerHeight = headerBaseHeight + topSpacing + 60;
@@ -741,131 +880,14 @@ const SearchScreen = () => {
showsVerticalScrollIndicator={false}
>
{!query.trim() && renderRecentSearches()}
- {/* Render results grouped by addon */}
- {results.byAddon.map((addonGroup, addonIndex) => {
- // Group by type within each addon
- const movieResults = addonGroup.results.filter(item => item.type === 'movie');
- const seriesResults = addonGroup.results.filter(item => item.type === 'series');
- const otherResults = addonGroup.results.filter(item => item.type !== 'movie' && item.type !== 'series');
-
- return (
-
- {/* Addon Header */}
-
-
-
- {addonGroup.addonName}
-
-
-
- {addonGroup.results.length}
-
-
-
-
- {/* Movies from this addon */}
- {movieResults.length > 0 && (
-
-
- Movies ({movieResults.length})
-
- (
-
- )}
- keyExtractor={item => `${addonGroup.addonId}-movie-${item.id}`}
- horizontal
- showsHorizontalScrollIndicator={false}
- contentContainerStyle={styles.horizontalListContent}
- extraData={refreshFlag}
- />
-
- )}
-
- {/* TV Shows from this addon */}
- {seriesResults.length > 0 && (
-
-
- TV Shows ({seriesResults.length})
-
- (
-
- )}
- keyExtractor={item => `${addonGroup.addonId}-series-${item.id}`}
- horizontal
- showsHorizontalScrollIndicator={false}
- contentContainerStyle={styles.horizontalListContent}
- extraData={refreshFlag}
- />
-
- )}
-
- {/* Other content types (anime, etc.) */}
- {otherResults.length > 0 && (
-
-
- {otherResults[0].type.charAt(0).toUpperCase() + otherResults[0].type.slice(1)} ({otherResults.length})
-
- (
-
- )}
- keyExtractor={item => `${addonGroup.addonId}-${item.type}-${item.id}`}
- horizontal
- showsHorizontalScrollIndicator={false}
- contentContainerStyle={styles.horizontalListContent}
- extraData={refreshFlag}
- />
-
- )}
-
- );
- })}
+ {/* Render results grouped by addon using memoized component */}
+ {results.byAddon.map((addonGroup, addonIndex) => (
+
+ ))}
)}
@@ -1015,7 +1037,7 @@ const styles = StyleSheet.create({
borderBottomColor: 'rgba(255,255,255,0.1)',
},
addonHeaderIcon: {
- marginRight: 8,
+ // removed icon
},
addonHeaderText: {
fontSize: isTablet ? 18 : 16,