@zezosoft/zezo-ott-react-native-ui-kit 1.0.3 → 1.0.4

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.
Files changed (108) hide show
  1. package/README.md +1 -1
  2. package/lib/module/assets/animations/heart.json +788 -0
  3. package/lib/module/components/Auth/QrLogin/QrLogin.js +267 -0
  4. package/lib/module/components/Auth/QrLogin/QrLogin.js.map +1 -0
  5. package/lib/module/components/Auth/QrLogin/components/QrViewArea.js +178 -0
  6. package/lib/module/components/Auth/QrLogin/components/QrViewArea.js.map +1 -0
  7. package/lib/module/components/Auth/index.js +3 -1
  8. package/lib/module/components/Auth/index.js.map +1 -1
  9. package/lib/module/components/Headers/AppHeader.js +1 -1
  10. package/lib/module/components/Headers/AppHeader.js.map +1 -1
  11. package/lib/module/components/Headers/One.js +1 -1
  12. package/lib/module/components/Headers/One.js.map +1 -1
  13. package/lib/module/components/Headers/Two.js +1 -1
  14. package/lib/module/components/Headers/Two.js.map +1 -1
  15. package/lib/module/components/Reels/ReelsSeries/Model/Episodes.js +110 -0
  16. package/lib/module/components/Reels/ReelsSeries/Model/Episodes.js.map +1 -0
  17. package/lib/module/components/Reels/ReelsSeries/Model/Synopsis.js +216 -0
  18. package/lib/module/components/Reels/ReelsSeries/Model/Synopsis.js.map +1 -0
  19. package/lib/module/components/Reels/ReelsSeries/ReelSeriesDetailsModal.js +182 -0
  20. package/lib/module/components/Reels/ReelsSeries/ReelSeriesDetailsModal.js.map +1 -0
  21. package/lib/module/components/Reels/ReelsSeries/ReelSeriesOverlay.js +203 -0
  22. package/lib/module/components/Reels/ReelsSeries/ReelSeriesOverlay.js.map +1 -0
  23. package/lib/module/components/Reels/ReelsSeries/ReelsSeries.js +121 -0
  24. package/lib/module/components/Reels/ReelsSeries/ReelsSeries.js.map +1 -0
  25. package/lib/module/components/Reels/ReelsSeries/ReelsSeriesItem.js +290 -0
  26. package/lib/module/components/Reels/ReelsSeries/ReelsSeriesItem.js.map +1 -0
  27. package/lib/module/components/Reels/ReelsSeries/types.js +2 -0
  28. package/lib/module/components/Reels/ReelsSeries/types.js.map +1 -0
  29. package/lib/module/components/Reels/index.js +11 -0
  30. package/lib/module/components/Reels/index.js.map +1 -0
  31. package/lib/module/components/User/DeviceSessions/DeviceSessions.js +8 -0
  32. package/lib/module/components/User/DeviceSessions/DeviceSessions.js.map +1 -1
  33. package/lib/module/components/User/ProfileUpdate/ProfileUpdate.js +258 -0
  34. package/lib/module/components/User/ProfileUpdate/ProfileUpdate.js.map +1 -0
  35. package/lib/module/components/User/components/UserSection.js +8 -13
  36. package/lib/module/components/User/components/UserSection.js.map +1 -1
  37. package/lib/module/components/User/index.js +2 -1
  38. package/lib/module/components/User/index.js.map +1 -1
  39. package/lib/module/components/index.js +1 -0
  40. package/lib/module/components/index.js.map +1 -1
  41. package/lib/module/theme/ThemeProvider.js +13 -10
  42. package/lib/module/theme/ThemeProvider.js.map +1 -1
  43. package/lib/module/theme/themes.js +2 -0
  44. package/lib/module/theme/themes.js.map +1 -1
  45. package/lib/module/utils/Formater.js +17 -0
  46. package/lib/module/utils/Formater.js.map +1 -0
  47. package/lib/typescript/src/components/Auth/QrLogin/QrLogin.d.ts +32 -0
  48. package/lib/typescript/src/components/Auth/QrLogin/QrLogin.d.ts.map +1 -0
  49. package/lib/typescript/src/components/Auth/QrLogin/components/QrViewArea.d.ts +15 -0
  50. package/lib/typescript/src/components/Auth/QrLogin/components/QrViewArea.d.ts.map +1 -0
  51. package/lib/typescript/src/components/Auth/index.d.ts +1 -0
  52. package/lib/typescript/src/components/Auth/index.d.ts.map +1 -1
  53. package/lib/typescript/src/components/Reels/ReelsSeries/Model/Episodes.d.ts +12 -0
  54. package/lib/typescript/src/components/Reels/ReelsSeries/Model/Episodes.d.ts.map +1 -0
  55. package/lib/typescript/src/components/Reels/ReelsSeries/Model/Synopsis.d.ts +9 -0
  56. package/lib/typescript/src/components/Reels/ReelsSeries/Model/Synopsis.d.ts.map +1 -0
  57. package/lib/typescript/src/components/Reels/ReelsSeries/ReelSeriesDetailsModal.d.ts +13 -0
  58. package/lib/typescript/src/components/Reels/ReelsSeries/ReelSeriesDetailsModal.d.ts.map +1 -0
  59. package/lib/typescript/src/components/Reels/ReelsSeries/ReelSeriesOverlay.d.ts +18 -0
  60. package/lib/typescript/src/components/Reels/ReelsSeries/ReelSeriesOverlay.d.ts.map +1 -0
  61. package/lib/typescript/src/components/Reels/ReelsSeries/ReelsSeries.d.ts +15 -0
  62. package/lib/typescript/src/components/Reels/ReelsSeries/ReelsSeries.d.ts.map +1 -0
  63. package/lib/typescript/src/components/Reels/ReelsSeries/ReelsSeriesItem.d.ts +18 -0
  64. package/lib/typescript/src/components/Reels/ReelsSeries/ReelsSeriesItem.d.ts.map +1 -0
  65. package/lib/typescript/src/components/Reels/ReelsSeries/types.d.ts +24 -0
  66. package/lib/typescript/src/components/Reels/ReelsSeries/types.d.ts.map +1 -0
  67. package/lib/typescript/src/components/Reels/index.d.ts +8 -0
  68. package/lib/typescript/src/components/Reels/index.d.ts.map +1 -0
  69. package/lib/typescript/src/components/User/DeviceSessions/DeviceSessions.d.ts +1 -0
  70. package/lib/typescript/src/components/User/DeviceSessions/DeviceSessions.d.ts.map +1 -1
  71. package/lib/typescript/src/components/User/ProfileUpdate/ProfileUpdate.d.ts +27 -0
  72. package/lib/typescript/src/components/User/ProfileUpdate/ProfileUpdate.d.ts.map +1 -0
  73. package/lib/typescript/src/components/User/components/UserSection.d.ts.map +1 -1
  74. package/lib/typescript/src/components/User/index.d.ts +2 -1
  75. package/lib/typescript/src/components/User/index.d.ts.map +1 -1
  76. package/lib/typescript/src/components/index.d.ts +1 -0
  77. package/lib/typescript/src/components/index.d.ts.map +1 -1
  78. package/lib/typescript/src/theme/ThemeProvider.d.ts.map +1 -1
  79. package/lib/typescript/src/theme/themes.d.ts +1 -0
  80. package/lib/typescript/src/theme/themes.d.ts.map +1 -1
  81. package/lib/typescript/src/utils/Formater.d.ts +2 -0
  82. package/lib/typescript/src/utils/Formater.d.ts.map +1 -0
  83. package/package.json +13 -5
  84. package/src/assets/animations/heart.json +788 -0
  85. package/src/components/Auth/QrLogin/QrLogin.tsx +306 -0
  86. package/src/components/Auth/QrLogin/components/QrViewArea.tsx +213 -0
  87. package/src/components/Auth/index.ts +2 -0
  88. package/src/components/Headers/AppHeader.tsx +1 -1
  89. package/src/components/Headers/One.tsx +1 -1
  90. package/src/components/Headers/Two.tsx +1 -1
  91. package/src/components/Reels/ReelsSeries/Model/Episodes.tsx +133 -0
  92. package/src/components/Reels/ReelsSeries/Model/Synopsis.tsx +249 -0
  93. package/src/components/Reels/ReelsSeries/ReelSeriesDetailsModal.tsx +209 -0
  94. package/src/components/Reels/ReelsSeries/ReelSeriesOverlay.tsx +185 -0
  95. package/src/components/Reels/ReelsSeries/ReelsSeries.tsx +163 -0
  96. package/src/components/Reels/ReelsSeries/ReelsSeriesItem.tsx +333 -0
  97. package/src/components/Reels/ReelsSeries/types.ts +27 -0
  98. package/src/components/Reels/index.ts +8 -0
  99. package/src/components/User/DeviceSessions/DeviceSessions.tsx +11 -0
  100. package/src/components/User/ProfileUpdate/ProfileUpdate.tsx +265 -0
  101. package/src/components/User/components/UserSection.tsx +10 -13
  102. package/src/components/User/index.ts +3 -1
  103. package/src/components/index.ts +1 -0
  104. package/src/theme/ThemeProvider.tsx +12 -9
  105. package/src/theme/themes.ts +3 -0
  106. package/src/utils/Formater.ts +14 -0
  107. /package/lib/module/assets/{img → svg}/h.svg +0 -0
  108. /package/src/assets/{img → svg}/h.svg +0 -0
