@zezosoft/zezo-ott-react-native-video-player 1.0.4 → 1.0.5

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 (65) hide show
  1. package/package.json +1 -2
  2. package/src/AdsPlayer/AdsPlayer.tsx +0 -311
  3. package/src/AdsPlayer/MediaControls/AdBottomControls.tsx +0 -191
  4. package/src/AdsPlayer/MediaControls/AdMediaControls.tsx +0 -104
  5. package/src/AdsPlayer/MediaControls/AdMediaControlsProvider.tsx +0 -62
  6. package/src/AdsPlayer/MediaControls/AdMiddleControls.tsx +0 -63
  7. package/src/AdsPlayer/MediaControls/AdTopControls.tsx +0 -191
  8. package/src/AdsPlayer/MediaControls/index.ts +0 -5
  9. package/src/AdsPlayer/components/RotatingLoader.tsx +0 -79
  10. package/src/AdsPlayer/index.ts +0 -4
  11. package/src/AdsPlayer/store/adsPlayer.type.ts +0 -29
  12. package/src/AdsPlayer/store/adsPlayerStore.ts +0 -59
  13. package/src/AdsPlayer/store/index.ts +0 -2
  14. package/src/AdsPlayer/utils/adStateReset.ts +0 -29
  15. package/src/AdsPlayer/utils/controls.ts +0 -69
  16. package/src/AdsPlayer/utils/useAdControlsAutoHide.ts +0 -32
  17. package/src/AdsPlayer/utils/useAdInitialization.ts +0 -86
  18. package/src/AdsPlayer/utils/useAdTracking.ts +0 -89
  19. package/src/AdsPlayer/utils/useAdsManager.ts +0 -215
  20. package/src/VideoPlayer/MediaControls/BottomControls.tsx +0 -210
  21. package/src/VideoPlayer/MediaControls/MediaControls.tsx +0 -30
  22. package/src/VideoPlayer/MediaControls/MediaControlsProvider.tsx +0 -104
  23. package/src/VideoPlayer/MediaControls/MiddleControls.tsx +0 -259
  24. package/src/VideoPlayer/MediaControls/TopControls.tsx +0 -100
  25. package/src/VideoPlayer/Settings/AudioAndSubtitles.tsx +0 -295
  26. package/src/VideoPlayer/Settings/Episodes.tsx +0 -297
  27. package/src/VideoPlayer/Settings/SettingModal.tsx +0 -127
  28. package/src/VideoPlayer/Settings/SpeedControls.tsx +0 -130
  29. package/src/VideoPlayer/Settings/VideoPlayerSettings.tsx +0 -141
  30. package/src/VideoPlayer/VideoPlayerCore.tsx +0 -356
  31. package/src/VideoPlayer/components/ProgressBar.tsx +0 -211
  32. package/src/VideoPlayer/components/SkipAndNextControls.tsx +0 -192
  33. package/src/VideoPlayer/components/SubtitleView.tsx +0 -53
  34. package/src/VideoPlayer/components/Toast.tsx +0 -61
  35. package/src/VideoPlayer/context/VideoPlayerConfig.tsx +0 -65
  36. package/src/VideoPlayer/context/index.ts +0 -5
  37. package/src/VideoPlayer/index.ts +0 -4
  38. package/src/VideoPlayer/store/index.ts +0 -2
  39. package/src/VideoPlayer/store/videoPlayer.type.ts +0 -214
  40. package/src/VideoPlayer/store/videoPlayerStore.ts +0 -97
  41. package/src/VideoPlayer/styles/globalStyles.ts +0 -73
  42. package/src/VideoPlayer/utils/display/Display.ts +0 -10
  43. package/src/VideoPlayer/utils/display/index.ts +0 -1
  44. package/src/VideoPlayer/utils/format/index.ts +0 -1
  45. package/src/VideoPlayer/utils/format/timeFormatter.ts +0 -44
  46. package/src/VideoPlayer/utils/hooks/index.ts +0 -5
  47. package/src/VideoPlayer/utils/hooks/useAdEventHandler.ts +0 -95
  48. package/src/VideoPlayer/utils/hooks/useOrientationLock.ts +0 -29
  49. package/src/VideoPlayer/utils/hooks/usePauseVideoOnAd.ts +0 -46
  50. package/src/VideoPlayer/utils/hooks/useVideoPlayerBack.ts +0 -66
  51. package/src/VideoPlayer/utils/hooks/useVideoResolutions.ts +0 -125
  52. package/src/VideoPlayer/utils/index.ts +0 -6
  53. package/src/VideoPlayer/utils/platform/PlatformSelector.ts +0 -13
  54. package/src/VideoPlayer/utils/platform/index.ts +0 -2
  55. package/src/VideoPlayer/utils/platform/lockOrientation.ts +0 -40
  56. package/src/VideoPlayer/utils/player/index.ts +0 -2
  57. package/src/VideoPlayer/utils/player/playerEvents.ts +0 -97
  58. package/src/VideoPlayer/utils/player/useWatchReporter.ts +0 -105
  59. package/src/VideoPlayer/utils/video/index.ts +0 -5
  60. package/src/VideoPlayer/utils/video/videoControl.ts +0 -185
  61. package/src/VideoPlayer/utils/video/videoRef.ts +0 -21
  62. package/src/VideoPlayer/utils/video/videoResume.ts +0 -23
  63. package/src/VideoPlayer/utils/video/videoSource.ts +0 -23
  64. package/src/VideoPlayer.tsx +0 -181
  65. package/src/index.tsx +0 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zezosoft/zezo-ott-react-native-video-player",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "Production-ready React Native OTT video player library for Android & iOS. Features: playlists, seasons, auto-next playback, subtitles (SRT/VTT), custom theming, analytics tracking, fullscreen mode, gesture controls, ads player (pre-roll/mid-roll/post-roll), TypeScript support, and centralized state management. Perfect for streaming apps, video platforms, and OTT services.",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -19,7 +19,6 @@
