Skip to content

Commit d0bfd35

Browse files
authored
Merge pull request #639 from chrisk325/catalogs
remove hardcoded catalog type + seperate search result for anime + fix for tvdb+kitsu
2 parents dd6b0ff + f60087c commit d0bfd35

File tree

6 files changed

+174
-184
lines changed

6 files changed

+174
-184
lines changed

src/components/search/AddonSection.tsx

Lines changed: 46 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
1735
export 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

src/components/search/DiscoverBottomSheets.tsx

Lines changed: 37 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ interface DiscoverBottomSheetsProps {
1111
typeSheetRef: RefObject<BottomSheetModal>;
1212
catalogSheetRef: RefObject<BottomSheetModal>;
1313
genreSheetRef: RefObject<BottomSheetModal>;
14-
selectedDiscoverType: 'movie' | 'series';
14+
selectedDiscoverType: string;
1515
selectedCatalog: DiscoverCatalog | null;
1616
selectedDiscoverGenre: string | null;
1717
filteredCatalogs: DiscoverCatalog[];
1818
availableGenres: string[];
19-
onTypeSelect: (type: 'movie' | 'series') => void;
19+
availableTypes: string[];
20+
onTypeSelect: (type: string) => void;
2021
onCatalogSelect: (catalog: DiscoverCatalog) => void;
2122
onGenreSelect: (genre: string | null) => void;
2223
currentTheme: any;
@@ -31,14 +32,28 @@ export const DiscoverBottomSheets = ({
3132
selectedDiscoverGenre,
3233
filteredCatalogs,
3334
availableGenres,
35+
availableTypes,
3436
onTypeSelect,
3537
onCatalogSelect,
3638
onGenreSelect,
3739
currentTheme,
3840
}: DiscoverBottomSheetsProps) => {
3941
const { t } = useTranslation();
4042

41-
const typeSnapPoints = useMemo(() => ['25%'], []);
43+
const TYPE_LABELS: Record<string, string> = {
44+
'movie': t('search.movies'),
45+
'series': t('search.tv_shows'),
46+
'anime.movie': t('search.anime_movies'),
47+
'anime.series': t('search.anime_series'),
48+
};
49+
const getLabelForType = (type: string) =>
50+
TYPE_LABELS[type] ?? type.replace(/[._]/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
51+
52+
const typeSnapPoints = useMemo(() => {
53+
const itemCount = availableTypes.length;
54+
const snapPct = Math.min(20 + itemCount * 10, 60);
55+
return [`${snapPct}%`];
56+
}, [availableTypes]);
4257
const catalogSnapPoints = useMemo(() => ['50%'], []);
4358
const genreSnapPoints = useMemo(() => ['50%'], []);
4459
const [activeBottomSheetRef, setActiveBottomSheetRef] = useState(null);
@@ -225,47 +240,25 @@ export const DiscoverBottomSheets = ({
225240
style={{ backgroundColor: currentTheme.colors.darkGray || '#0A0C0C' }}
226241
contentContainerStyle={styles.bottomSheetContent}
227242
>
228-
{/* Movies option */}
229-
<TouchableOpacity
230-
style={[
231-
styles.bottomSheetItem,
232-
selectedDiscoverType === 'movie' && styles.bottomSheetItemSelected
233-
]}
234-
onPress={() => onTypeSelect('movie')}
235-
>
236-
<View style={styles.bottomSheetItemContent}>
237-
<Text style={[styles.bottomSheetItemTitle, { color: currentTheme.colors.white }]}>
238-
{t('search.movies')}
239-
</Text>
240-
<Text style={[styles.bottomSheetItemSubtitle, { color: currentTheme.colors.lightGray }]}>
241-
{t('search.browse_movies')}
242-
</Text>
243-
</View>
244-
{selectedDiscoverType === 'movie' && (
245-
<MaterialIcons name="check" size={24} color={currentTheme.colors.primary} />
246-
)}
247-
</TouchableOpacity>
248-
249-
{/* TV Shows option */}
250-
<TouchableOpacity
251-
style={[
252-
styles.bottomSheetItem,
253-
selectedDiscoverType === 'series' && styles.bottomSheetItemSelected
254-
]}
255-
onPress={() => onTypeSelect('series')}
256-
>
257-
<View style={styles.bottomSheetItemContent}>
258-
<Text style={[styles.bottomSheetItemTitle, { color: currentTheme.colors.white }]}>
259-
{t('search.tv_shows')}
260-
</Text>
261-
<Text style={[styles.bottomSheetItemSubtitle, { color: currentTheme.colors.lightGray }]}>
262-
{t('search.browse_tv')}
263-
</Text>
264-
</View>
265-
{selectedDiscoverType === 'series' && (
266-
<MaterialIcons name="check" size={24} color={currentTheme.colors.primary} />
267-
)}
268-
</TouchableOpacity>
243+
{availableTypes.map((type) => (
244+
<TouchableOpacity
245+
key={type}
246+
style={[
247+
styles.bottomSheetItem,
248+
selectedDiscoverType === type && styles.bottomSheetItemSelected
249+
]}
250+
onPress={() => onTypeSelect(type)}
251+
>
252+
<View style={styles.bottomSheetItemContent}>
253+
<Text style={[styles.bottomSheetItemTitle, { color: currentTheme.colors.white }]}>
254+
{getLabelForType(type)}
255+
</Text>
256+
</View>
257+
{selectedDiscoverType === type && (
258+
<MaterialIcons name="check" size={24} color={currentTheme.colors.primary} />
259+
)}
260+
</TouchableOpacity>
261+
))}
269262
</BottomSheetScrollView>
270263
</BottomSheetModal>
271264
</>

src/components/search/DiscoverSection.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ interface DiscoverSectionProps {
2222
pendingDiscoverResults: StreamingContent[];
2323
loadingMore: boolean;
2424
selectedCatalog: DiscoverCatalog | null;
25-
selectedDiscoverType: 'movie' | 'series';
25+
selectedDiscoverType: string;
2626
selectedDiscoverGenre: string | null;
2727
availableGenres: string[];
2828
typeSheetRef: React.RefObject<BottomSheetModal>;
@@ -78,7 +78,11 @@ export const DiscoverSection = ({
7878
onPress={() => typeSheetRef.current?.present()}
7979
>
8080
<Text style={[styles.discoverSelectorText, { color: currentTheme.colors.white }]} numberOfLines={1}>
81-
{selectedDiscoverType === 'movie' ? t('search.movies') : t('search.tv_shows')}
81+
{selectedDiscoverType === 'movie' ? t('search.movies')
82+
: selectedDiscoverType === 'series' ? t('search.tv_shows')
83+
: selectedDiscoverType === 'anime.movie' ? t('search.anime_movies')
84+
: selectedDiscoverType === 'anime.series' ? t('search.anime_series')
85+
: selectedDiscoverType.replace(/[._]/g, ' ').replace(/\b\w/g, c => c.toUpperCase())}
8286
</Text>
8387
<MaterialIcons name="keyboard-arrow-down" size={20} color={currentTheme.colors.lightGray} />
8488
</TouchableOpacity>
@@ -112,8 +116,13 @@ export const DiscoverSection = ({
112116
{selectedCatalog && (
113117
<View style={styles.discoverFilterSummary}>
114118
<Text style={[styles.discoverFilterSummaryText, { color: currentTheme.colors.lightGray }]}>
115-
{selectedCatalog.addonName}{selectedCatalog.type === 'movie' ? t('search.movies') : t('search.tv_shows')}
116-
{selectedDiscoverGenre ? ` • ${selectedDiscoverGenre}` : ''}
119+
{selectedCatalog.addonName}{
120+
selectedCatalog.type === 'movie' ? t('search.movies')
121+
: selectedCatalog.type === 'series' ? t('search.tv_shows')
122+
: selectedCatalog.type === 'anime.movie' ? t('search.anime_movies')
123+
: selectedCatalog.type === 'anime.series' ? t('search.anime_series')
124+
: selectedCatalog.type.replace(/[._]/g, ' ').replace(/\b\w/g, c => c.toUpperCase())
125+
}{selectedDiscoverGenre ? ` • ${selectedDiscoverGenre}` : ''}
117126
</Text>
118127
</View>
119128
)}

0 commit comments

Comments
 (0)