@@ -0,0 +1,306 @@
1
+ /**
2
+ * @author Ashok Desai
3
+ * @lastModified oct 02 Oct 2025 at 10:30 AM
4
+ */
5
+ import React, { useState, useRef } from 'react';
6
+ import {
7
+ View,
8
+ StyleSheet,
9
+ Dimensions,
10
+ TouchableOpacity,
11
+ ActivityIndicator,
12
+ StatusBar,
13
+ } from 'react-native';
14
+ import { scale } from 'react-native-size-matters';
15
+ import { RFValue } from 'react-native-responsive-fontsize';
16
+ import { Text } from '../../Text';
17
+ import AppHeader from '../../Headers/AppHeader';
18
+ import { useTheme } from '../../../theme/hook/useTheme';
19
+ import type { AppTheme } from '../../../theme/themes';
20
+ import { Camera as CameraIcon, QrCode } from 'lucide-react-native';
21
+ import {
22
+ Camera,
23
+ useCameraDevice,
24
+ useCameraPermission,
25
+ useCodeScanner,
26
+ type Code as BaseCode,
27
+ } from 'react-native-vision-camera';
28
+ import QrViewArea from './components/QrViewArea';
29
+
30
+ const { width: SCREEN_W, height: SCREEN_H } = Dimensions.get('window');
31
+ const HEADER_HEIGHT = 25;
32
+
33
+ export interface Code extends BaseCode {
34
+ bounds?: {
35
+ origin: { x: number; y: number };
36
+ size: { width: number; height: number };
37
+ };
38
+ }
39
+
40
+ export type QrLoginProps = {
41
+ title?: string;
42
+ description?: string;
43
+ scanButtonText?: string;
44
+ onBackPress?: () => void;
45
+ onScanSuccess?: (value: string) => void;
46
+ theme?: AppTheme;
47
+ cameraType?: 'back' | 'front';
48
+ layout?: 'one' | 'two';
49
+ };
50
+
51
+ const DEFAULTS = {
52
+ title: 'QR Login',
53
+ description: 'Please move your camera over the QR Code',
54
+ scanButtonText: 'Scan QR Code',
55
+ cameraType: 'back' as const,
56
+ };
57
+
58
+ export const QrLogin: React.FC<QrLoginProps> = ({
59
+ title = DEFAULTS.title,
60
+ description = DEFAULTS.description,
61
+ scanButtonText = DEFAULTS.scanButtonText,
62
+ onBackPress,
63
+ onScanSuccess,
64
+ theme,
65
+ cameraType = DEFAULTS.cameraType,
66
+ layout = 'one',
67
+ }) => {
68
+ const { theme: appliedTheme } = useTheme(theme);
69
+ const { colors } = appliedTheme;
70
+
71
+ const [scanActive, setScanActive] = useState(false);
72
+ const [requesting, setRequesting] = useState(false);
73
+
74
+ const device = useCameraDevice(cameraType);
75
+ const { hasPermission, requestPermission } = useCameraPermission();
76
+
77
+ const scanTimer = useRef<NodeJS.Timeout | null>(null);
78
+ const lastValueRef = useRef<string | null>(null);
79
+ const isScanning = useRef(false);
80
+
81
+ // ---- Check if QR inside scan box ----
82
+ const isCodeInsideBox = (code: Code): boolean => {
83
+ if (!code.bounds) return true;
84
+ const { origin, size } = code.bounds;
85
+ if (!origin || !size) return true;
86
+
87
+ const defaultBoxW = Math.min(SCREEN_W * 0.7, 320);
88
+ const boxW = defaultBoxW * 0.9;
89
+ const boxH = boxW;
90
+ const availableHeight = SCREEN_H - HEADER_HEIGHT;
91
+ const boxTop = Math.floor(
92
+ HEADER_HEIGHT + (availableHeight - boxH) / 2 - scale(40)
93
+ );
94
+ const sideOverlayWidth = Math.floor((SCREEN_W - boxW) / 2);
95
+
96
+ return (
97
+ origin.x >= sideOverlayWidth &&
98
+ origin.y >= boxTop &&
99
+ origin.x + size.width <= sideOverlayWidth + boxW &&
100
+ origin.y + size.height <= boxTop + boxH
101
+ );
102
+ };
103
+
104
+ // ---- Code Scanner ----
105
+ const codeScanner = useCodeScanner({
106
+ codeTypes: ['qr'],
107
+ onCodeScanned: (codes) => {
108
+ if (isScanning.current) return;
109
+ for (const c of codes) {
110
+ const code = c as Code;
111
+ const value = code?.value;
112
+ if (!value || !isCodeInsideBox(code)) continue;
113
+
114
+ if (lastValueRef.current === value) return;
115
+ lastValueRef.current = value;
116
+
117
+ if (scanTimer.current) clearTimeout(scanTimer.current);
118
+
119
+ scanTimer.current = setTimeout(() => {
120
+ isScanning.current = true;
121
+ setScanActive(false);
122
+ onScanSuccess?.(value);
123
+
124
+ setTimeout(() => {
125
+ isScanning.current = false;
126
+ lastValueRef.current = null;
127
+ }, 1000);
128
+ }, 1000);
129
+
130
+ break;
131
+ }
132
+ },
133
+ });
134
+
135
+ const startScan = async () => {
136
+ if (!hasPermission) {
137
+ setRequesting(true);
138
+ const granted = await requestPermission();
139
+ setRequesting(false);
140
+ if (!granted) return;
141
+ }
142
+ setScanActive(true);
143
+ };
144
+
145
+ return (
146
+ <View style={[styles.container, { backgroundColor: colors.background }]}>
147
+ <StatusBar
148
+ translucent
149
+ backgroundColor="transparent"
150
+ barStyle={appliedTheme.dark ? 'light-content' : 'dark-content'}
151
+ />
152
+
153
+ <View style={[styles.headerWrapper, { height: HEADER_HEIGHT }]}>
154
+ <AppHeader
155
+ title={title}
156
+ onBackPress={onBackPress}
157
+ theme={appliedTheme}
158
+ titleAlign="left"
159
+ />
160
+ </View>
161
+
162
+ <View style={styles.cameraWrapper}>
163
+ {scanActive ? (
164
+ device ? (
165
+ <>
166
+ <Camera
167
+ style={StyleSheet.absoluteFill}
168
+ device={device}
169
+ isActive={true}
170
+ codeScanner={codeScanner}
171
+ />
172
+ <QrViewArea
173
+ theme={appliedTheme}
174
+ layout={layout}
175
+ headerHeight={HEADER_HEIGHT}
176
+ />
177
+ </>
178
+ ) : (
179
+ <View
180
+ style={[styles.center, { backgroundColor: colors.background }]}
181
+ >
182
+ <Text
183
+ style={{ color: colors.textPrimary, fontSize: RFValue(16) }}
184
+ >
185
+ No camera available
186
+ </Text>
187
+ </View>
188
+ )
189
+ ) : (
190
+ <View style={styles.content}>
191
+ <View style={styles.card}>
192
+ <CameraIcon
193
+ size={scale(50)}
194
+ color={colors.button}
195
+ strokeWidth={1.6}
196
+ style={{ marginBottom: scale(12) }}
197
+ />
198
+
199
+ <Text style={[styles.descText, { color: colors.textPrimary }]}>
200
+ {description}
201
+ </Text>
202
+
203
+ <View
204
+ style={[
205
+ styles.qrBox,
206
+ {
207
+ borderColor: colors.primary,
208
+ backgroundColor: colors.skeletonBaseColor,
209
+ },
210
+ ]}
211
+ >
212
+ <QrCode
213
+ size={scale(180)}
214
+ color={colors.button}
215
+ strokeWidth={1.6}
216
+ />
217
+ </View>
218
+
219
+ <TouchableOpacity
220
+ style={[styles.scanBtn, { borderColor: colors.button }]}
221
+ onPress={startScan}
222
+ activeOpacity={0.85}
223
+ >
224
+ <CameraIcon
225
+ size={scale(22)}
226
+ color={colors.button}
227
+ strokeWidth={2}
228
+ style={{ marginRight: 10 }}
229
+ />
230
+ <Text style={[styles.scanText, { color: colors.button }]}>
231
+ {scanButtonText}
232
+ </Text>
233
+ </TouchableOpacity>
234
+
235
+ {requesting && (
236
+ <ActivityIndicator
237
+ style={{ marginTop: scale(12) }}
238
+ color={colors.primary}
239
+ />
240
+ )}
241
+ </View>
242
+ </View>
243
+ )}
244
+ </View>
245
+ </View>
246
+ );
247
+ };
248
+
249
+ const styles = StyleSheet.create({
250
+ container: { flex: 1 },
251
+ headerWrapper: {
252
+ position: 'absolute',
253
+ top: 0,
254
+ left: 0,
255
+ right: 0,
256
+ zIndex: 20,
257
+ elevation: 20,
258
+ },
259
+ cameraWrapper: {
260
+ flex: 1,
261
+ top: HEADER_HEIGHT,
262
+ },
263
+ center: {
264
+ flex: 1,
265
+ alignItems: 'center',
266
+ justifyContent: 'center',
267
+ },
268
+ content: {
269
+ flex: 1,
270
+ justifyContent: 'center',
271
+ alignItems: 'center',
272
+ padding: scale(14),
273
+ marginBottom: scale(50),
274
+ },
275
+ card: {
276
+ width: SCREEN_W - scale(40),
277
+ minHeight: SCREEN_H / 2.5,
278
+ alignItems: 'center',
279
+ padding: scale(20),
280
+ },
281
+ descText: {
282
+ fontSize: RFValue(15),
283
+ textAlign: 'center',
284
+ marginVertical: scale(12),
285
+ },
286
+ qrBox: {
287
+ marginVertical: scale(20),
288
+ padding: scale(20),
289
+ borderWidth: 2,
290
+ borderRadius: scale(12),
291
+ justifyContent: 'center',
292
+ alignItems: 'center',
293
+ },
294
+ scanBtn: {
295
+ flexDirection: 'row',
296
+ alignItems: 'center',
297
+ borderWidth: scale(2),
298
+ borderRadius: scale(12),
299
+ paddingVertical: scale(10),
300
+ paddingHorizontal: scale(25),
301
+ marginTop: scale(20),
302
+ },
303
+ scanText: { fontWeight: '600', fontSize: RFValue(14) },
304
+ });
305
+
306
+ export default QrLogin;
@@ -0,0 +1,213 @@
1
+ /**
2
+ * @author Ashok Desai
3
+ * @lastModified oct 02 Oct 2025 at 10:30 AM
4
+ */
5
+ import React, { useEffect, useRef } from 'react';
6
+ import {
7
+ View,
8
+ StyleSheet,
9
+ Animated,
10
+ Easing,
11
+ Dimensions,
12
+ Platform,
13
+ } from 'react-native';
14
+ import { scale } from 'react-native-size-matters';
15
+ import Svg, { Rect } from 'react-native-svg';
16
+ import type { AppTheme } from '../../../../theme/themes';
17
+ import { useTheme } from '../../../../theme/hook/useTheme';
18
+
19
+ const { width: SCREEN_W, height: SCREEN_H } = Dimensions.get('window');
20
+
21
+ type QrViewAreaProps = {
22
+ scanBoxWidth?: number;
23
+ layout?: 'one' | 'two';
24
+ headerHeight?: number;
25
+ theme?: AppTheme;
26
+ };
27
+
28
+ const QrViewArea: React.FC<QrViewAreaProps> = ({
29
+ scanBoxWidth,
30
+ layout = 'one',
31
+ headerHeight = 60,
32
+ theme,
33
+ }) => {
34
+ const { theme: appliedTheme } = useTheme(theme);
35
+ const { colors } = appliedTheme;
36
+
37
+ const anim = useRef(new Animated.Value(0)).current;
38
+
39
+ const defaultBoxW = scanBoxWidth || Math.min(SCREEN_W * 0.7, 320);
40
+ const boxW = defaultBoxW * 0.9;
41
+ const boxH = boxW;
42
+
43
+ const availableHeight = SCREEN_H - headerHeight;
44
+ const boxTop = Math.floor(
45
+ headerHeight + (availableHeight - boxH) / 2 - scale(40)
46
+ );
47
+
48
+ const topOverlayHeight = Math.floor(boxTop - headerHeight);
49
+ const bottomOverlayHeight = Math.floor(SCREEN_H - (boxTop + boxH));
50
+ const sideOverlayWidth = Math.floor((SCREEN_W - boxW) / 2);
51
+
52
+ const margin = scale(12);
53
+ const lineTravel = boxH - margin * 2 - scale(3);
54
+
55
+ const translateY = anim.interpolate({
56
+ inputRange: [0, 1],
57
+ outputRange: [0, lineTravel],
58
+ });
59
+
60
+ useEffect(() => {
61
+ const animation = Animated.loop(
62
+ Animated.sequence([
63
+ Animated.timing(anim, {
64
+ toValue: 1,
65
+ duration: 2000,
66
+ easing: Easing.linear,
67
+ useNativeDriver: true,
68
+ }),
69
+ Animated.timing(anim, {
70
+ toValue: 0,
71
+ duration: 2000,
72
+ easing: Easing.linear,
73
+ useNativeDriver: true,
74
+ }),
75
+ ])
76
+ );
77
+ animation.start();
78
+ return () => animation.stop();
79
+ }, [anim]);
80
+
81
+ return (
82
+ <>
83
+ <View
84
+ style={[
85
+ styles.overlayTB,
86
+ {
87
+ top: headerHeight,
88
+ height: topOverlayHeight,
89
+ backgroundColor: colors.overlay,
90
+ },
91
+ ]}
92
+ />
93
+ <View
94
+ style={[
95
+ styles.overlayTB,
96
+ {
97
+ top: boxTop + boxH,
98
+ height: bottomOverlayHeight,
99
+ backgroundColor: colors.overlay,
100
+ },
101
+ ]}
102
+ />
103
+ <View
104
+ style={[
105
+ styles.overlayLR,
106
+ {
107
+ top: boxTop,
108
+ height: boxH,
109
+ width: sideOverlayWidth,
110
+ backgroundColor: colors.overlay,
111
+ },
112
+ ]}
113
+ />
114
+ <View
115
+ style={[
116
+ styles.overlayLR,
117
+ {
118
+ top: boxTop,
119
+ right: 0,
120
+ height: boxH,
121
+ width: sideOverlayWidth,
122
+ backgroundColor: colors.overlay,
123
+ },
124
+ ]}
125
+ />
126
+
127
+ <View
128
+ style={[
129
+ styles.scanBox,
130
+ { top: boxTop, width: boxW, height: boxH, left: sideOverlayWidth },
131
+ ]}
132
+ >
133
+ {layout === 'two' ? (
134
+ <Svg width={boxW} height={boxH}>
135
+ <Rect
136
+ width={boxW}
137
+ height={boxH}
138
+ ry={scale(8)}
139
+ stroke={colors.button}
140
+ strokeWidth={scale(5)}
141
+ strokeDasharray={[scale(12), scale(6)]}
142
+ fill="none"
143
+ />
144
+ </Svg>
145
+ ) : (
146
+ ['tl', 'tr', 'bl', 'br'].map((c) => (
147
+ <View
148
+ key={c}
149
+ style={[
150
+ styles.corner,
151
+ styles[`corner_${c}` as keyof typeof styles],
152
+ {
153
+ borderColor: colors.button,
154
+ width: scale(18),
155
+ height: scale(18),
156
+ borderTopWidth: c.startsWith('t') ? scale(3) : 0,
157
+ borderBottomWidth: c.startsWith('b') ? scale(3) : 0,
158
+ borderLeftWidth: c.endsWith('l') ? scale(3) : 0,
159
+ borderRightWidth: c.endsWith('r') ? scale(3) : 0,
160
+ },
161
+ ]}
162
+ />
163
+ ))
164
+ )}
165
+
166
+ <Animated.View
167
+ style={{
168
+ position: 'absolute',
169
+ top: margin,
170
+ left: margin,
171
+ right: margin,
172
+ height: scale(3),
173
+ borderRadius: scale(1.5),
174
+ backgroundColor: colors.button,
175
+ transform: [{ translateY }],
176
+ shadowColor: colors.shadow,
177
+ shadowOffset: { width: 0, height: 4 },
178
+ shadowOpacity: 0.3,
179
+ shadowRadius: 5,
180
+ elevation: Platform.OS === 'android' ? 4 : 0,
181
+ }}
182
+ />
183
+ </View>
184
+ </>
185
+ );
186
+ };
187
+
188
+ const styles = StyleSheet.create({
189
+ overlayTB: {
190
+ position: 'absolute',
191
+ left: 0,
192
+ right: 0,
193
+ },
194
+ overlayLR: {
195
+ position: 'absolute',
196
+ },
197
+ scanBox: {
198
+ position: 'absolute',
199
+ justifyContent: 'center',
200
+ alignItems: 'center',
201
+ overflow: 'hidden',
202
+ zIndex: 10,
203
+ },
204
+ corner: {
205
+ position: 'absolute',
206
+ },
207
+ corner_tl: { top: 0, left: 0 },
208
+ corner_tr: { top: 0, right: 0 },
209
+ corner_bl: { bottom: 0, left: 0 },
210
+ corner_br: { bottom: 0, right: 0 },
211
+ });
212
+
213
+ export default QrViewArea;
@@ -7,6 +7,7 @@ import ForgotPassword from './ForgotPassword/ForgotPassword';
7
7
  import LoginWithEmail from './Login/LoginWithEmail';