19
19
  "ios",
20
20
  "lib",
21
21
  "react-native.config.js",
22
- "src",
23
22
  "!android/build",
24
23
  "!android/gradle",
25
24
  "!android/gradlew",
@@ -1,311 +0,0 @@
1
- import {
2
- useEffect,
3
- useRef,
4
- useImperativeHandle,
5
- forwardRef,
6
- useCallback,
7
- useMemo,
8
- } from 'react';
9
- import { View } from 'react-native';
10
- import globalStyles from '../VideoPlayer/styles/globalStyles';
11
- import Video, {
12
- type OnBufferData,
13
- type OnLoadStartData,
14
- type OnProgressData,
15
- type VideoRef,
16
- } from 'react-native-video';
17
- import type { EdgeInsets } from 'react-native-safe-area-context';
18
- import { useAdsPlayerStore } from './store/adsPlayerStore';
19
- import type { VideoAd } from '../VideoPlayer/store/videoPlayer.type';
20
- import { AdMediaControlsProvider } from './MediaControls';
21
- import { Gesture, GestureDetector } from 'react-native-gesture-handler';
22
- import { runOnJS } from 'react-native-reanimated';
23
- import { toggleAdControls, showAdControls } from './utils/controls';
24
- import { resetAdState } from './utils/adStateReset';
25
- import { useAdTracking } from './utils/useAdTracking';
26
-
27
- export interface AdsPlayerProps {
28
- onAdEnd: (ad: VideoAd) => void;
29
- onAdSkip?: (ad: VideoAd) => void;
30
- onAdError?: (error: Error, ad: VideoAd) => void;
31
- onAdTracking?: ({
32
- ad,
33
- trackingUrl,
34
- event,
35
- }: {
36
- ad: VideoAd;
37
- trackingUrl: string;
38
- event: string;
39
- }) => void;
40
- onClose?: () => void;
41
- insets: EdgeInsets;
42
- }
43
-
44
- export interface AdsPlayerRef {
45
- seek: (time: number) => void;
46
- getCurrentTime: () => number;
47
- getDuration: () => number;
48
- }
49
-
50
- const AdsPlayer = forwardRef<AdsPlayerRef, AdsPlayerProps>(
51
- ({ onAdEnd, onAdSkip, onAdError, onAdTracking, onClose, insets }, ref) => {
52
- const {
53
- currentAd,
54
- adDuration,
55
- adCurrentTime,
56
- isAdPaused,
57
- isAdMuted,
58
- isAdPlaying,
59
- setIsAdPaused,
60
- setAdCurrentTime,
61
- setAdDuration,
62
- setShowSkipButton,
63
- setIsAdBuffering,
64
- } = useAdsPlayerStore();
65
- const adVideoRef = useRef<VideoRef>(null);
66
- const trackedQuartilesRef = useRef({
67
- firstQuartile: false,
68
- midpoint: false,
69
- thirdQuartile: false,
70
- });
71
- const trackedImpressionRef = useRef<string | null>(null);
72
- const {
73
- trackAdImpression,
74
- trackAdStart,
75
- trackAdComplete,
76
- trackAdQuartile,
77
- } = useAdTracking({ onAdTracking });
78
-
79
- const trigger = useCallback(() => {
80
- toggleAdControls();
81
- }, []);
82
-
83
- const tapGesture = useMemo(
84
- () =>
85
- Gesture.Tap()
86
- .onEnd(() => {
87
- runOnJS(trigger)();
88
- })
89
- .maxDuration(250),
90
- [trigger]
91
- );
92
-
93
- useImperativeHandle(ref, () => ({
94
- seek: (time: number) => {
95
- adVideoRef.current?.seek(time);
96
- },
97
- getCurrentTime: () => adCurrentTime,
98
- getDuration: () => adDuration,
99
- }));
100
-
101
- // Track impression only once per ad
102
- useEffect(() => {
103
- if (
104
- currentAd &&
105
- isAdPlaying &&
106
- trackedImpressionRef.current !== currentAd.id
107
- ) {
108
- try {
109
- trackedImpressionRef.current = currentAd.id;
110
- trackAdImpression(currentAd);
111
- } catch (error) {
112
- console.error('Error in ad impression tracking:', error);
113
- }
114
- }
115
- }, [currentAd, isAdPlaying, trackAdImpression]);
116
-
117
- // Separate effect for ensuring ad is not paused
118
- useEffect(() => {
119
- if (currentAd && isAdPlaying) {
120
- setIsAdPaused(false);
121
- showAdControls();
122
- }
123
- }, [currentAd, isAdPlaying, setIsAdPaused]);
124
-
125
- useEffect(() => {
126
- if (currentAd?.skippable) {
127
- if (currentAd.skipAfter > 0) {
128
- const timer = setTimeout(() => {
129
- setShowSkipButton(true);
130
- }, currentAd.skipAfter * 1000);
131
- return () => clearTimeout(timer);
132
- }
133
- setShowSkipButton(true);
134
- return undefined;
135
- }
136
- setShowSkipButton(false);
137
- return undefined;
138
- }, [currentAd, setShowSkipButton]);
139
-
140
- const handleAdProgress = (data: OnProgressData) => {
141
- setAdCurrentTime(data.currentTime);
142
- setAdDuration(data.seekableDuration || 0);
143
-
144
- // Reset buffering state when progress updates (playback has resumed)
145
- const { isAdBuffering } = useAdsPlayerStore.getState();
146
- if (isAdBuffering) {
147
- setIsAdBuffering(false);
148
- }
149
-
150
- if (currentAd && adDuration > 0) {
151
- const progress = data.currentTime / adDuration;
152
- if (progress >= 0.25 && !trackedQuartilesRef.current.firstQuartile) {
153
- trackedQuartilesRef.current.firstQuartile = true;
154
- trackAdQuartile(currentAd, 'firstQuartile');
155
- }
156
- if (progress >= 0.5 && !trackedQuartilesRef.current.midpoint) {
157
- trackedQuartilesRef.current.midpoint = true;
158
- trackAdQuartile(currentAd, 'midpoint');
159
- }
160
- if (progress >= 0.75 && !trackedQuartilesRef.current.thirdQuartile) {
161
- trackedQuartilesRef.current.thirdQuartile = true;
162
- trackAdQuartile(currentAd, 'thirdQuartile');
163
- }
164
- }
165
- };
166
-
167
- const handleAdLoadStart = (_: OnLoadStartData) => {
168
- setIsAdBuffering(true);
169
- };
170
-
171
- const handleAdLoad = () => {
172
- try {
173
- setIsAdBuffering(false);
174
- if (currentAd) {
175
- setAdDuration(currentAd.duration);
176
- trackAdStart(currentAd);
177
- }
178
- } catch (error) {
179
- console.error('Error in handleAdLoad:', error);
180
- if (currentAd && onAdError) {
181
- onAdError(
182
- error instanceof Error ? error : new Error('Ad load failed'),
183
- currentAd
184
- );
185
- }
186
- }
187
- };
188
-
189
- const handleAdEnd = () => {
190
- if (!currentAd) return;
191
-
192
- try {
193
- trackAdComplete(currentAd);
194
- resetAdState();
195
- trackedQuartilesRef.current = {
196
- firstQuartile: false,
197
- midpoint: false,
198
- thirdQuartile: false,
199
- };
200
- // Reset impression tracking for next ad
201
- trackedImpressionRef.current = null;
202
- onAdEnd(currentAd);
203
- } catch (error) {
204
- console.error('Error in handleAdEnd:', error);
205
- resetAdState();
206
- trackedImpressionRef.current = null;
207
- onAdEnd(currentAd);
208
- }
209
- };
210
-
211
- const handleAdError = (error: {
212
- error: {
213
- code?: string;
214
- localizedDescription?: string;
215
- errorString?: string;
216
- };
217
- }) => {
218
- if (!currentAd) return;
219
-
220
- try {
221
- const errorMessage =
222
- error.error?.localizedDescription ||
223
- error.error?.errorString ||
224
- 'Ad playback failed';
225
- console.error('Ad playback error:', errorMessage, error);
226
-
227
- resetAdState();
228
- // Reset impression tracking on error
229
- trackedImpressionRef.current = null;
230
-
231
- if (onAdError) {
232
- onAdError(new Error(errorMessage), currentAd);
233
- } else {
234
- // Fallback: treat error as ad end
235
- onAdEnd(currentAd);
236
- }
237
- } catch (err) {
238
- console.error('Error in handleAdError:', err);
239
- resetAdState();
240
- trackedImpressionRef.current = null;
241
- if (currentAd && onAdEnd) {
242
- onAdEnd(currentAd);
243
- }
244
- }
245
- };
246
-
247
- // Memoize video style for performance (no scale to avoid layout overflow)
248
- const videoStyle = useMemo(() => globalStyles.absoluteFill, []);
249
-
250
- // Memoize container style — overflow hidden to prevent layout glitches
251
- const containerStyle = useMemo(
252
- () => [
253
- globalStyles.absoluteFill,
254
- { backgroundColor: '#000', overflow: 'hidden' as const },
255
- ],
256
- []
257
- );
258
-
259
- if (!currentAd || !isAdPlaying) {
260
- return null;
261
- }
262
-
263
- return (
264
- <GestureDetector gesture={tapGesture}>
265
- <View
266
- style={containerStyle}
267
- collapsable={false}
268
- pointerEvents="box-none"
269
- >
270
- <AdMediaControlsProvider
271
- onAdSkip={onAdSkip}
272
- onAdTracking={onAdTracking}
273
- onClose={onClose}
274
- insets={insets}
275
- >
276
- <Video
277
- ref={adVideoRef}
278
- source={{ uri: currentAd.source }}
279
- paused={isAdPaused}
280
- muted={isAdMuted}
281
- onProgress={handleAdProgress}
282
- onLoad={handleAdLoad}
283
- onEnd={handleAdEnd}
284
- onError={handleAdError}
285
- onBuffer={({ isBuffering }: OnBufferData) => {
286
- if (isBuffering) {
287
- setIsAdBuffering(true);
288
- } else {
289
- setTimeout(() => setIsAdBuffering(false), 150);
290
- }
291
- }}
292
- onLoadStart={handleAdLoadStart}
293
- resizeMode="cover"
294
- style={videoStyle}
295
- controls={false}
296
- playInBackground={false}
297
- playWhenInactive={false}
298
- pointerEvents="box-none"
299
- ignoreSilentSwitch="ignore"
300
- progressUpdateInterval={250}
301
- />
302
- </AdMediaControlsProvider>
303
- </View>
304
- </GestureDetector>
305
- );
306
- }
307
- );
308
-
309
- AdsPlayer.displayName = 'AdsPlayer';
310
-
311
- export default AdsPlayer;
@@ -1,191 +0,0 @@
1
- import React, { memo, useCallback, useMemo } from 'react';
2
- import {
3
- Animated,
4
- StyleSheet,
5
- Text,
6
- View,
7
- TouchableOpacity,
8
- } from 'react-native';
9
- import { scale, moderateScale } from 'react-native-size-matters';
10
- import { RFValue } from 'react-native-responsive-fontsize';
11
- import { ExternalLink } from 'lucide-react-native';
12
- import { useAdsPlayerStore } from '../store/adsPlayerStore';
13
- import { useAdControls } from './AdMediaControlsProvider';
14
- import { useAdControlsAutoHide } from '../utils/useAdControlsAutoHide';
15
- import ProgressBar from '../../VideoPlayer/components/ProgressBar';
16
- import { handleAdClickThrough } from '../utils/controls';
17
- import { resetAdState } from '../utils/adStateReset';
18
- import { useAdTracking } from '../utils/useAdTracking';
19
-
20
- const AdBottomControls: React.FC = () => {
21
- const currentAd = useAdsPlayerStore((state) => state.currentAd);
22
- const adCurrentTime = useAdsPlayerStore((state) => state.adCurrentTime);
23
- const adDuration = useAdsPlayerStore((state) => state.adDuration);
24
- const { onAdSkip, onAdTracking } = useAdControls();
25
- const fadeAnim = useAdControlsAutoHide();
26
- const { trackAdSkip, trackAdClick } = useAdTracking({ onAdTracking });
27
-
28
- const shouldShowSkipButton = useMemo(
29
- () =>
30
- currentAd?.skippable &&
31
- currentAd.skipAfter > 0 &&
32
- adCurrentTime >= currentAd.skipAfter,
33
- [currentAd?.skippable, currentAd?.skipAfter, adCurrentTime]
34
- );
35
-
36
- // Memoize animated style
37
- const animatedOpacityStyle = useMemo(
38
- () => ({ opacity: fadeAnim }),
39
- [fadeAnim]
40
- );
41
-
42
- const handleSkip = useCallback(() => {
43
- if (!currentAd) return;
44
-
45
- trackAdSkip(currentAd);
46
- onAdSkip?.(currentAd);
47
- resetAdState();
48
- }, [currentAd, onAdSkip, trackAdSkip]);
49
-
50
- const handleClickThrough = useCallback(() => {
51
- if (currentAd) {
52
- trackAdClick(currentAd);
53
- handleAdClickThrough(currentAd, onAdTracking);
54
- }
55
- }, [currentAd, onAdTracking, trackAdClick]);
56
-
57
- if (!currentAd) {
58
- return null;
59
- }
60
-
61
- return (
62
- <View style={bottomControlsStyles.container}>
63
- {/* Ad Metadata */}
64
- {currentAd.clickThroughUrl && (
65
- <Animated.View style={animatedOpacityStyle} pointerEvents="box-none">
66
- <View pointerEvents="auto">
67
- <TouchableOpacity
68
- style={bottomControlsStyles.metadataContainer}
69
- onPress={handleClickThrough}
70
- activeOpacity={0.7}
71
- >
72
- <View style={bottomControlsStyles.metadataContent}>
73
- <View style={bottomControlsStyles.metadataTextContainer}>
74
- {currentAd.title && (
75
- <Text
76
- style={bottomControlsStyles.metadataTitle}
77
- numberOfLines={1}
78
- ellipsizeMode="tail"
79
- >
80
- {currentAd.title}
81
- </Text>
82
- )}
83
- {currentAd.description && (
84
- <Text
85
- style={bottomControlsStyles.metadataDesc}
86
- numberOfLines={2}
87
- ellipsizeMode="tail"
88
- >
89
- {currentAd.description}
90
- </Text>
91
- )}
92
- </View>
93
- <View style={bottomControlsStyles.clickIndicator}>
94
- <ExternalLink size={scale(14)} color="#fff" />
95
- </View>
96
- </View>
97
- </TouchableOpacity>
98
- </View>
99
- </Animated.View>
100
- )}
101
-
102
- <Animated.View style={animatedOpacityStyle}>
103
- <ProgressBar
104
- duration={adDuration}
105
- currentTime={adCurrentTime}
106
- bufferedTime={adDuration}
107
- disabled={true}
108
- height={moderateScale(3)}
109
- />
110
- </Animated.View>
111
- {shouldShowSkipButton && (
112
- <View pointerEvents="auto">
113
- <TouchableOpacity
114
- onPress={handleSkip}
115
- style={bottomControlsStyles.skipButton}
116
- activeOpacity={0.7}
117
- >
118
- <Text style={bottomControlsStyles.skipButtonText}>Skip Ad</Text>
119
- </TouchableOpacity>
120
- </View>
121
- )}
122
- </View>
123
- );
124
- };
125
-
126
- const bottomControlsStyles = StyleSheet.create({
127
- container: {
128
- position: 'relative',
129
- width: '100%',
130
- bottom: 0,
131
- zIndex: 1,
132
- },
133
- metadataContainer: {
134
- backgroundColor: 'rgba(21, 21, 21, 0.95)',
135
- borderRadius: moderateScale(6),
136
- overflow: 'hidden',
137
- marginBottom: moderateScale(5),
138
- maxWidth: moderateScale(200),
139
- borderWidth: 1,
140
- borderColor: 'rgba(174, 174, 174, 0.12)',
141
- },
142
- metadataContent: {
143
- flexDirection: 'row',
144
- alignItems: 'center',
145
- padding: moderateScale(5),
146
- },
147
- metadataTextContainer: {
148
- flex: 1,
149
- marginRight: moderateScale(4),
150
- minWidth: 0,
151
- },
152
- metadataTitle: {
153
- color: '#FFFFFF',
154
- fontSize: RFValue(10),
155
- fontWeight: '400',
156
- marginBottom: moderateScale(0.5),
157
- letterSpacing: 0.1,
158
- },
159
- metadataDesc: {
160
- color: 'rgba(255, 255, 255, 0.7)',
161
- fontSize: RFValue(9),
162
- lineHeight: RFValue(12),
163
- },
164
- clickIndicator: {
165
- width: moderateScale(20),
166
- height: moderateScale(20),
167
- borderRadius: moderateScale(10),
168
- backgroundColor: 'rgba(255, 255, 255, 0.20)',
169
- justifyContent: 'center',
170
- alignItems: 'center',
171
- },
172
- skipButton: {
173
- position: 'absolute',
174
- right: moderateScale(2),
175
- bottom: moderateScale(32),
176
- backgroundColor: 'rgba(21, 21, 21, 0.95)',
177
- paddingHorizontal: moderateScale(15),
178
- paddingVertical: moderateScale(7),
179
- borderRadius: moderateScale(14),
180
- zIndex: 10,
181
- borderWidth: 1,
182
- borderColor: 'rgba(255, 255, 255, 0.20)',
183
- },
184
- skipButtonText: {
185
- color: '#fff',
186
- fontSize: RFValue(12),
187
- fontWeight: '600',
188
- },
189
- });
190
-
191
- export default memo(AdBottomControls);
@@ -1,104 +0,0 @@
1
- import { memo, useMemo } from 'react';
2
- import { Animated, StyleSheet, View, type ViewStyle } from 'react-native';
3
- import LinearGradient from 'react-native-linear-gradient';
4
- import { moderateScale } from 'react-native-size-matters';
5
- import type { EdgeInsets } from 'react-native-safe-area-context';
6
- import { useAdControlsAutoHide } from '../utils/useAdControlsAutoHide';
7
- import AdTopControls from './AdTopControls';
8
- import AdMiddleControls from './AdMiddleControls';
9
- import AdBottomControls from './AdBottomControls';
10
- import globalStyles from '../../VideoPlayer/styles/globalStyles';
11
-
12
- interface AdMediaControlsProps {
13
- onClose?: () => void;
14
- insets: EdgeInsets;
15
- }
16
-
17
- const HORIZONTAL_PADDING = moderateScale(12);
18
- const BOTTOM_PADDING_EXTRA = moderateScale(8);
19
- const MIN_TOP_PADDING = moderateScale(10);
20
-
21
- const AdMediaControls: React.FC<AdMediaControlsProps> = ({
22
- onClose,
23
- insets,
24
- }) => {
25
- const { top, bottom, left, right } = insets;
26
- const fadeAnim = useAdControlsAutoHide();
27
-
28
- // Safe-area aware padding: never negative, consistent touch targets
29
- const containerStyle: ViewStyle = useMemo(
30
- () => ({
31
- ...styles.container,
32
- paddingTop: top - MIN_TOP_PADDING,
33
- paddingBottom: bottom + BOTTOM_PADDING_EXTRA,
34
- paddingLeft: left + HORIZONTAL_PADDING,
35
- paddingRight: right + HORIZONTAL_PADDING,
36
- }),
37
- [top, bottom, left, right]
38
- );
39
-
40
- // Memoize gradient overlay styles — consistent positioning, no negative glitches
41
- const topGradientStyle = useMemo(
42
- () => [styles.topGradientOverlay, { opacity: fadeAnim, top: -top }],
43
- [fadeAnim, top]
44
- );
45
-
46
- const bottomGradientStyle = useMemo(
47
- () => [
48
- styles.bottomGradientOverlay,
49
- { opacity: fadeAnim, bottom: -bottom },
50
- ],
51
- [fadeAnim, bottom]
52
- );
53
-
54
- return (
55
- <>
56
- <Animated.View style={topGradientStyle} pointerEvents="none">
57
- <LinearGradient
58
- colors={['rgba(0, 0, 0, 0.4)', 'transparent', 'transparent']}
59
- locations={[0, 0.4, 1]}
60
- style={globalStyles.absoluteFill}
61
- pointerEvents="none"
62
- />
63
- </Animated.View>
64
- <View style={containerStyle}>
65
- <AdTopControls onClose={onClose} />
66
- <AdMiddleControls />
67
- <AdBottomControls />
68
- </View>
69
- <Animated.View style={bottomGradientStyle} pointerEvents="none">
70
- <LinearGradient
71
- colors={['transparent', 'transparent', 'rgba(0, 0, 0, 0.5)']}
72
- locations={[0, 0.4, 1]}
73
- style={globalStyles.absoluteFill}
74
- pointerEvents="none"
75
- />
76
- </Animated.View>
77
- </>
78
- );
79
- };
80
-
81
- export default memo(AdMediaControls);
82
-
83
- const styles = StyleSheet.create({
84
- container: {
85
- ...globalStyles.absoluteFill,
86
- justifyContent: 'space-between',
87
- },
88
- topGradientOverlay: {
89
- position: 'absolute',
90
- left: 0,
91
- right: 0,
92
- width: '100%',
93
- height: moderateScale(150),
94
- zIndex: 0,
95
- },
96
- bottomGradientOverlay: {
97
- position: 'absolute',
98
- left: 0,
99
- right: 0,
100
- width: '100%',
101
- height: moderateScale(150),
102
- zIndex: 0,
103
- },
104
- });
@@ -1,62 +0,0 @@
1
- import React, { createContext, useContext } from 'react';
2
- import type { EdgeInsets } from 'react-native-safe-area-context';
3
-
4
- import { useAdsPlayerStore } from '../store/adsPlayerStore';
5
- import type { VideoAd } from '../../VideoPlayer/store/videoPlayer.type';
6
- import AdMediaControls from './AdMediaControls';
7
-
8
- interface AdControlsContextType {
9
- onAdSkip?: (ad: VideoAd) => void;
10
- onAdTracking?: ({
11
- ad,
12
- trackingUrl,
13
- event,
14
- }: {
15
- ad: VideoAd;
16
- trackingUrl: string;
17
- event: string;
18
- }) => void;
19
- }
20
-
21
- const AdControlsContext = createContext<AdControlsContextType>({});
22
-
23
- export const useAdControls = () => useContext(AdControlsContext);
24
-
25
- interface AdMediaControlsProviderProps {
26
- children: React.ReactNode;
27
- onAdSkip?: (ad: VideoAd) => void;
28
- onAdTracking?: ({
29
- ad,
30
- trackingUrl,
31
- event,
32
- }: {
33
- ad: VideoAd;
34
- trackingUrl: string;
35
- event: string;
36
- }) => void;
37
- onClose?: () => void;
38
- insets: EdgeInsets;
39
- }
40
-
41
- const AdMediaControlsProvider: React.FC<AdMediaControlsProviderProps> = ({
42
- children,
43
- onAdSkip,
44
- onAdTracking,
45
- onClose,
46
- insets,
47
- }) => {
48
- const isAdPlaying = useAdsPlayerStore((state) => state.isAdPlaying);
49
-
50
- if (!isAdPlaying) {
51
- return <>{children}</>;
52
- }
53
-
54
- return (
55
- <AdControlsContext.Provider value={{ onAdSkip, onAdTracking }}>
56
- {children}
57
- <AdMediaControls onClose={onClose} insets={insets} />
58
- </AdControlsContext.Provider>
59
- );
60
- };
61
-
62
- export default AdMediaControlsProvider;