@zezosoft/zezo-ott-react-native-ui-kit 1.1.0 → 1.1.1

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 (178) hide show
  1. package/lib/module/components/BottomSheet/BottomSheet.js +152 -0
  2. package/lib/module/components/BottomSheet/BottomSheet.js.map +1 -0
  3. package/lib/module/components/BottomSheet/index.js +4 -0
  4. package/lib/module/components/BottomSheet/index.js.map +1 -0
  5. package/lib/module/components/Button/BackBtn.js +28 -2
  6. package/lib/module/components/Button/BackBtn.js.map +1 -1
  7. package/lib/module/components/Reels/ReelsSeries/MediaControls/BottomControls.js +134 -0
  8. package/lib/module/components/Reels/ReelsSeries/MediaControls/BottomControls.js.map +1 -0
  9. package/lib/module/components/Reels/ReelsSeries/MediaControls/MediaControlsProvider.js +154 -0
  10. package/lib/module/components/Reels/ReelsSeries/MediaControls/MediaControlsProvider.js.map +1 -0
  11. package/lib/module/components/Reels/ReelsSeries/MediaControls/MiddleControls.js +173 -0
  12. package/lib/module/components/Reels/ReelsSeries/MediaControls/MiddleControls.js.map +1 -0
  13. package/lib/module/components/Reels/ReelsSeries/MediaControls/RightControls.js +77 -0
  14. package/lib/module/components/Reels/ReelsSeries/MediaControls/RightControls.js.map +1 -0
  15. package/lib/module/components/Reels/ReelsSeries/MediaControls/TopControls.js +132 -0
  16. package/lib/module/components/Reels/ReelsSeries/MediaControls/TopControls.js.map +1 -0
  17. package/lib/module/components/Reels/ReelsSeries/Model/DetailsModal.js +165 -0
  18. package/lib/module/components/Reels/ReelsSeries/Model/DetailsModal.js.map +1 -0
  19. package/lib/module/components/Reels/ReelsSeries/Model/SettingModal.js +106 -0
  20. package/lib/module/components/Reels/ReelsSeries/Model/SettingModal.js.map +1 -0
  21. package/lib/module/components/Reels/ReelsSeries/Model/UnlockModal.js +124 -0
  22. package/lib/module/components/Reels/ReelsSeries/Model/UnlockModal.js.map +1 -0
  23. package/lib/module/components/Reels/ReelsSeries/ReelsSeries.js +102 -64
  24. package/lib/module/components/Reels/ReelsSeries/ReelsSeries.js.map +1 -1
  25. package/lib/module/components/Reels/ReelsSeries/ReelsSeriesItem.js +241 -232
  26. package/lib/module/components/Reels/ReelsSeries/ReelsSeriesItem.js.map +1 -1
  27. package/lib/module/components/Reels/ReelsSeries/components/AnimatedThreeLines.js +153 -0
  28. package/lib/module/components/Reels/ReelsSeries/components/AnimatedThreeLines.js.map +1 -0
  29. package/lib/module/components/Reels/ReelsSeries/{Model → components}/Episodes.js +46 -36
  30. package/lib/module/components/Reels/ReelsSeries/components/Episodes.js.map +1 -0
  31. package/lib/module/components/Reels/ReelsSeries/components/GradientOverlay.js +35 -0
  32. package/lib/module/components/Reels/ReelsSeries/components/GradientOverlay.js.map +1 -0
  33. package/lib/module/components/Reels/ReelsSeries/components/Like.js +37 -0
  34. package/lib/module/components/Reels/ReelsSeries/components/Like.js.map +1 -0
  35. package/lib/module/components/Reels/ReelsSeries/components/RotatingLoader.js +55 -0
  36. package/lib/module/components/Reels/ReelsSeries/components/RotatingLoader.js.map +1 -0
  37. package/lib/module/components/Reels/ReelsSeries/components/Synopsis.js +268 -0
  38. package/lib/module/components/Reels/ReelsSeries/components/Synopsis.js.map +1 -0
  39. package/lib/module/components/Reels/ReelsSeries/components/VideoControls/QualityControl.js +143 -0
  40. package/lib/module/components/Reels/ReelsSeries/components/VideoControls/QualityControl.js.map +1 -0
  41. package/lib/module/components/Reels/ReelsSeries/components/VideoControls/SpeedControl.js +56 -0
  42. package/lib/module/components/Reels/ReelsSeries/components/VideoControls/SpeedControl.js.map +1 -0
  43. package/lib/module/components/Reels/ReelsSeries/components/VideoControls/index.js +5 -0
  44. package/lib/module/components/Reels/ReelsSeries/components/VideoControls/index.js.map +1 -0
  45. package/lib/module/components/Reels/utils/Controls/gestureUtils.js +30 -0
  46. package/lib/module/components/Reels/utils/Controls/gestureUtils.js.map +1 -0
  47. package/lib/module/components/Reels/utils/Controls/index.js +7 -0
  48. package/lib/module/components/Reels/utils/Controls/index.js.map +1 -0
  49. package/lib/module/components/Reels/utils/Controls/overlayUtils.js +28 -0
  50. package/lib/module/components/Reels/utils/Controls/overlayUtils.js.map +1 -0
  51. package/lib/module/components/Reels/utils/Controls/videoControlsConstants.js +27 -0
  52. package/lib/module/components/Reels/utils/Controls/videoControlsConstants.js.map +1 -0
  53. package/lib/module/components/Reels/utils/Controls/videoRef.js +5 -0
  54. package/lib/module/components/Reels/utils/Controls/videoRef.js.map +1 -0
  55. package/lib/module/components/Reels/utils/index.js +4 -0
  56. package/lib/module/components/Reels/utils/index.js.map +1 -0
  57. package/lib/module/components/Reels/utils/timeoutUtils.js +24 -0
  58. package/lib/module/components/Reels/utils/timeoutUtils.js.map +1 -0
  59. package/lib/module/components/index.js +1 -0
  60. package/lib/module/components/index.js.map +1 -1
  61. package/lib/module/theme/ThemeProvider.js +7 -4
  62. package/lib/module/theme/ThemeProvider.js.map +1 -1
  63. package/lib/module/theme/themes.js +3 -3
  64. package/lib/typescript/src/components/BottomSheet/BottomSheet.d.ts +18 -0
  65. package/lib/typescript/src/components/BottomSheet/BottomSheet.d.ts.map +1 -0
  66. package/lib/typescript/src/components/BottomSheet/index.d.ts +3 -0
  67. package/lib/typescript/src/components/BottomSheet/index.d.ts.map +1 -0
  68. package/lib/typescript/src/components/Button/BackBtn.d.ts +3 -1
  69. package/lib/typescript/src/components/Button/BackBtn.d.ts.map +1 -1
  70. package/lib/typescript/src/components/Button/index.d.ts +2 -0
  71. package/lib/typescript/src/components/Button/index.d.ts.map +1 -1
  72. package/lib/typescript/src/components/Reels/ReelsSeries/MediaControls/BottomControls.d.ts +14 -0
  73. package/lib/typescript/src/components/Reels/ReelsSeries/MediaControls/BottomControls.d.ts.map +1 -0
  74. package/lib/typescript/src/components/Reels/ReelsSeries/MediaControls/MediaControlsProvider.d.ts +60 -0
  75. package/lib/typescript/src/components/Reels/ReelsSeries/MediaControls/MediaControlsProvider.d.ts.map +1 -0
  76. package/lib/typescript/src/components/Reels/ReelsSeries/MediaControls/MiddleControls.d.ts +14 -0
  77. package/lib/typescript/src/components/Reels/ReelsSeries/MediaControls/MiddleControls.d.ts.map +1 -0
  78. package/lib/typescript/src/components/Reels/ReelsSeries/MediaControls/RightControls.d.ts +14 -0
  79. package/lib/typescript/src/components/Reels/ReelsSeries/MediaControls/RightControls.d.ts.map +1 -0
  80. package/lib/typescript/src/components/Reels/ReelsSeries/MediaControls/TopControls.d.ts +12 -0
  81. package/lib/typescript/src/components/Reels/ReelsSeries/MediaControls/TopControls.d.ts.map +1 -0
  82. package/lib/typescript/src/components/Reels/ReelsSeries/Model/DetailsModal.d.ts +16 -0
  83. package/lib/typescript/src/components/Reels/ReelsSeries/Model/DetailsModal.d.ts.map +1 -0
  84. package/lib/typescript/src/components/Reels/ReelsSeries/Model/SettingModal.d.ts +15 -0
  85. package/lib/typescript/src/components/Reels/ReelsSeries/Model/SettingModal.d.ts.map +1 -0
  86. package/lib/typescript/src/components/Reels/ReelsSeries/Model/UnlockModal.d.ts +14 -0
  87. package/lib/typescript/src/components/Reels/ReelsSeries/Model/UnlockModal.d.ts.map +1 -0
  88. package/lib/typescript/src/components/Reels/ReelsSeries/ReelsSeries.d.ts +8 -2
  89. package/lib/typescript/src/components/Reels/ReelsSeries/ReelsSeries.d.ts.map +1 -1
  90. package/lib/typescript/src/components/Reels/ReelsSeries/ReelsSeriesItem.d.ts +8 -2
  91. package/lib/typescript/src/components/Reels/ReelsSeries/ReelsSeriesItem.d.ts.map +1 -1
  92. package/lib/typescript/src/components/Reels/ReelsSeries/components/AnimatedThreeLines.d.ts +13 -0
  93. package/lib/typescript/src/components/Reels/ReelsSeries/components/AnimatedThreeLines.d.ts.map +1 -0
  94. package/lib/typescript/src/components/Reels/ReelsSeries/{Model → components}/Episodes.d.ts +3 -2
  95. package/lib/typescript/src/components/Reels/ReelsSeries/components/Episodes.d.ts.map +1 -0
  96. package/lib/typescript/src/components/Reels/ReelsSeries/components/GradientOverlay.d.ts +7 -0
  97. package/lib/typescript/src/components/Reels/ReelsSeries/components/GradientOverlay.d.ts.map +1 -0
  98. package/lib/typescript/src/components/Reels/ReelsSeries/components/Like.d.ts +7 -0
  99. package/lib/typescript/src/components/Reels/ReelsSeries/components/Like.d.ts.map +1 -0
  100. package/lib/typescript/src/components/Reels/ReelsSeries/components/RotatingLoader.d.ts +12 -0
  101. package/lib/typescript/src/components/Reels/ReelsSeries/components/RotatingLoader.d.ts.map +1 -0
  102. package/lib/typescript/src/components/Reels/ReelsSeries/components/Synopsis.d.ts +12 -0
  103. package/lib/typescript/src/components/Reels/ReelsSeries/components/Synopsis.d.ts.map +1 -0
  104. package/lib/typescript/src/components/Reels/ReelsSeries/components/VideoControls/QualityControl.d.ts +11 -0
  105. package/lib/typescript/src/components/Reels/ReelsSeries/components/VideoControls/QualityControl.d.ts.map +1 -0
  106. package/lib/typescript/src/components/Reels/ReelsSeries/components/VideoControls/SpeedControl.d.ts +10 -0
  107. package/lib/typescript/src/components/Reels/ReelsSeries/components/VideoControls/SpeedControl.d.ts.map +1 -0
  108. package/lib/typescript/src/components/Reels/ReelsSeries/components/VideoControls/index.d.ts +3 -0
  109. package/lib/typescript/src/components/Reels/ReelsSeries/components/VideoControls/index.d.ts.map +1 -0
  110. package/lib/typescript/src/components/Reels/ReelsSeries/types.d.ts +13 -0
  111. package/lib/typescript/src/components/Reels/ReelsSeries/types.d.ts.map +1 -1
  112. package/lib/typescript/src/components/Reels/utils/Controls/gestureUtils.d.ts +10 -0
  113. package/lib/typescript/src/components/Reels/utils/Controls/gestureUtils.d.ts.map +1 -0
  114. package/lib/typescript/src/components/Reels/utils/Controls/index.d.ts +5 -0
  115. package/lib/typescript/src/components/Reels/utils/Controls/index.d.ts.map +1 -0
  116. package/lib/typescript/src/components/Reels/utils/Controls/overlayUtils.d.ts +12 -0
  117. package/lib/typescript/src/components/Reels/utils/Controls/overlayUtils.d.ts.map +1 -0
  118. package/lib/typescript/src/components/Reels/utils/Controls/videoControlsConstants.d.ts +24 -0
  119. package/lib/typescript/src/components/Reels/utils/Controls/videoControlsConstants.d.ts.map +1 -0
  120. package/lib/typescript/src/components/Reels/utils/Controls/videoRef.d.ts +3 -0
  121. package/lib/typescript/src/components/Reels/utils/Controls/videoRef.d.ts.map +1 -0
  122. package/lib/typescript/src/components/Reels/utils/index.d.ts +2 -0
  123. package/lib/typescript/src/components/Reels/utils/index.d.ts.map +1 -0
  124. package/lib/typescript/src/components/Reels/utils/timeoutUtils.d.ts +14 -0
  125. package/lib/typescript/src/components/Reels/utils/timeoutUtils.d.ts.map +1 -0
  126. package/lib/typescript/src/components/index.d.ts +1 -0
  127. package/lib/typescript/src/components/index.d.ts.map +1 -1
  128. package/package.json +1 -1
  129. package/src/components/BottomSheet/BottomSheet.tsx +195 -0
  130. package/src/components/BottomSheet/index.ts +2 -0
  131. package/src/components/Button/BackBtn.tsx +33 -0
  132. package/src/components/Reels/ReelsSeries/MediaControls/BottomControls.tsx +156 -0
  133. package/src/components/Reels/ReelsSeries/MediaControls/MediaControlsProvider.tsx +234 -0
  134. package/src/components/Reels/ReelsSeries/MediaControls/MiddleControls.tsx +196 -0
  135. package/src/components/Reels/ReelsSeries/MediaControls/RightControls.tsx +92 -0
  136. package/src/components/Reels/ReelsSeries/MediaControls/TopControls.tsx +163 -0
  137. package/src/components/Reels/ReelsSeries/Model/DetailsModal.tsx +210 -0
  138. package/src/components/Reels/ReelsSeries/Model/SettingModal.tsx +143 -0
  139. package/src/components/Reels/ReelsSeries/Model/UnlockModal.tsx +154 -0
  140. package/src/components/Reels/ReelsSeries/ReelsSeries.tsx +142 -74
  141. package/src/components/Reels/ReelsSeries/ReelsSeriesItem.tsx +349 -250
  142. package/src/components/Reels/ReelsSeries/components/AnimatedThreeLines.tsx +184 -0
  143. package/src/components/Reels/ReelsSeries/{Model → components}/Episodes.tsx +54 -33
  144. package/src/components/Reels/ReelsSeries/components/GradientOverlay.tsx +41 -0
  145. package/src/components/Reels/ReelsSeries/components/Like.tsx +40 -0
  146. package/src/components/Reels/ReelsSeries/components/RotatingLoader.tsx +79 -0
  147. package/src/components/Reels/ReelsSeries/components/Synopsis.tsx +332 -0
  148. package/src/components/Reels/ReelsSeries/components/VideoControls/QualityControl.tsx +190 -0
  149. package/src/components/Reels/ReelsSeries/components/VideoControls/SpeedControl.tsx +80 -0
  150. package/src/components/Reels/ReelsSeries/components/VideoControls/index.ts +2 -0
  151. package/src/components/Reels/ReelsSeries/types.ts +13 -2
  152. package/src/components/Reels/utils/Controls/gestureUtils.ts +42 -0
  153. package/src/components/Reels/utils/Controls/index.ts +4 -0
  154. package/src/components/Reels/utils/Controls/overlayUtils.ts +35 -0
  155. package/src/components/Reels/utils/Controls/videoControlsConstants.ts +25 -0
  156. package/src/components/Reels/utils/Controls/videoRef.ts +4 -0
  157. package/src/components/Reels/utils/index.ts +1 -0
  158. package/src/components/Reels/utils/timeoutUtils.ts +29 -0
  159. package/src/components/index.ts +1 -0
  160. package/src/theme/ThemeProvider.tsx +8 -2
  161. package/src/theme/themes.ts +3 -3
  162. package/lib/module/components/Reels/ReelsSeries/Model/Episodes.js.map +0 -1
  163. package/lib/module/components/Reels/ReelsSeries/Model/Synopsis.js +0 -212
  164. package/lib/module/components/Reels/ReelsSeries/Model/Synopsis.js.map +0 -1
  165. package/lib/module/components/Reels/ReelsSeries/ReelSeriesDetailsModal.js +0 -182
  166. package/lib/module/components/Reels/ReelsSeries/ReelSeriesDetailsModal.js.map +0 -1
  167. package/lib/module/components/Reels/ReelsSeries/ReelSeriesOverlay.js +0 -203
  168. package/lib/module/components/Reels/ReelsSeries/ReelSeriesOverlay.js.map +0 -1
  169. package/lib/typescript/src/components/Reels/ReelsSeries/Model/Episodes.d.ts.map +0 -1
  170. package/lib/typescript/src/components/Reels/ReelsSeries/Model/Synopsis.d.ts +0 -9
  171. package/lib/typescript/src/components/Reels/ReelsSeries/Model/Synopsis.d.ts.map +0 -1
  172. package/lib/typescript/src/components/Reels/ReelsSeries/ReelSeriesDetailsModal.d.ts +0 -13
  173. package/lib/typescript/src/components/Reels/ReelsSeries/ReelSeriesDetailsModal.d.ts.map +0 -1
  174. package/lib/typescript/src/components/Reels/ReelsSeries/ReelSeriesOverlay.d.ts +0 -18
  175. package/lib/typescript/src/components/Reels/ReelsSeries/ReelSeriesOverlay.d.ts.map +0 -1
  176. package/src/components/Reels/ReelsSeries/Model/Synopsis.tsx +0 -242
  177. package/src/components/Reels/ReelsSeries/ReelSeriesDetailsModal.tsx +0 -209
  178. package/src/components/Reels/ReelsSeries/ReelSeriesOverlay.tsx +0 -185
