@zezosoft/zezo-ott-react-native-ui-kit 1.1.2 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/module/components/Auth/QrLogin/QrLogin.js +304 -138
- package/lib/module/components/Auth/QrLogin/QrLogin.js.map +1 -1
- package/lib/module/components/Auth/QrLogin/components/QrViewArea.js +193 -141
- package/lib/module/components/Auth/QrLogin/components/QrViewArea.js.map +1 -1
- package/lib/module/components/Content/Card/Category/Category.js +83 -11
- package/lib/module/components/Content/Card/Category/Category.js.map +1 -1
- package/lib/module/components/Content/Card/NowWatching/NowWatching.js +237 -108
- package/lib/module/components/Content/Card/NowWatching/NowWatching.js.map +1 -1
- package/lib/module/components/Content/Card/Sliders/Styles/One.js +185 -126
- package/lib/module/components/Content/Card/Sliders/Styles/One.js.map +1 -1
- package/lib/module/components/Content/Card/Sliders/Styles/Two.js +139 -92
- package/lib/module/components/Content/Card/Sliders/Styles/Two.js.map +1 -1
- package/lib/module/components/Content/Card/Styles/Five.js +131 -48
- package/lib/module/components/Content/Card/Styles/Five.js.map +1 -1
- package/lib/module/components/Content/Card/Styles/Four.js +126 -59
- package/lib/module/components/Content/Card/Styles/Four.js.map +1 -1
- package/lib/module/components/Content/Card/Styles/One.js +125 -50
- package/lib/module/components/Content/Card/Styles/One.js.map +1 -1
- package/lib/module/components/Content/Card/Styles/RotateInOut.js +138 -53
- package/lib/module/components/Content/Card/Styles/RotateInOut.js.map +1 -1
- package/lib/module/components/Content/Card/Styles/Six.js +207 -115
- package/lib/module/components/Content/Card/Styles/Six.js.map +1 -1
- package/lib/module/components/Content/Card/Styles/Three.js +134 -79
- package/lib/module/components/Content/Card/Styles/Three.js.map +1 -1
- package/lib/module/components/Content/Card/Styles/TopTen.js +186 -171
- package/lib/module/components/Content/Card/Styles/TopTen.js.map +1 -1
- package/lib/module/components/Content/Card/Styles/Two.js +144 -64
- package/lib/module/components/Content/Card/Styles/Two.js.map +1 -1
- package/lib/module/components/Content/Card/components/AdsPoster.js +162 -0
- package/lib/module/components/Content/Card/components/AdsPoster.js.map +1 -0
- package/lib/module/components/Content/Card/components/CardPoster.js +120 -136
- package/lib/module/components/Content/Card/components/CardPoster.js.map +1 -1
- package/lib/module/components/Content/Card/components/index.js +4 -0
- package/lib/module/components/Content/Card/components/index.js.map +1 -0
- package/lib/module/components/Content/Content.js +67 -27
- package/lib/module/components/Content/Content.js.map +1 -1
- package/lib/module/components/Content/Sections.js +32 -11
- package/lib/module/components/Content/Sections.js.map +1 -1
- package/lib/module/constants/dummySections.js +44 -4
- package/lib/module/constants/dummySections.js.map +1 -1
- package/lib/module/hooks/Images/index.js +5 -0
- package/lib/module/hooks/Images/index.js.map +1 -0
- package/lib/module/hooks/Images/useImageLoader.js +168 -0
- package/lib/module/hooks/Images/useImageLoader.js.map +1 -0
- package/lib/module/hooks/Images/useImageValidation.js +36 -0
- package/lib/module/hooks/Images/useImageValidation.js.map +1 -0
- package/lib/module/hooks/index.js +3 -0
- package/lib/module/hooks/index.js.map +1 -1
- package/lib/module/hooks/useAdTracking.js +270 -0
- package/lib/module/hooks/useAdTracking.js.map +1 -0
- package/lib/module/hooks/useCards.js +164 -0
- package/lib/module/hooks/useCards.js.map +1 -0
- package/lib/module/hooks/usePaginatedSection.js +11 -6
- package/lib/module/hooks/usePaginatedSection.js.map +1 -1
- package/lib/typescript/src/components/Auth/QrLogin/QrLogin.d.ts +2 -0
- package/lib/typescript/src/components/Auth/QrLogin/QrLogin.d.ts.map +1 -1
- package/lib/typescript/src/components/Auth/QrLogin/components/QrViewArea.d.ts.map +1 -1
- package/lib/typescript/src/components/Content/Card/Category/Category.d.ts.map +1 -1
- package/lib/typescript/src/components/Content/Card/NowWatching/NowWatching.d.ts.map +1 -1
- package/lib/typescript/src/components/Content/Card/Sliders/Styles/One.d.ts.map +1 -1
- package/lib/typescript/src/components/Content/Card/Sliders/Styles/Two.d.ts.map +1 -1
- package/lib/typescript/src/components/Content/Card/Styles/Five.d.ts +13 -1
- package/lib/typescript/src/components/Content/Card/Styles/Five.d.ts.map +1 -1
- package/lib/typescript/src/components/Content/Card/Styles/Four.d.ts +13 -1
- package/lib/typescript/src/components/Content/Card/Styles/Four.d.ts.map +1 -1
- package/lib/typescript/src/components/Content/Card/Styles/One.d.ts +15 -3
- package/lib/typescript/src/components/Content/Card/Styles/One.d.ts.map +1 -1
- package/lib/typescript/src/components/Content/Card/Styles/RotateInOut.d.ts +13 -1
- package/lib/typescript/src/components/Content/Card/Styles/RotateInOut.d.ts.map +1 -1
- package/lib/typescript/src/components/Content/Card/Styles/Six.d.ts +1 -0
- package/lib/typescript/src/components/Content/Card/Styles/Six.d.ts.map +1 -1
- package/lib/typescript/src/components/Content/Card/Styles/Three.d.ts +13 -5
- package/lib/typescript/src/components/Content/Card/Styles/Three.d.ts.map +1 -1
- package/lib/typescript/src/components/Content/Card/Styles/TopTen.d.ts +1 -0
- package/lib/typescript/src/components/Content/Card/Styles/TopTen.d.ts.map +1 -1
- package/lib/typescript/src/components/Content/Card/Styles/Two.d.ts +13 -1
- package/lib/typescript/src/components/Content/Card/Styles/Two.d.ts.map +1 -1
- package/lib/typescript/src/components/Content/Card/components/AdsPoster.d.ts +26 -0
- package/lib/typescript/src/components/Content/Card/components/AdsPoster.d.ts.map +1 -0
- package/lib/typescript/src/components/Content/Card/components/CardPoster.d.ts +3 -1
- package/lib/typescript/src/components/Content/Card/components/CardPoster.d.ts.map +1 -1
- package/lib/typescript/src/components/Content/Card/components/index.d.ts +2 -0
- package/lib/typescript/src/components/Content/Card/components/index.d.ts.map +1 -0
- package/lib/typescript/src/components/Content/Card/index.d.ts +76 -6
- package/lib/typescript/src/components/Content/Card/index.d.ts.map +1 -1
- package/lib/typescript/src/components/Content/Content.d.ts +4 -3
- package/lib/typescript/src/components/Content/Content.d.ts.map +1 -1
- package/lib/typescript/src/components/Content/Sections.d.ts +20 -6
- package/lib/typescript/src/components/Content/Sections.d.ts.map +1 -1
- package/lib/typescript/src/constants/dummySections.d.ts +5 -0
- package/lib/typescript/src/constants/dummySections.d.ts.map +1 -1
- package/lib/typescript/src/hooks/Images/index.d.ts +3 -0
- package/lib/typescript/src/hooks/Images/index.d.ts.map +1 -0
- package/lib/typescript/src/hooks/Images/useImageLoader.d.ts +36 -0
- package/lib/typescript/src/hooks/Images/useImageLoader.d.ts.map +1 -0
- package/lib/typescript/src/hooks/Images/useImageValidation.d.ts +17 -0
- package/lib/typescript/src/hooks/Images/useImageValidation.d.ts.map +1 -0
- package/lib/typescript/src/hooks/index.d.ts +3 -0
- package/lib/typescript/src/hooks/index.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useAdTracking.d.ts +39 -0
- package/lib/typescript/src/hooks/useAdTracking.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useCards.d.ts +36 -0
- package/lib/typescript/src/hooks/useCards.d.ts.map +1 -0
- package/lib/typescript/src/hooks/usePaginatedSection.d.ts +12 -2
- package/lib/typescript/src/hooks/usePaginatedSection.d.ts.map +1 -1
- package/lib/typescript/src/types/sections/index.d.ts +7 -4
- package/lib/typescript/src/types/sections/index.d.ts.map +1 -1
- package/package.json +6 -3
- package/src/components/Auth/QrLogin/QrLogin.tsx +382 -122
- package/src/components/Auth/QrLogin/components/QrViewArea.tsx +291 -197
- package/src/components/Content/Card/Category/Category.tsx +95 -8
- package/src/components/Content/Card/NowWatching/NowWatching.tsx +281 -136
- package/src/components/Content/Card/Sliders/Styles/One.tsx +244 -148
- package/src/components/Content/Card/Sliders/Styles/Two.tsx +171 -102
- package/src/components/Content/Card/Styles/Five.tsx +161 -62
- package/src/components/Content/Card/Styles/Four.tsx +164 -85
- package/src/components/Content/Card/Styles/One.tsx +161 -71
- package/src/components/Content/Card/Styles/RotateInOut.tsx +157 -60
- package/src/components/Content/Card/Styles/Six.tsx +242 -142
- package/src/components/Content/Card/Styles/Three.tsx +166 -133
- package/src/components/Content/Card/Styles/TopTen.tsx +230 -191
- package/src/components/Content/Card/Styles/Two.tsx +182 -79
- package/src/components/Content/Card/components/AdsPoster.tsx +202 -0
- package/src/components/Content/Card/components/CardPoster.tsx +134 -154
- package/src/components/Content/Card/components/index.ts +1 -0
- package/src/components/Content/Content.tsx +83 -45
- package/src/components/Content/Sections.tsx +51 -10
- package/src/constants/dummySections.ts +48 -1
- package/src/hooks/Images/index.ts +2 -0
- package/src/hooks/Images/useImageLoader.ts +206 -0
- package/src/hooks/Images/useImageValidation.ts +36 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/useAdTracking.ts +349 -0
- package/src/hooks/useCards.ts +228 -0
- package/src/hooks/usePaginatedSection.ts +26 -7
- package/src/types/sections/index.ts +7 -4
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @author Naresh Dhamu
|
|
3
|
-
* @lastModified
|
|
3
|
+
* @lastModified Wed 24 Dec 2025 at 04:01 PM
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import React, {
|
|
6
|
+
import React, { useMemo } from 'react';
|
|
7
7
|
import { View, StyleSheet, type StyleProp, type ViewStyle } from 'react-native';
|
|
8
8
|
import FastImage, {
|
|
9
9
|
type ResizeMode,
|
|
@@ -16,9 +16,18 @@ import { ImageOff } from 'lucide-react-native';
|
|
|
16
16
|
import type { ITheme } from '../../../../theme/themes';
|
|
17
17
|
import RentOrBuyIcon from './RentOrBuyIcon';
|
|
18
18
|
import type { IContentData } from '@zezosoft/zezo-ott-api-client';
|
|
19
|
+
import { useImageLoader } from '../../../../hooks';
|
|
20
|
+
import LinearGradient from 'react-native-linear-gradient';
|
|
21
|
+
import { RFValue } from 'react-native-responsive-fontsize';
|
|
19
22
|
|
|
20
|
-
//
|
|
21
|
-
const
|
|
23
|
+
// Rank number styling constants
|
|
24
|
+
const RANK_NUMBER_SIZE = scale(40);
|
|
25
|
+
const RANK_NUMBER_BORDER_RADIUS = scale(30);
|
|
26
|
+
const RANK_NUMBER_TEXT_SIZE = RFValue(25);
|
|
27
|
+
const RANK_NUMBER_TEXT_MARGIN_TOP = scale(2);
|
|
28
|
+
const RANK_NUMBER_TEXT_MARGIN_RIGHT = scale(6);
|
|
29
|
+
const GRADIENT_START = { x: 1, y: 0.5 };
|
|
30
|
+
const GRADIENT_END = { x: 0.5, y: 1 };
|
|
22
31
|
|
|
23
32
|
export type CardPosterProps = {
|
|
24
33
|
content_offering_type?: IContentData['content_offering_type'];
|
|
@@ -29,63 +38,45 @@ export type CardPosterProps = {
|
|
|
29
38
|
posterWrapperStyle?: StyleProp<ViewStyle>;
|
|
30
39
|
imageStyle?: ImageStyle;
|
|
31
40
|
resizeMode?: ResizeMode;
|
|
41
|
+
showRankNumber?: boolean;
|
|
42
|
+
rankNumber?: number;
|
|
32
43
|
};
|
|
33
44
|
|
|
45
|
+
// ----------------------------------
|
|
46
|
+
// Image Fallback
|
|
47
|
+
// ----------------------------------
|
|
48
|
+
const ImageFallback = React.memo(({ errorColor, bg }: any) => (
|
|
49
|
+
<View style={[styles.absolute, styles.center, { backgroundColor: bg }]}>
|
|
50
|
+
<ImageOff size={scale(18)} color={errorColor} />
|
|
51
|
+
<Text style={styles.fallbackText} color={errorColor}>
|
|
52
|
+
Image Failed
|
|
53
|
+
</Text>
|
|
54
|
+
</View>
|
|
55
|
+
));
|
|
34
56
|
const CardPoster: React.FC<CardPosterProps> = ({
|
|
35
57
|
content_offering_type,
|
|
36
58
|
posterUri,
|
|
37
59
|
theme,
|
|
38
|
-
isLoading = false,
|
|
39
60
|
borderRadius = scale(6),
|
|
40
61
|
posterWrapperStyle,
|
|
41
62
|
imageStyle,
|
|
63
|
+
isLoading = false,
|
|
42
64
|
resizeMode = FastImage.resizeMode.cover,
|
|
65
|
+
showRankNumber = false,
|
|
66
|
+
rankNumber,
|
|
43
67
|
}) => {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
// Reset image loading state when isLoading prop changes
|
|
57
|
-
useEffect(() => {
|
|
58
|
-
if (isLoading) {
|
|
59
|
-
setImageLoading(true);
|
|
60
|
-
setImageError(false);
|
|
61
|
-
}
|
|
62
|
-
}, [isLoading]);
|
|
63
|
-
|
|
64
|
-
useEffect(() => {
|
|
65
|
-
if (hasValidPoster) {
|
|
66
|
-
// Check if this image was already loaded successfully before
|
|
67
|
-
const isAlreadyLoaded = loadedImageCache.has(posterUri);
|
|
68
|
-
|
|
69
|
-
if (isAlreadyLoaded) {
|
|
70
|
-
// Image was loaded before, skip loading state
|
|
71
|
-
setImageLoading(false);
|
|
72
|
-
setImageError(false);
|
|
73
|
-
} else {
|
|
74
|
-
// Image not loaded before, show loading
|
|
75
|
-
setImageLoading(true);
|
|
76
|
-
setImageError(false);
|
|
77
|
-
FastImage.preload([{ uri: posterUri }]);
|
|
78
|
-
}
|
|
79
|
-
} else {
|
|
80
|
-
setImageLoading(false);
|
|
81
|
-
setImageError(true);
|
|
82
|
-
}
|
|
83
|
-
}, [posterUri, hasValidPoster]);
|
|
84
|
-
|
|
85
|
-
const showSkeleton = useMemo(
|
|
86
|
-
() => isLoading || (imageLoading && !imageError && hasValidPoster),
|
|
87
|
-
[isLoading, imageLoading, imageError, hasValidPoster]
|
|
88
|
-
);
|
|
68
|
+
const {
|
|
69
|
+
showSkeleton,
|
|
70
|
+
showFallback,
|
|
71
|
+
hasValidImage,
|
|
72
|
+
imageSource,
|
|
73
|
+
imageLoading,
|
|
74
|
+
handleLoad,
|
|
75
|
+
handleError,
|
|
76
|
+
} = useImageLoader({
|
|
77
|
+
imageUri: posterUri,
|
|
78
|
+
isLoading,
|
|
79
|
+
});
|
|
89
80
|
|
|
90
81
|
// Memoize theme colors
|
|
91
82
|
const themeColors = useMemo(
|
|
@@ -95,141 +86,113 @@ const CardPoster: React.FC<CardPosterProps> = ({
|
|
|
95
86
|
skeletonHighlightColor: theme.colors.skeletonHighlightColor,
|
|
96
87
|
error: theme.colors.error,
|
|
97
88
|
errorContainer: theme.colors.errorContainer,
|
|
89
|
+
border: theme.colors.border,
|
|
98
90
|
}),
|
|
99
91
|
[theme.colors]
|
|
100
92
|
);
|
|
101
93
|
|
|
102
|
-
const fallbackImageStyle: ImageStyle = useMemo(
|
|
103
|
-
() => ({
|
|
104
|
-
width: '100%',
|
|
105
|
-
aspectRatio: 2 / 3,
|
|
106
|
-
borderRadius,
|
|
107
|
-
position: 'absolute',
|
|
108
|
-
top: 0,
|
|
109
|
-
left: 0,
|
|
110
|
-
}),
|
|
111
|
-
[borderRadius]
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
// Memoize FastImage source
|
|
115
|
-
const imageSource = useMemo(
|
|
116
|
-
() => ({
|
|
117
|
-
uri: posterUri,
|
|
118
|
-
cache: FastImage.cacheControl.immutable,
|
|
119
|
-
priority: FastImage.priority.normal,
|
|
120
|
-
}),
|
|
121
|
-
[posterUri]
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
// Memoize callback handlers
|
|
125
|
-
const handleLoadStart = useCallback(() => {
|
|
126
|
-
// Only set loading if image is not already in cache
|
|
127
|
-
if (!loadedImageCache.has(posterUri)) {
|
|
128
|
-
setImageLoading(true);
|
|
129
|
-
setImageError(false);
|
|
130
|
-
}
|
|
131
|
-
}, [posterUri]);
|
|
132
|
-
|
|
133
|
-
const handleLoad = useCallback(() => {
|
|
134
|
-
setImageLoading(false);
|
|
135
|
-
setImageError(false);
|
|
136
|
-
// Add to cache when successfully loaded
|
|
137
|
-
loadedImageCache.add(posterUri);
|
|
138
|
-
}, [posterUri]);
|
|
139
|
-
|
|
140
|
-
const handleError = useCallback(() => {
|
|
141
|
-
setImageLoading(false);
|
|
142
|
-
setImageError(true);
|
|
143
|
-
}, []);
|
|
144
|
-
|
|
145
94
|
// Memoize wrapper style
|
|
146
95
|
const wrapperStyle = useMemo(
|
|
147
96
|
() => [
|
|
148
97
|
styles.posterWrapper,
|
|
149
|
-
{
|
|
98
|
+
{
|
|
99
|
+
width: '100%' as const,
|
|
100
|
+
aspectRatio: 2 / 3,
|
|
101
|
+
borderRadius,
|
|
102
|
+
backgroundColor: themeColors.background,
|
|
103
|
+
borderWidth: showSkeleton ? scale(0.5) : showFallback ? scale(0.3) : 0,
|
|
104
|
+
borderColor: showSkeleton
|
|
105
|
+
? themeColors.border
|
|
106
|
+
: showFallback
|
|
107
|
+
? themeColors.error
|
|
108
|
+
: 'transparent',
|
|
109
|
+
} as ViewStyle,
|
|
150
110
|
posterWrapperStyle,
|
|
151
111
|
],
|
|
152
|
-
[
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
112
|
+
[
|
|
113
|
+
borderRadius,
|
|
114
|
+
themeColors.background,
|
|
115
|
+
themeColors.border,
|
|
116
|
+
themeColors.error,
|
|
117
|
+
showSkeleton,
|
|
118
|
+
showFallback,
|
|
119
|
+
posterWrapperStyle,
|
|
120
|
+
]
|
|
159
121
|
);
|
|
160
122
|
|
|
161
123
|
// Memoize skeleton item style
|
|
162
|
-
const skeletonItemStyle = useMemo(
|
|
163
|
-
() => [styles.skeleton, { borderRadius }],
|
|
164
|
-
[borderRadius]
|
|
165
|
-
);
|
|
166
124
|
|
|
167
|
-
// Memoize image style array
|
|
125
|
+
// Memoize image style array - hide image until it successfully loads
|
|
168
126
|
const imageStyleArray = useMemo(
|
|
169
127
|
() => [
|
|
170
|
-
|
|
171
|
-
|
|
128
|
+
styles.absolute,
|
|
129
|
+
// Hide image when skeleton is showing or image is still loading
|
|
130
|
+
showSkeleton || imageLoading ? styles.hidden : styles.visible,
|
|
172
131
|
imageStyle,
|
|
173
132
|
],
|
|
174
|
-
[
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
// Memoize fallback wrapper style
|
|
178
|
-
const fallbackWrapperStyle = useMemo(
|
|
179
|
-
() => [
|
|
180
|
-
styles.fallbackWrapper,
|
|
181
|
-
{
|
|
182
|
-
borderRadius,
|
|
183
|
-
backgroundColor: themeColors.background,
|
|
184
|
-
borderColor: themeColors.error,
|
|
185
|
-
},
|
|
186
|
-
],
|
|
187
|
-
[borderRadius, themeColors.background, themeColors.error]
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
// Memoize fallback inner style
|
|
191
|
-
const fallbackInnerStyle = useMemo(
|
|
192
|
-
() => [styles.fallback, { backgroundColor: themeColors.errorContainer }],
|
|
193
|
-
[themeColors.errorContainer]
|
|
133
|
+
[showSkeleton, imageLoading, imageStyle]
|
|
194
134
|
);
|
|
195
135
|
|
|
196
136
|
return (
|
|
197
137
|
<View style={wrapperStyle}>
|
|
198
|
-
{
|
|
138
|
+
{/* Rent/Buy */}
|
|
139
|
+
{!showSkeleton && !showFallback && content_offering_type && (
|
|
199
140
|
<RentOrBuyIcon
|
|
200
|
-
theme={theme}
|
|
201
141
|
key={posterUri}
|
|
202
142
|
content_offering_type={content_offering_type}
|
|
143
|
+
theme={theme}
|
|
203
144
|
/>
|
|
204
145
|
)}
|
|
146
|
+
|
|
147
|
+
{/* Skeleton */}
|
|
205
148
|
{showSkeleton && (
|
|
206
|
-
<View style={
|
|
149
|
+
<View style={[styles.absolute]}>
|
|
207
150
|
<SkeletonPlaceholder
|
|
208
151
|
backgroundColor={themeColors.skeletonBaseColor}
|
|
209
152
|
highlightColor={themeColors.skeletonHighlightColor}
|
|
210
153
|
>
|
|
211
|
-
<SkeletonPlaceholder.Item
|
|
154
|
+
<SkeletonPlaceholder.Item width="100%" height="100%" />
|
|
212
155
|
</SkeletonPlaceholder>
|
|
213
156
|
</View>
|
|
214
157
|
)}
|
|
215
|
-
|
|
158
|
+
|
|
159
|
+
{/* Valid Image - Render when has valid image, but keep hidden until loaded */}
|
|
160
|
+
{hasValidImage && !showFallback && (
|
|
216
161
|
<FastImage
|
|
162
|
+
key={`${posterUri}-image`}
|
|
217
163
|
source={imageSource}
|
|
218
164
|
style={imageStyleArray}
|
|
219
165
|
resizeMode={resizeMode}
|
|
220
|
-
onLoadStart={handleLoadStart}
|
|
221
166
|
onLoad={handleLoad}
|
|
222
167
|
onError={handleError}
|
|
223
168
|
/>
|
|
224
169
|
)}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
170
|
+
|
|
171
|
+
{/* Fallback */}
|
|
172
|
+
{!isLoading && !showSkeleton && showFallback && (
|
|
173
|
+
<ImageFallback
|
|
174
|
+
key={`${posterUri}-fallback`}
|
|
175
|
+
errorColor={themeColors.error}
|
|
176
|
+
bg={themeColors.errorContainer}
|
|
177
|
+
/>
|
|
178
|
+
)}
|
|
179
|
+
|
|
180
|
+
{/* Rank Number */}
|
|
181
|
+
{!showSkeleton && showRankNumber && (
|
|
182
|
+
<View style={styles.numberContainer}>
|
|
183
|
+
<LinearGradient
|
|
184
|
+
colors={theme.colors.backgroundLayoutGradient}
|
|
185
|
+
start={GRADIENT_START}
|
|
186
|
+
end={GRADIENT_END}
|
|
187
|
+
style={[styles.rankNumberBox]}
|
|
188
|
+
>
|
|
189
|
+
<Text
|
|
190
|
+
style={styles.rankNumberText}
|
|
191
|
+
color={theme.colors.textPrimary}
|
|
192
|
+
>
|
|
193
|
+
{rankNumber ?? ''}
|
|
231
194
|
</Text>
|
|
232
|
-
</
|
|
195
|
+
</LinearGradient>
|
|
233
196
|
</View>
|
|
234
197
|
)}
|
|
235
198
|
</View>
|
|
@@ -240,27 +203,23 @@ export default React.memo(CardPoster);
|
|
|
240
203
|
|
|
241
204
|
const styles = StyleSheet.create({
|
|
242
205
|
posterWrapper: {
|
|
243
|
-
width: '100%',
|
|
244
|
-
aspectRatio: 2 / 3,
|
|
245
206
|
overflow: 'hidden',
|
|
246
207
|
position: 'relative',
|
|
247
208
|
},
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
209
|
+
|
|
210
|
+
absolute: {
|
|
211
|
+
...StyleSheet.absoluteFillObject,
|
|
212
|
+
},
|
|
213
|
+
center: {
|
|
214
|
+
justifyContent: 'center',
|
|
215
|
+
alignItems: 'center',
|
|
254
216
|
},
|
|
255
|
-
skeleton: { width: '100%', aspectRatio: 2 / 3 },
|
|
256
217
|
visible: { opacity: 1 },
|
|
257
218
|
hidden: { opacity: 0 },
|
|
258
219
|
fallbackWrapper: {
|
|
259
220
|
position: 'absolute',
|
|
260
221
|
top: 0,
|
|
261
222
|
left: 0,
|
|
262
|
-
width: '100%',
|
|
263
|
-
aspectRatio: 2 / 3,
|
|
264
223
|
borderWidth: scale(1),
|
|
265
224
|
alignItems: 'center',
|
|
266
225
|
justifyContent: 'center',
|
|
@@ -269,10 +228,31 @@ const styles = StyleSheet.create({
|
|
|
269
228
|
position: 'absolute',
|
|
270
229
|
top: 0,
|
|
271
230
|
left: 0,
|
|
272
|
-
width: '100%',
|
|
273
|
-
aspectRatio: 2 / 3,
|
|
274
231
|
alignItems: 'center',
|
|
275
232
|
justifyContent: 'center',
|
|
276
233
|
},
|
|
277
234
|
fallbackText: { marginTop: scale(4), fontSize: 10 },
|
|
235
|
+
numberContainer: {
|
|
236
|
+
position: 'absolute',
|
|
237
|
+
bottom: -1,
|
|
238
|
+
left: 0,
|
|
239
|
+
zIndex: 10,
|
|
240
|
+
},
|
|
241
|
+
rankNumberBox: {
|
|
242
|
+
width: RANK_NUMBER_SIZE,
|
|
243
|
+
height: RANK_NUMBER_SIZE,
|
|
244
|
+
borderTopRightRadius: RANK_NUMBER_BORDER_RADIUS,
|
|
245
|
+
justifyContent: 'center',
|
|
246
|
+
alignItems: 'center',
|
|
247
|
+
},
|
|
248
|
+
rankNumberText: {
|
|
249
|
+
fontSize: RANK_NUMBER_TEXT_SIZE,
|
|
250
|
+
fontWeight: '900',
|
|
251
|
+
textShadowColor: 'rgba(0, 0, 0, 0.6)',
|
|
252
|
+
textShadowOffset: { width: 1, height: 1 },
|
|
253
|
+
textShadowRadius: 3,
|
|
254
|
+
marginTop: RANK_NUMBER_TEXT_MARGIN_TOP,
|
|
255
|
+
marginRight: RANK_NUMBER_TEXT_MARGIN_RIGHT,
|
|
256
|
+
letterSpacing: 0.5,
|
|
257
|
+
},
|
|
278
258
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './AdsPoster';
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import React, { useMemo, useCallback } from 'react';
|
|
6
6
|
import {
|
|
7
7
|
ActivityIndicator,
|
|
8
|
+
Dimensions,
|
|
8
9
|
FlatList,
|
|
9
10
|
type FlatListProps,
|
|
10
11
|
type NativeScrollEvent,
|
|
@@ -19,7 +20,7 @@ import Animated, {
|
|
|
19
20
|
} from 'react-native-reanimated';
|
|
20
21
|
|
|
21
22
|
import SectionType from './Sections';
|
|
22
|
-
import type { IGetSectionData, MoreFetchData } from '../../types';
|
|
23
|
+
import type { IAdItem, IGetSectionData, MoreFetchData } from '../../types';
|
|
23
24
|
import { dummySections } from '../../constants/dummySections';
|
|
24
25
|
import CategoryCard, { type ICategory } from './Card/Category/Category';
|
|
25
26
|
import type { IHistoryItem } from './Card/NowWatching/NowWatching';
|
|
@@ -27,8 +28,23 @@ import type { ThemeOverride } from '../../theme/themes';
|
|
|
27
28
|
import { NoContentFallback, type NoContentFallbackProps } from '../Fallbacks';
|
|
28
29
|
import { useInternalTheme } from '../../theme/hook';
|
|
29
30
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
30
|
-
import type { IContentData } from '@zezosoft/zezo-ott-api-client';
|
|
31
|
+
import type { IContentData, IServeAd } from '@zezosoft/zezo-ott-api-client';
|
|
31
32
|
|
|
33
|
+
// Constants
|
|
34
|
+
const VIEWPORT_OFFSETS = {
|
|
35
|
+
top: 140,
|
|
36
|
+
bottom: 140,
|
|
37
|
+
left: 20,
|
|
38
|
+
right: 20,
|
|
39
|
+
} as const;
|
|
40
|
+
|
|
41
|
+
const INITIAL_NUM_TO_RENDER = 5;
|
|
42
|
+
const SCROLL_EVENT_THROTTLE = 16;
|
|
43
|
+
const END_REACHED_THRESHOLD = 0.7;
|
|
44
|
+
const LOADER_PADDING_VERTICAL = 30;
|
|
45
|
+
const CONTENT_PADDING_BOTTOM = 100;
|
|
46
|
+
|
|
47
|
+
// Types
|
|
32
48
|
export interface ICustomComponentsForContent {
|
|
33
49
|
type: string;
|
|
34
50
|
component: React.ReactElement;
|
|
@@ -62,7 +78,7 @@ export interface IContentProps {
|
|
|
62
78
|
theme?: ThemeOverride;
|
|
63
79
|
autoSwitchTheme?: boolean;
|
|
64
80
|
events?: {
|
|
65
|
-
onPressItem?: (item: IContentData) => void;
|
|
81
|
+
onPressItem?: (item: IContentData | IAdItem) => void;
|
|
66
82
|
onPressMore?: (params: {
|
|
67
83
|
section_id: IGetSectionData['_id'];
|
|
68
84
|
name: IGetSectionData['name'];
|
|
@@ -70,6 +86,7 @@ export interface IContentProps {
|
|
|
70
86
|
}) => void;
|
|
71
87
|
activeCategory?: ICategory;
|
|
72
88
|
onPressCategory?: (category: ICategory) => void;
|
|
89
|
+
onDisplayAds?: (ad: IServeAd) => void;
|
|
73
90
|
};
|
|
74
91
|
|
|
75
92
|
sectionProps?: {
|
|
@@ -82,9 +99,12 @@ export interface IContentProps {
|
|
|
82
99
|
noContentFallbackProps?: NoContentFallbackProps;
|
|
83
100
|
}
|
|
84
101
|
|
|
102
|
+
// Animated Components
|
|
85
103
|
const AnimatedFlatList = Animated.createAnimatedComponent(
|
|
86
104
|
FlatList as React.ComponentType<FlatListProps<IGetSectionData>>
|
|
87
105
|
);
|
|
106
|
+
|
|
107
|
+
// Footer Component Types
|
|
88
108
|
interface FooterProps {
|
|
89
109
|
isEmpty: boolean;
|
|
90
110
|
ErrorComponent?: React.ReactNode;
|
|
@@ -94,6 +114,7 @@ interface FooterProps {
|
|
|
94
114
|
noContentFallbackProps?: NoContentFallbackProps;
|
|
95
115
|
}
|
|
96
116
|
|
|
117
|
+
// Footer Component
|
|
97
118
|
const ContentFooter = React.memo(
|
|
98
119
|
({
|
|
99
120
|
isEmpty,
|
|
@@ -107,7 +128,7 @@ const ContentFooter = React.memo(
|
|
|
107
128
|
|
|
108
129
|
const loaderStyle = useMemo(
|
|
109
130
|
() => ({
|
|
110
|
-
paddingVertical: scale(
|
|
131
|
+
paddingVertical: scale(LOADER_PADDING_VERTICAL),
|
|
111
132
|
alignItems: 'center' as const,
|
|
112
133
|
justifyContent: 'center' as const,
|
|
113
134
|
backgroundColor: 'transparent',
|
|
@@ -144,6 +165,7 @@ const ContentFooter = React.memo(
|
|
|
144
165
|
}
|
|
145
166
|
);
|
|
146
167
|
|
|
168
|
+
// Main Content Component
|
|
147
169
|
export const Content: React.FC<IContentProps> = ({
|
|
148
170
|
customComponents,
|
|
149
171
|
InfiniteScrollLoaderComponent,
|
|
@@ -161,13 +183,15 @@ export const Content: React.FC<IContentProps> = ({
|
|
|
161
183
|
isLoading,
|
|
162
184
|
noContentFallbackProps,
|
|
163
185
|
}) => {
|
|
186
|
+
// Hooks
|
|
164
187
|
const insets = useSafeAreaInsets();
|
|
188
|
+
|
|
189
|
+
// Memoized Data
|
|
165
190
|
const sectionData = useMemo<IGetSectionData[]>(
|
|
166
191
|
() => contentData?.sectionData ?? [],
|
|
167
192
|
[contentData?.sectionData]
|
|
168
193
|
);
|
|
169
194
|
|
|
170
|
-
// Memoize section data object to prevent unnecessary re-renders
|
|
171
195
|
const sectionDataMemo = useMemo(
|
|
172
196
|
() => ({
|
|
173
197
|
historyData: contentData.historyData,
|
|
@@ -176,7 +200,6 @@ export const Content: React.FC<IContentProps> = ({
|
|
|
176
200
|
[contentData.historyData, contentData.category]
|
|
177
201
|
);
|
|
178
202
|
|
|
179
|
-
// Memoize loading state object
|
|
180
203
|
const loadingStateMemo = useMemo(
|
|
181
204
|
() => ({
|
|
182
205
|
section: isLoading?.section ?? false,
|
|
@@ -186,9 +209,23 @@ export const Content: React.FC<IContentProps> = ({
|
|
|
186
209
|
[isLoading?.section, isLoading?.history, isLoading?.category]
|
|
187
210
|
);
|
|
188
211
|
|
|
212
|
+
const screenDimensions = useMemo(() => Dimensions.get('window'), []);
|
|
213
|
+
|
|
214
|
+
const listData = useMemo(
|
|
215
|
+
() => (isLoading?.section ? dummySections : sectionData),
|
|
216
|
+
[isLoading?.section, sectionData]
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
const isEmpty = useMemo(
|
|
220
|
+
() => !isLoading?.section && (!sectionData || sectionData.length === 0),
|
|
221
|
+
[isLoading?.section, sectionData]
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
// Header Component
|
|
189
225
|
const headerComponent = useMemo(() => {
|
|
190
|
-
if (!contentData?.category || contentData.category.length === 0)
|
|
226
|
+
if (!contentData?.category || contentData.category.length === 0) {
|
|
191
227
|
return null;
|
|
228
|
+
}
|
|
192
229
|
|
|
193
230
|
return (
|
|
194
231
|
<CategoryCard
|
|
@@ -198,7 +235,7 @@ export const Content: React.FC<IContentProps> = ({
|
|
|
198
235
|
isLoading={isLoading?.category}
|
|
199
236
|
mode="filled"
|
|
200
237
|
theme={theme}
|
|
201
|
-
key=
|
|
238
|
+
key="category"
|
|
202
239
|
/>
|
|
203
240
|
);
|
|
204
241
|
}, [
|
|
@@ -209,6 +246,7 @@ export const Content: React.FC<IContentProps> = ({
|
|
|
209
246
|
theme,
|
|
210
247
|
]);
|
|
211
248
|
|
|
249
|
+
// Render Item Callback
|
|
212
250
|
const renderItem = useCallback(
|
|
213
251
|
({ item, index }: { item: IGetSectionData | null; index: number }) => {
|
|
214
252
|
if (!item) return null;
|
|
@@ -241,83 +279,83 @@ export const Content: React.FC<IContentProps> = ({
|
|
|
241
279
|
sectionProps={sectionProps}
|
|
242
280
|
moreFetchDataHistory={moreFetchDataHistory}
|
|
243
281
|
moreFetchData={moreFetchData}
|
|
282
|
+
onDisplayAds={events?.onDisplayAds}
|
|
283
|
+
screenDimensions={screenDimensions}
|
|
284
|
+
viewportOffsets={VIEWPORT_OFFSETS}
|
|
244
285
|
/>
|
|
245
286
|
);
|
|
246
287
|
},
|
|
247
288
|
[
|
|
248
|
-
isLoading,
|
|
289
|
+
isLoading?.section,
|
|
249
290
|
LoaderComponent,
|
|
250
291
|
sectionDataMemo,
|
|
251
|
-
loadingStateMemo,
|
|
252
292
|
theme,
|
|
253
293
|
customComponents,
|
|
254
294
|
events,
|
|
295
|
+
loadingStateMemo,
|
|
255
296
|
sectionProps,
|
|
256
297
|
moreFetchDataHistory,
|
|
257
298
|
moreFetchData,
|
|
299
|
+
screenDimensions,
|
|
258
300
|
]
|
|
259
301
|
);
|
|
260
302
|
|
|
261
|
-
|
|
262
|
-
() => !isLoading?.section && (!sectionData || sectionData.length === 0),
|
|
263
|
-
[isLoading?.section, sectionData]
|
|
264
|
-
);
|
|
265
|
-
|
|
303
|
+
// Key Extractor
|
|
266
304
|
const keyExtractor = useCallback(
|
|
267
305
|
(_item: IGetSectionData, index: number) => index.toString(),
|
|
268
306
|
[]
|
|
269
307
|
);
|
|
270
308
|
|
|
309
|
+
// Handlers
|
|
271
310
|
const handleEndReached = useCallback(() => {
|
|
272
311
|
onEndReached?.();
|
|
273
312
|
}, [onEndReached]);
|
|
274
313
|
|
|
314
|
+
// Styles
|
|
275
315
|
const contentContainerStyleMemo = useMemo(
|
|
276
316
|
() => [
|
|
277
|
-
{ paddingBottom: verticalScale(
|
|
317
|
+
{ paddingBottom: verticalScale(CONTENT_PADDING_BOTTOM) + insets.bottom },
|
|
278
318
|
contentContainerStyle,
|
|
279
319
|
],
|
|
280
320
|
[insets.bottom, contentContainerStyle]
|
|
281
321
|
);
|
|
282
322
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
323
|
+
// Footer Component
|
|
324
|
+
const footerComponent = useMemo(
|
|
325
|
+
() => (
|
|
326
|
+
<ContentFooter
|
|
327
|
+
InfiniteScrollLoaderComponent={InfiniteScrollLoaderComponent}
|
|
328
|
+
InfiniteScrollIsLoading={isLoading?.InfiniteScrollIsLoading}
|
|
329
|
+
isEmpty={isEmpty}
|
|
330
|
+
ErrorComponent={ErrorComponent}
|
|
331
|
+
theme={theme}
|
|
332
|
+
noContentFallbackProps={noContentFallbackProps}
|
|
333
|
+
/>
|
|
334
|
+
),
|
|
335
|
+
[
|
|
336
|
+
InfiniteScrollLoaderComponent,
|
|
337
|
+
isLoading?.InfiniteScrollIsLoading,
|
|
338
|
+
isEmpty,
|
|
339
|
+
ErrorComponent,
|
|
340
|
+
theme,
|
|
341
|
+
noContentFallbackProps,
|
|
342
|
+
]
|
|
286
343
|
);
|
|
287
344
|
|
|
288
345
|
return (
|
|
289
346
|
<AnimatedFlatList
|
|
290
|
-
initialNumToRender={5}
|
|
291
|
-
showsVerticalScrollIndicator={false}
|
|
292
|
-
removeClippedSubviews
|
|
293
|
-
onScroll={onScroll as any}
|
|
294
|
-
scrollEventThrottle={16}
|
|
295
347
|
data={listData}
|
|
296
348
|
renderItem={renderItem as any}
|
|
297
349
|
keyExtractor={keyExtractor}
|
|
350
|
+
onScroll={onScroll as any}
|
|
298
351
|
onEndReached={handleEndReached}
|
|
352
|
+
onEndReachedThreshold={END_REACHED_THRESHOLD}
|
|
353
|
+
scrollEventThrottle={SCROLL_EVENT_THROTTLE}
|
|
354
|
+
initialNumToRender={INITIAL_NUM_TO_RENDER}
|
|
355
|
+
showsVerticalScrollIndicator={false}
|
|
356
|
+
removeClippedSubviews
|
|
299
357
|
ListHeaderComponent={headerComponent}
|
|
300
|
-
ListFooterComponent={
|
|
301
|
-
() => (
|
|
302
|
-
<ContentFooter
|
|
303
|
-
InfiniteScrollLoaderComponent={InfiniteScrollLoaderComponent}
|
|
304
|
-
InfiniteScrollIsLoading={isLoading?.InfiniteScrollIsLoading}
|
|
305
|
-
isEmpty={isEmpty}
|
|
306
|
-
ErrorComponent={ErrorComponent}
|
|
307
|
-
theme={theme}
|
|
308
|
-
noContentFallbackProps={noContentFallbackProps}
|
|
309
|
-
/>
|
|
310
|
-
),
|
|
311
|
-
[
|
|
312
|
-
InfiniteScrollLoaderComponent,
|
|
313
|
-
isLoading?.InfiniteScrollIsLoading,
|
|
314
|
-
isEmpty,
|
|
315
|
-
ErrorComponent,
|
|
316
|
-
theme,
|
|
317
|
-
noContentFallbackProps,
|
|
318
|
-
]
|
|
319
|
-
)}
|
|
320
|
-
onEndReachedThreshold={0.7}
|
|
358
|
+
ListFooterComponent={footerComponent}
|
|
321
359
|
contentContainerStyle={contentContainerStyleMemo}
|
|
322
360
|
/>
|
|
323
361
|
);
|