8
8
  import LoginWithPhone from './Login/LoginWithPhone';
9
9
  import OTP from './OTP/OTP';
10
+ import QrLogin from './QrLogin/QrLogin';
10
11
  import SignUp from './SignUp/SignUp';
11
12
  import { SplashScreen } from './SplashScreen/SplashScreen';
12
13
 
@@ -17,6 +18,7 @@ const Auth = {
17
18
  OTP,
18
19
  ForgotPassword,
19
20
  SplashScreen,
21
+ QrLogin,
20
22
  };
21
23
 
22
24
  export { Auth };
@@ -81,7 +81,7 @@ const AppHeader: React.FC<AppHeaderProps> = ({
81
81
  </View>
82
82
 
83
83
  {/* Right icon */}
84
- {rightIcon ? (
84
+ {rightIcon && onRightPress ? (
85
85
  <TouchableOpacity
86
86
  onPress={onRightPress}
87
87
  activeOpacity={0.7}
@@ -38,7 +38,7 @@ const HeaderOne: React.FC<HeaderOneProps> = ({
38
38
 
39
39
  const iconColor = appliedTheme.colors?.onSurface;
40
40
  const rippleColor = appliedTheme.colors.surfaceVariant + '33';
41
- const pressedBackgroundColor = appliedTheme.colors.backdrop;
41
+ const pressedBackgroundColor = appliedTheme.colors.surfaceVariant;
42
42
 
43
43
  const MemoMenuIcon = useMemo(
44
44
  () => <MenuIcon width={scale(28)} height={scale(28)} color={iconColor} />,
@@ -27,7 +27,7 @@ const HeaderTwo: React.FC<HeaderTwoProps> = ({
27
27
  const { theme: appliedTheme } = useTheme(theme);
28
28
 
29
29
  const rippleColor = appliedTheme.colors.surfaceVariant + '33';
30
- const pressedBackgroundColor = appliedTheme.colors.backdrop;
30
+ const pressedBackgroundColor = appliedTheme.colors.surfaceVariant;
31
31
 
32
32
  return (
33
33
  <View style={styles.headerContainer}>
@@ -0,0 +1,133 @@
1
+ import React from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ FlatList,
6
+ StyleSheet,
7
+ Dimensions,
8
+ TouchableOpacity,
9
+ } from 'react-native';
10
+ import { Lock, ChartNoAxesColumnIncreasing } from 'lucide-react-native';
11
+ import { moderateScale, verticalScale } from 'react-native-size-matters';
12
+ import { RFValue } from 'react-native-responsive-fontsize';
13
+ import type { ISeriesEpisode, ISeriesItem } from '../types';
14
+
15
+ const { height, width } = Dimensions.get('window');
16
+ const NUM_COLUMNS = 6;
17
+
18
+ interface EpisodesProps {
19
+ reel: ISeriesEpisode;
20
+ data: ISeriesItem;
21
+ currentReel: number;
22
+ onEpisodeSelect?: (index: number) => void;
23
+ totalReels?: number;
24
+ }
25
+
26
+ const Episodes: React.FC<EpisodesProps> = ({
27
+ data,
28
+ currentReel,
29
+ onEpisodeSelect,
30
+ totalReels = 0,
31
+ }) => {
32
+ const totalBoxes = Math.max(data.episodes?.length || 0, totalReels);
33
+ const EpisodeData = Array.from({ length: totalBoxes }, (_, i) => i + 1);
34
+
35
+ const renderItem = ({ item }: { item: number }) => {
36
+ const isAvailable = item <= (data.episodes.length ?? 0);
37
+ const isActive = item === currentReel + 1;
38
+
39
+ return (
40
+ <TouchableOpacity
41
+ key={`episode-${item}`}
42
+ onPress={() => isAvailable && onEpisodeSelect?.(item - 1)}
43
+ activeOpacity={0.8}
44
+ >
45
+ <View
46
+ style={[
47
+ styles.box,
48
+ {
49
+ backgroundColor: isActive
50
+ ? 'rgba(60,60,60,1)'
51
+ : 'rgba(34,34,34,1)',
52
+ },
53
+ ]}
54
+ >
55
+ <Text style={[styles.number, { color: 'white' }]}>{item}</Text>
56
+
57
+ {!isAvailable && (
58
+ <View
59
+ style={[
60
+ styles.lockContainer,
61
+ { backgroundColor: 'rgba(76,76,76,1)' },
62
+ ]}
63
+ >
64
+ <Lock color={'white'} size={moderateScale(11)} />
65
+ </View>
66
+ )}
67
+
68
+ {isActive && (
69
+ <ChartNoAxesColumnIncreasing
70
+ color={'white'}
71
+ size={moderateScale(16)}
72
+ style={styles.activeIcon}
73
+ />
74
+ )}
75
+ </View>
76
+ </TouchableOpacity>
77
+ );
78
+ };
79
+
80
+ return (
81
+ <FlatList
82
+ data={EpisodeData}
83
+ numColumns={NUM_COLUMNS}
84
+ keyExtractor={(item) => `episode-${item}`}
85
+ renderItem={renderItem}
86
+ contentContainerStyle={[{ paddingBottom: verticalScale(20) }]}
87
+ showsVerticalScrollIndicator={false}
88
+ scrollEnabled={true} // important for modal
89
+ removeClippedSubviews={true} // improves performance
90
+ windowSize={10} // virtualized window
91
+ initialNumToRender={NUM_COLUMNS * 2}
92
+ nestedScrollEnabled={true} // Allows scroll inside modal
93
+ />
94
+ );
95
+ };
96
+
97
+ export default Episodes;
98
+
99
+ const styles = StyleSheet.create({
100
+ box: {
101
+ width:
102
+ (width - moderateScale(8) * 2 - moderateScale(6) * NUM_COLUMNS) /
103
+ NUM_COLUMNS,
104
+ height: (height * 0.52) / 8,
105
+ borderRadius: moderateScale(10),
106
+ justifyContent: 'center',
107
+ alignItems: 'center',
108
+ marginVertical: moderateScale(2.5),
109
+ marginHorizontal: moderateScale(2.5),
110
+ position: 'relative',
111
+ },
112
+ number: {
113
+ fontSize: RFValue(15),
114
+ fontWeight: '600',
115
+ },
116
+ lockContainer: {
117
+ position: 'absolute',
118
+ top: 0,
119
+ right: 0,
120
+ width: moderateScale(18),
121
+ height: moderateScale(18),
122
+ borderRadius: moderateScale(7),
123
+ borderTopLeftRadius: 0,
124
+ borderBottomRightRadius: 0,
125
+ justifyContent: 'center',
126
+ alignItems: 'center',
127
+ },
128
+ activeIcon: {
129
+ position: 'absolute',
130
+ bottom: verticalScale(6),
131
+ left: moderateScale(6),
132
+ },
133
+ });