@@ -1,21 +1,44 @@
1
- import React, { useState, useEffect, useRef, useCallback, memo } from 'react';
1
+ import React, {
2
+ useState,
3
+ useEffect,
4
+ useRef,
5
+ useCallback,
6
+ useMemo,
7
+ memo,
8
+ } from 'react';
2
9
  import { View, Dimensions, StyleSheet } from 'react-native';
3
- import ReelSeriesOverlay from './ReelSeriesOverlay';
4
- import ReelSeriesDetailsModal from './ReelSeriesDetailsModal';
5
- import type { ISeriesEpisode, ISeriesItem } from './types';
6
- import { Video, type VideoRef } from 'react-native-video';
7
- import FastImage from 'react-native-fast-image';
10
+ import { Video } from 'react-native-video';
8
11
  import {
9
- Gesture,
10
12
  GestureDetector,
11
13
  GestureHandlerRootView,
12
14
  } from 'react-native-gesture-handler';
13
- import { Pause, Play } from 'lucide-react-native';
14
- import { moderateScale } from 'react-native-size-matters';
15
- import LottieView from 'lottie-react-native';
16
- import DoubleTapAnim from '../../../assets/animations/heart.json';
17
15
 
18
- const { width } = Dimensions.get('window');
16
+ import type {
17
+ ILikeMoreData,
18
+ ISeriesEpisode,
19
+ ISeriesItem,
20
+ ModalState,
21
+ } from './types';
22
+ import type { ITheme } from '../../../theme';
23
+ import {
24
+ videoRef,
25
+ videoControlsStyles,
26
+ PAUSE_ICON_TIMEOUT,
27
+ LIKE_ANIM_TIMEOUT,
28
+ UNLOCK_MODAL_DELAY,
29
+ useOverlayThresholds,
30
+ useIsInOverlay,
31
+ useCreateTapGesture,
32
+ useCombinedGesture,
33
+ } from '../utils/Controls';
34
+ import { clearTimeoutSafely, setTimeoutSafely } from '../utils';
35
+ import MediaControlsProvider from './MediaControls/MediaControlsProvider';
36
+ import FastImage from 'react-native-fast-image';
37
+
38
+ const INITIAL_MODAL_STATE: ModalState = {
39
+ type: 'none',
40
+ isVisible: false,
41
+ };
19
42
 
