@@ -14,6 +14,24 @@ interface AddonSectionProps {
1414 currentTheme : any ;
1515}
1616
17+ const TYPE_LABELS : Record < string , string > = {
18+ 'movie' : 'search.movies' ,
19+ 'series' : 'search.tv_shows' ,
20+ 'anime.movie' : 'search.anime_movies' ,
21+ 'anime.series' : 'search.anime_series' ,
22+ } ;
23+
24+ const subtitleStyle = ( currentTheme : any ) => ( {
25+ color : currentTheme . colors . lightGray ,
26+ fontSize : isTV ? 18 : isLargeTablet ? 17 : isTablet ? 16 : 14 ,
27+ marginBottom : isTV ? 14 : isLargeTablet ? 13 : isTablet ? 12 : 8 ,
28+ paddingHorizontal : isTV ? 24 : isLargeTablet ? 20 : isTablet ? 16 : 16 ,
29+ } ) ;
30+
31+ const containerStyle = {
32+ marginBottom : isTV ? 40 : isLargeTablet ? 36 : isTablet ? 32 : 24 ,
33+ } ;
34+
1735export const AddonSection = React . memo ( ( {
1836 addonGroup,
1937 addonIndex,
@@ -23,18 +41,27 @@ export const AddonSection = React.memo(({
2341} : AddonSectionProps ) => {
2442 const { t } = useTranslation ( ) ;
2543
26- const movieResults = useMemo ( ( ) =>
27- addonGroup . results . filter ( item => item . type === 'movie' ) ,
28- [ addonGroup . results ]
29- ) ;
30- const seriesResults = useMemo ( ( ) =>
31- addonGroup . results . filter ( item => item . type === 'series' ) ,
32- [ addonGroup . results ]
33- ) ;
34- const otherResults = useMemo ( ( ) =>
35- addonGroup . results . filter ( item => item . type !== 'movie' && item . type !== 'series' ) ,
36- [ addonGroup . results ]
37- ) ;
44+ // Group results by their exact type, preserving order of first appearance
45+ const groupedByType = useMemo ( ( ) => {
46+ const order : string [ ] = [ ] ;
47+ const groups : Record < string , StreamingContent [ ] > = { } ;
48+
49+ for ( const item of addonGroup . results ) {
50+ if ( ! groups [ item . type ] ) {
51+ order . push ( item . type ) ;
52+ groups [ item . type ] = [ ] ;
53+ }
54+ groups [ item . type ] . push ( item ) ;
55+ }
56+
57+ return order . map ( type => ( { type, items : groups [ type ] } ) ) ;
58+ } , [ addonGroup . results ] ) ;
59+
60+ const getLabelForType = ( type : string ) : string => {
61+ if ( TYPE_LABELS [ type ] ) return t ( TYPE_LABELS [ type ] ) ;
62+ // Fallback: capitalise and replace dots/underscores for unknown types
63+ return type . replace ( / [ . _ ] / g, ' ' ) . replace ( / \b \w / g, c => c . toUpperCase ( ) ) ;
64+ } ;
3865
3966 return (
4067 < View >
@@ -50,86 +77,13 @@ export const AddonSection = React.memo(({
5077 </ View >
5178 </ View >
5279
53- { /* Movies */ }
54- { movieResults . length > 0 && (
55- < View style = { [ styles . carouselContainer , { marginBottom : isTV ? 40 : isLargeTablet ? 36 : isTablet ? 32 : 24 } ] } >
56- < Text style = { [
57- styles . carouselSubtitle ,
58- {
59- color : currentTheme . colors . lightGray ,
60- fontSize : isTV ? 18 : isLargeTablet ? 17 : isTablet ? 16 : 14 ,
61- marginBottom : isTV ? 14 : isLargeTablet ? 13 : isTablet ? 12 : 8 ,
62- paddingHorizontal : isTV ? 24 : isLargeTablet ? 20 : isTablet ? 16 : 16
63- }
64- ] } >
65- { t ( 'search.movies' ) } ({ movieResults . length } )
66- </ Text >
67- < FlatList
68- data = { movieResults }
69- renderItem = { ( { item, index } ) => (
70- < SearchResultItem
71- item = { item }
72- index = { index }
73- onPress = { onItemPress }
74- onLongPress = { onItemLongPress }
75- />
76- ) }
77- keyExtractor = { item => `${ addonGroup . addonId } -movie-${ item . id } ` }
78- horizontal
79- showsHorizontalScrollIndicator = { false }
80- contentContainerStyle = { styles . horizontalListContent }
81- />
82- </ View >
83- ) }
84-
85- { /* TV Shows */ }
86- { seriesResults . length > 0 && (
87- < View style = { [ styles . carouselContainer , { marginBottom : isTV ? 40 : isLargeTablet ? 36 : isTablet ? 32 : 24 } ] } >
88- < Text style = { [
89- styles . carouselSubtitle ,
90- {
91- color : currentTheme . colors . lightGray ,
92- fontSize : isTV ? 18 : isLargeTablet ? 17 : isTablet ? 16 : 14 ,
93- marginBottom : isTV ? 14 : isLargeTablet ? 13 : isTablet ? 12 : 8 ,
94- paddingHorizontal : isTV ? 24 : isLargeTablet ? 20 : isTablet ? 16 : 16
95- }
96- ] } >
97- { t ( 'search.tv_shows' ) } ({ seriesResults . length } )
98- </ Text >
99- < FlatList
100- data = { seriesResults }
101- renderItem = { ( { item, index } ) => (
102- < SearchResultItem
103- item = { item }
104- index = { index }
105- onPress = { onItemPress }
106- onLongPress = { onItemLongPress }
107- />
108- ) }
109- keyExtractor = { item => `${ addonGroup . addonId } -series-${ item . id } ` }
110- horizontal
111- showsHorizontalScrollIndicator = { false }
112- contentContainerStyle = { styles . horizontalListContent }
113- />
114- </ View >
115- ) }
116-
117- { /* Other types */ }
118- { otherResults . length > 0 && (
119- < View style = { [ styles . carouselContainer , { marginBottom : isTV ? 40 : isLargeTablet ? 36 : isTablet ? 32 : 24 } ] } >
120- < Text style = { [
121- styles . carouselSubtitle ,
122- {
123- color : currentTheme . colors . lightGray ,
124- fontSize : isTV ? 18 : isLargeTablet ? 17 : isTablet ? 16 : 14 ,
125- marginBottom : isTV ? 14 : isLargeTablet ? 13 : isTablet ? 12 : 8 ,
126- paddingHorizontal : isTV ? 24 : isLargeTablet ? 20 : isTablet ? 16 : 16
127- }
128- ] } >
129- { otherResults [ 0 ] . type . charAt ( 0 ) . toUpperCase ( ) + otherResults [ 0 ] . type . slice ( 1 ) } ({ otherResults . length } )
80+ { groupedByType . map ( ( { type, items } ) => (
81+ < View key = { type } style = { [ styles . carouselContainer , containerStyle ] } >
82+ < Text style = { [ styles . carouselSubtitle , subtitleStyle ( currentTheme ) ] } >
83+ { getLabelForType ( type ) } ({ items . length } )
13084 </ Text >
13185 < FlatList
132- data = { otherResults }
86+ data = { items }
13387 renderItem = { ( { item, index } ) => (
13488 < SearchResultItem
13589 item = { item }
@@ -138,17 +92,16 @@ export const AddonSection = React.memo(({
13892 onLongPress = { onItemLongPress }
13993 />
14094 ) }
141- keyExtractor = { item => `${ addonGroup . addonId } -${ item . type } -${ item . id } ` }
95+ keyExtractor = { item => `${ addonGroup . addonId } -${ type } -${ item . id } ` }
14296 horizontal
14397 showsHorizontalScrollIndicator = { false }
14498 contentContainerStyle = { styles . horizontalListContent }
14599 />
146100 </ View >
147- ) }
101+ ) ) }
148102 </ View >
149103 ) ;
150104} , ( prev , next ) => {
151- // Only re-render if this section's reference changed
152105 return prev . addonGroup === next . addonGroup && prev . addonIndex === next . addonIndex ;
153106} ) ;
154107
0 commit comments