20
43
  interface IReelItemProps {
21
44
  reel: ISeriesEpisode;
@@ -28,7 +51,12 @@ interface IReelItemProps {
28
51
  isVisible: boolean;
29
52
  preload: boolean;
30
53
  activeEpisodeIndex: number;
31
- onEpisodeSelect: (index: number) => void;
54
+ onEpisodeSelect: (episode: ISeriesEpisode) => void;
55
+ theme: ITheme;
56
+ onBackPress?: () => void;
57
+ onSearchPress?: () => void;
58
+ onLikeMorePress?: (item: ILikeMoreData) => void;
59
+ onEpisodeUnlockPress?: (episode: ISeriesEpisode) => void;
32
60
  }
33
61
 
34
62
  const ReelItem: React.FC<IReelItemProps> = ({
@@ -38,296 +66,367 @@ const ReelItem: React.FC<IReelItemProps> = ({
38
66
  videoHeight,
39
67
  onLikePress,
40
68
  onSharePress,
69
+ onBackPress,
41
70
  isVisible,
42
71
  preload,
43
72
  activeEpisodeIndex,
44
73
  onEpisodeSelect,
74
+ onSearchPress,
75
+ onLikeMorePress,
76
+ onEpisodeUnlockPress,
77
+ theme,
45
78
  }) => {
46
- const videoRef = useRef<VideoRef>(null);
47
- const timeoutRef = useRef<NodeJS.Timeout | null>(null);
48
- const [isPaused, setIsPaused] = useState<boolean>(false);
49
- const [paused, setPaused] = useState<string | null>(null);
50
- const [modalVisible, setModalVisible] = useState(false);
79
+ // State
80
+ const [buffering, setBuffering] = useState(true);
81
+ const [isPaused, setIsPaused] = useState(false);
82
+ const [showPauseIcon, setShowPauseIcon] = useState<boolean | null>(null);
83
+ const [modal, setModal] = useState<ModalState>(INITIAL_MODAL_STATE);
84
+ const [unlockModalVisible, setUnlockModalVisible] = useState(false);
85
+ const [unlockModalDismissed, setUnlockModalDismissed] = useState(false);
86
+ const [showLikeAnim, setShowLikeAnim] = useState(false);
51
87
  const [currentTime, setCurrentTime] = useState(0);
52
88
  const [duration, setDuration] = useState(0);
53
- const [videoLoaded, setVideoLoaded] = useState(false);
54
- const [showLikeAnim, setShowLikeAnim] = useState<boolean>(false);
89
+ const [playbackRate, setPlaybackRate] = useState(1);
90
+ const [videoQuality, setVideoQuality] = useState('Auto');
91
+ const [isLiked, setIsLiked] = useState(data.isLiked);
92
+ const [likesCount, setLikesCount] = useState(data.likes);
93
+ const [hasError, setHasError] = useState(false);
94
+
95
+ // Refs
96
+ const timeoutRef = useRef<NodeJS.Timeout | null>(null);
97
+ const likeAnimTimeoutRef = useRef<NodeJS.Timeout | null>(null);
98
+
99
+ // Constants
100
+ const isLocked = reel.isLocked;
101
+ const videoUri = useMemo(() => reel.videoUrl || '', [reel.videoUrl]);
102
+ const thumbnail = data.thumbnail;
103
+
104
+ // Overlay thresholds
105
+ const { rightThreshold, bottomThreshold } = useOverlayThresholds(videoHeight);
106
+
107
+ // Computed values
108
+ const finalPause = useMemo(
109
+ () => isLocked || !isVisible || modal.isVisible || isPaused || hasError,
110
+ [isLocked, isVisible, modal.isVisible, isPaused, hasError]
111
+ );
112
+
113
+ const showCenterControls = useMemo(
114
+ () => showPauseIcon || buffering || isLocked || hasError,
115
+ [showPauseIcon, buffering, isLocked, hasError]
116
+ );
117
+
118
+ // Handlers
119
+ const closeUnlockModal = useCallback(() => {
120
+ setUnlockModalVisible(false);
121
+ setUnlockModalDismissed(true); // Prevent auto-open after manual close
122
+ }, []);
123
+
124
+ const closeAllModals = useCallback(() => {
125
+ setModal(INITIAL_MODAL_STATE);
126
+ closeUnlockModal();
127
+ }, [closeUnlockModal]);
128
+
129
+ const openUnlockModal = useCallback(() => {
130
+ setUnlockModalVisible(true);
131
+ }, []);
132
+
133
+ // Effects
134
+ useEffect(() => {
135
+ setIsLiked(data.isLiked);
136
+ setLikesCount(data.likes);
137
+ }, [data.isLiked, data.likes]);
55
138
 
56
139
  useEffect(() => {
57
- setIsPaused(!isVisible);
58
140
  if (!isVisible) {
59
- setPaused(null);
60
- setVideoLoaded(false);
141
+ setShowPauseIcon(null);
142
+ setModal((prev) =>
143
+ prev.isVisible ? { ...prev, isVisible: false } : prev
144
+ );
145
+ setUnlockModalVisible(false);
146
+ setUnlockModalDismissed(false);
147
+ setHasError(false);
61
148
  }
62
149
  }, [isVisible]);
63
150
 
64
- // Cleanup timeout on unmount
151
+ useEffect(() => {
152
+ if (isVisible && isLocked && !unlockModalVisible && !unlockModalDismissed) {
153
+ setIsPaused(true);
154
+ setUnlockModalDismissed(true);
155
+ }
156
+ }, [isVisible, isLocked, unlockModalVisible, unlockModalDismissed]);
157
+
65
158
  useEffect(() => {
66
159
  return () => {
67
- if (timeoutRef.current) {
68
- clearTimeout(timeoutRef.current);
69
- }
160
+ clearTimeoutSafely(timeoutRef);
161
+ clearTimeoutSafely(likeAnimTimeoutRef);
70
162
  };
71
163
  }, []);
72
164
 
73
- const videoUri = reel.videoUrl || '';
74
- // Handle like
75
165
  const handleLikePress = useCallback(() => {
76
- const newLiked = !data.isLiked;
166
+ const newLiked = !isLiked;
167
+ const newCount = newLiked ? likesCount + 1 : Math.max(0, likesCount - 1);
168
+
169
+ setIsLiked(newLiked);
170
+ setLikesCount(newCount);
171
+
172
+ // Update data object for parent component
77
173
  data.isLiked = newLiked;
78
- data.likes += newLiked ? 1 : -1;
174
+ data.likes = newCount;
175
+
79
176
  if (newLiked) {
80
177
  setShowLikeAnim(true);
81
- setTimeout(() => setShowLikeAnim(false), 1200);
178
+ setTimeoutSafely(
179
+ likeAnimTimeoutRef,
180
+ () => setShowLikeAnim(false),
181
+ LIKE_ANIM_TIMEOUT
182
+ );
82
183
  }
184
+
83
185
  onLikePress?.(reel.episodeId, newLiked);
84
- }, [data, onLikePress, reel.episodeId]);
186
+ }, [isLiked, likesCount, data, onLikePress, reel.episodeId]);
85
187
 
86
- const handleEpisodeSelect = (index: number) => {
87
- if (data && index >= 0 && index < data.episodes.length) {
88
- onEpisodeSelect(index);
89
- setModalVisible(false);
188
+ const handleTogglePlay = useCallback(() => {
189
+ setIsPaused((prev) => !prev);
190
+ setShowPauseIcon(true);
191
+ if (isPaused) {
192
+ setTimeoutSafely(
193
+ timeoutRef,
194
+ () => setShowPauseIcon(false),
195
+ PAUSE_ICON_TIMEOUT
196
+ );
197
+ } else {
198
+ setShowPauseIcon(true);
90
199
  }
91
- };
92
-
93
- const handleVideoLoad = () => {
94
- setVideoLoaded(true);
95
- };
200
+ }, [isPaused]);
201
+
202
+ const handleEpisodeSelect = useCallback(
203
+ (episode: ISeriesEpisode) => {
204
+ closeAllModals();
205
+ if (episode.isLocked) {
206
+ setTimeout(() => openUnlockModal(), UNLOCK_MODAL_DELAY);
207
+ } else {
208
+ onEpisodeSelect(episode);
209
+ }
210
+ },
211
+ [closeAllModals, onEpisodeSelect, openUnlockModal]
212
+ );
96
213
 
97
- const handleTogglePlay = useCallback(() => {
98
- // Use functional updates to ensure we get the current state
99
- setIsPaused((prevIsPaused) => {
100
- const newIsPaused = !prevIsPaused;
101
- const currentState = newIsPaused ? 'paused' : 'play';
214
+ const handleVideoEnd = useCallback(() => {
215
+ if (!autoScroll) return;
216
+ const nextEpisode = data.episodes?.[activeEpisodeIndex + 1];
217
+ if (nextEpisode) {
218
+ onEpisodeSelect(nextEpisode);
219
+ }
220
+ }, [autoScroll, data.episodes, activeEpisodeIndex, onEpisodeSelect]);
221
+
222
+ // Gestures
223
+ const handleLockedTap = useCallback(() => {
224
+ closeAllModals();
225
+ openUnlockModal();
226
+ }, [closeAllModals, openUnlockModal]);
227
+
228
+ const isInOverlay = useIsInOverlay(rightThreshold, bottomThreshold);
229
+ const createTapGesture = useCreateTapGesture(
230
+ isInOverlay,
231
+ isLocked,
232
+ handleLockedTap
233
+ );
102
234
 
103
- setPaused(currentState);
235
+ const singleTap = useMemo(
236
+ () => createTapGesture(handleTogglePlay),
237
+ [createTapGesture, handleTogglePlay]
238
+ );
104
239
 
105
- // Clear any existing timeout
106
- if (timeoutRef.current) {
107
- clearTimeout(timeoutRef.current);
108
- }
240
+ const doubleTap = useMemo(
241
+ () => createTapGesture(handleLikePress, 2),
242
+ [createTapGesture, handleLikePress]
243
+ );
109
244
 
110
- // Set new timeout
111
- timeoutRef.current = setTimeout(() => {
112
- if (currentState === 'play') {
113
- setPaused(null);
114
- }
115
- }, 700);
245
+ const combinedGesture = useCombinedGesture(singleTap, doubleTap);
116
246
 
117
- return newIsPaused;
118
- });
119
- }, []);
247
+ // Memoized props objects
248
+ const unlockProps = useMemo(
249
+ () => ({
250
+ isVisible: unlockModalVisible,
251
+ onClose: closeUnlockModal,
252
+ }),
253
+ [unlockModalVisible, closeUnlockModal]
254
+ );
120
255
 
121
- const singleTap = Gesture.Tap()
122
- .maxDuration(300)
123
- .onStart((event) => {
124
- // Check if the tap is in the overlay area (right side icons or bottom area)
125
- const { x, y } = event;
126
- const screenWidth = width;
127
- const screenHeight = videoHeight;
256
+ const contentProps = useMemo(
257
+ () => ({
258
+ series: data,
259
+ episode: reel,
260
+ activeIndex: activeEpisodeIndex,
261
+ }),
262
+ [data, reel, activeEpisodeIndex]
263
+ );
128
264
 
129
- // Right side icons area (right 80px)
130
- const rightIconsArea = x > screenWidth - 80;
265
+ const videoProps = useMemo(
266
+ () => ({
267
+ currentTime,
268
+ duration,
269
+ paused: finalPause,
270
+ buffering,
271
+ showCenterControls,
272
+ isLocked,
273
+ hasError,
274
+ }),
275
+ [
276
+ currentTime,
277
+ duration,
278
+ finalPause,
279
+ buffering,
280
+ showCenterControls,
281
+ isLocked,
282
+ hasError,
283
+ ]
284
+ );
131
285
 
132
- // Bottom area (bottom 150px)
133
- const bottomArea = y > screenHeight - 150;
286
+ const likesProps = useMemo(
287
+ () => ({
288
+ liked: isLiked,
289
+ count: likesCount,
290
+ showAnimation: showLikeAnim,
291
+ }),
292
+ [isLiked, likesCount, showLikeAnim]
293
+ );
134
294
 
135
- // If tap is in overlay area, don't trigger play/pause
136
- if (rightIconsArea || bottomArea) {
137
- return;
138
- }
139
- handleTogglePlay();
140
- })
141
- .runOnJS(true);
142
-
143
- const doubleTap = Gesture.Tap()
144
- .maxDuration(300)
145
- .numberOfTaps(2)
146
- .onStart((event) => {
147
- // Check if the tap is in the overlay area (right side icons or bottom area)
148
- const { x, y } = event;
149
- const screenWidth = width;
150
- const screenHeight = videoHeight;
151
-
152
- // Right side icons area (right 80px)
153
- const rightIconsArea = x > screenWidth - 80;
154
-
155
- // Bottom area (bottom 150px)
156
- const bottomArea = y > screenHeight - 150;
157
-
158
- // If tap is in overlay area, don't trigger like
159
- if (rightIconsArea || bottomArea) {
160
- return;
161
- }
295
+ const actionsProps = useMemo(
296
+ () => ({
297
+ onLike: handleLikePress,
298
+ onEpisodeSelect: handleEpisodeSelect,
299
+ onLikeMore: onLikeMorePress,
300
+ onShare: onSharePress,
301
+ onUnLock: openUnlockModal,
302
+ onUnlockPress: onEpisodeUnlockPress,
303
+ }),
304
+ [
305
+ handleLikePress,
306
+ handleEpisodeSelect,
307
+ onLikeMorePress,
308
+ onSharePress,
309
+ onEpisodeUnlockPress,
310
+ openUnlockModal,
311
+ ]
312
+ );
162
313
 
163
- handleLikePress();
164
- })
165
- .runOnJS(true);
314
+ const navigationProps = useMemo(
315
+ () => ({
316
+ onBack: onBackPress,
317
+ onSearch: onSearchPress,
318
+ }),
319
+ [onBackPress, onSearchPress]
320
+ );
166
321
 
167
- const combined = Gesture.Exclusive(doubleTap, singleTap);
322
+ const settingsProps = useMemo(
323
+ () => ({
324
+ playbackRate,
325
+ quality: videoQuality,
326
+ onPlaybackRateChange: setPlaybackRate,
327
+ onQualityChange: setVideoQuality,
328
+ }),
329
+ [playbackRate, videoQuality]
330
+ );
168
331
 
332
+ // ----------------------- RENDER -----------------------
169
333
  return (
170
- <View
171
- style={[
172
- styles.container,
173
- { height: videoHeight, backgroundColor: '#000' },
174
- ]}
175
- >
176
- <GestureHandlerRootView style={{ flex: 1 }}>
177
- <GestureDetector gesture={combined}>
178
- <View style={{ flex: 1 }} collapsable={false}>
179
- {/* Video Player */}
180
- {!videoLoaded && (
181
- <FastImage
182
- source={{
183
- uri: data.thumbnail,
184
- priority: FastImage.priority.high,
185
- }}
186
- defaultSource={require('../../../assets/img/play.png')}
187
- resizeMode={FastImage.resizeMode.cover}
188
- />
189
- )}
190
-
191
- {isVisible || preload ? (
192
- <Video
193
- poster={data.thumbnail}
194
- ref={videoRef}
195
- source={{ uri: videoUri }}
196
- style={StyleSheet.absoluteFillObject}
197
- resizeMode="cover"
198
- posterResizeMode="cover"
199
- repeat
200
- paused={isPaused}
201
- onLoad={(e) => setDuration(e.duration)}
202
- onProgress={({ currentTime: ct }) => setCurrentTime(ct)}
203
- ignoreSilentSwitch="ignore"
204
- playWhenInactive={false}
205
- playInBackground={false}
206
- controls={false}
207
- controlsStyles={{
208
- hideForward: true,
209
- hideDuration: true,
210
- hideFullscreen: true,
211
- hidePlayPause: true,
212
- hideNavigationBarOnFullScreenMode: true,
213
- hideNotificationBarOnFullScreenMode: true,
214
- hideNext: true,
215
- hidePosition: true,
216
- hidePrevious: true,
217
- hideRewind: true,
218
- hideSeekBar: true,
219
- hideSettingButton: true,
220
- }}
221
- disableFocus={true}
222
- shutterColor="transparent"
223
- onReadyForDisplay={handleVideoLoad}
224
- onEnd={() => {
225
- if (autoScroll) {
226
- onEpisodeSelect(activeEpisodeIndex + 1);
227
- }
228
- }}
229
- />
230
- ) : null}
231
-
232
- {showLikeAnim && (
233
- <View style={styles.lottieContainer}>
234
- <LottieView
235
- style={styles.lottie}
236
- source={DoubleTapAnim}
237
- autoPlay
238
- loop={false}
334
+ <View style={[styles.container, { height: videoHeight }]}>
335
+ <MediaControlsProvider
336
+ theme={theme}
337
+ modal={modal}
338
+ setModal={setModal}
339
+ unlock={unlockProps}
340
+ content={contentProps}
341
+ video={videoProps}
342
+ likes={likesProps}
343
+ actions={actionsProps}
344
+ navigation={navigationProps}
345
+ settings={settingsProps}
346
+ >
347
+ <GestureHandlerRootView style={{ flex: 1 }}>
348
+ <GestureDetector gesture={combinedGesture}>
349
+ <View style={{ flex: 1 }}>
350
+ {buffering && (
351
+ <FastImage
352
+ source={{
353
+ uri: thumbnail,
354
+ priority: FastImage.priority.high,
355
+ }}
356
+ resizeMode={FastImage.resizeMode.cover}
357
+ defaultSource={require('../../../assets/img/play.png')}
358
+ style={StyleSheet.absoluteFillObject}
239
359
  />
240
- </View>
241
- )}
242
-
243
- {paused !== null && (
244
- <View
245
- style={[
246
- styles.playPauseButtonContainer,
247
- { height: videoHeight },
248
- ]}
249
- >
250
- <View style={styles.shadow} pointerEvents="none">
251
- {paused === 'paused' ? (
252
- <Pause color={'#fff'} size={moderateScale(25)} />
253
- ) : (
254
- <Play color={'#fff'} size={moderateScale(25)} />
255
- )}
256
- </View>
257
- </View>
258
- )}
259
-
260
- {/* Overlay */}
261
- <ReelSeriesOverlay
262
- data={data}
263
- reel={reel}
264
- currentTime={currentTime}
265
- duration={duration}
266
- onLikePress={handleLikePress}
267
- onPlayPausePress={() => setIsPaused((prev) => !prev)}
268
- onEpisodesPress={() => {
269
- setModalVisible(true);
270
- }}
271
- onSharePress={onSharePress}
272
- activeEpisodeIndex={activeEpisodeIndex}
273
- />
274
-
275
- {/* Modal */}
276
- <ReelSeriesDetailsModal
277
- visible={modalVisible}
278
- onClose={() => setModalVisible(false)}
279
- reel={reel}
280
- data={data}
281
- activeEpisodeIndex={activeEpisodeIndex}
282
- onEpisodeSelect={handleEpisodeSelect}
283
- />
284
- </View>
285
- </GestureDetector>
286
- </GestureHandlerRootView>
360
+ )}
361
+ {(isVisible || preload) && (
362
+ <Video
363
+ ref={videoRef}
364
+ source={{ uri: videoUri }}
365
+ paused={finalPause}
366
+ repeat
367
+ resizeMode="cover"
368
+ poster={thumbnail}
369
+ posterResizeMode="cover"
370
+ rate={playbackRate}
371
+ onLoadStart={() => {
372
+ setBuffering(true);
373
+ setHasError(false);
374
+ }}
375
+ onBuffer={({ isBuffering }) => setBuffering(isBuffering)}
376
+ onReadyForDisplay={() => {
377
+ setBuffering(false);
378
+ setHasError(false);
379
+ }}
380
+ onLoad={(info) => {
381
+ setDuration(info.duration);
382
+ setHasError(false);
383
+ }}
384
+ onProgress={(p) => setCurrentTime(p.currentTime)}
385
+ onEnd={handleVideoEnd}
386
+ style={StyleSheet.absoluteFillObject}
387
+ controls={false}
388
+ controlsStyles={videoControlsStyles}
389
+ ignoreSilentSwitch="ignore"
390
+ playInBackground={false}
391
+ onError={() => {
392
+ setBuffering(false);
393
+ setHasError(true);
394
+ setIsPaused(true);
395
+ }}
396
+ />
397
+ )}
398
+ </View>
399
+ </GestureDetector>
400
+ </GestureHandlerRootView>
401
+ </MediaControlsProvider>
287
402
  </View>
288
403
  );
289
404
  };
290
405
 
291
- const areEqual = (prevProps: IReelItemProps, nextProps: IReelItemProps) => {
406
+ // Memo comparison
407
+ const areEqual = (prev: IReelItemProps, next: IReelItemProps) => {
292
408
  return (
293
- prevProps?.reel?.episodeId === nextProps?.reel?.episodeId &&
294
- prevProps?.isVisible === nextProps?.isVisible
409
+ prev.reel.episodeId === next.reel.episodeId &&
410
+ prev.isVisible === next.isVisible &&
411
+ prev.preload === next.preload &&
412
+ prev.activeEpisodeIndex === next.activeEpisodeIndex &&
413
+ prev.videoHeight === next.videoHeight &&
414
+ prev.data.isLiked === next.data.isLiked &&
415
+ prev.data.likes === next.data.likes &&
416
+ prev.reel.isLocked === next.reel.isLocked &&
417
+ prev.reel.videoUrl === next.reel.videoUrl &&
418
+ prev.isVisible === next.isVisible
295
419
  );
296
420
  };
297
421
 
298
422
  export default memo(ReelItem, areEqual);
299
423
 
424
+ const { width } = Dimensions.get('window');
425
+
300
426
  const styles = StyleSheet.create({
301
427
  container: {
302
428
  width,
303
- position: 'relative',
304
429
  overflow: 'hidden',
305
- flex: 1,
306
- flexGrow: 1,
307
- },
308
- playPauseButtonContainer: {
309
- position: 'absolute',
310
- top: 0,
311
- left: 0,
312
- right: 0,
313
- bottom: 0,
314
- justifyContent: 'center',
315
- alignItems: 'center',
316
- },
317
- shadow: {
318
- backgroundColor: 'rgba(0,0,0,0.5)',
319
- padding: moderateScale(10),
320
- borderRadius: moderateScale(50),
321
- },
322
- lottieContainer: {
323
- width: '100%',
324
- height: '100%',
325
- position: 'absolute',
326
- justifyContent: 'center',
327
- alignItems: 'center',
328
- },
329
- lottie: {
330
- width: '100%',
331
- height: '100%',
430
+ backgroundColor: '#000',
332
431
  },
333
432
  });