@zezosoft/zezo-ott-react-native-video-player 1.0.3 → 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 (67) hide show
  1. package/lib/module/AdsPlayer/MediaControls/AdBottomControls.js +2 -2
  2. package/lib/module/AdsPlayer/MediaControls/AdBottomControls.js.map +1 -1
  3. package/package.json +1 -2
  4. package/src/AdsPlayer/AdsPlayer.tsx +0 -311
  5. package/src/AdsPlayer/MediaControls/AdBottomControls.tsx +0 -191
  6. package/src/AdsPlayer/MediaControls/AdMediaControls.tsx +0 -104
  7. package/src/AdsPlayer/MediaControls/AdMediaControlsProvider.tsx +0 -62
  8. package/src/AdsPlayer/MediaControls/AdMiddleControls.tsx +0 -63
  9. package/src/AdsPlayer/MediaControls/AdTopControls.tsx +0 -191
  10. package/src/AdsPlayer/MediaControls/index.ts +0 -5
  11. package/src/AdsPlayer/components/RotatingLoader.tsx +0 -79
  12. package/src/AdsPlayer/index.ts +0 -4
  13. package/src/AdsPlayer/store/adsPlayer.type.ts +0 -29
  14. package/src/AdsPlayer/store/adsPlayerStore.ts +0 -59
  15. package/src/AdsPlayer/store/index.ts +0 -2
  16. package/src/AdsPlayer/utils/adStateReset.ts +0 -29
  17. package/src/AdsPlayer/utils/controls.ts +0 -69
  18. package/src/AdsPlayer/utils/useAdControlsAutoHide.ts +0 -32
  19. package/src/AdsPlayer/utils/useAdInitialization.ts +0 -86
  20. package/src/AdsPlayer/utils/useAdTracking.ts +0 -89
  21. package/src/AdsPlayer/utils/useAdsManager.ts +0 -215
  22. package/src/VideoPlayer/MediaControls/BottomControls.tsx +0 -210
  23. package/src/VideoPlayer/MediaControls/MediaControls.tsx +0 -30
  24. package/src/VideoPlayer/MediaControls/MediaControlsProvider.tsx +0 -104
  25. package/src/VideoPlayer/MediaControls/MiddleControls.tsx +0 -259
  26. package/src/VideoPlayer/MediaControls/TopControls.tsx +0 -100
  27. package/src/VideoPlayer/Settings/AudioAndSubtitles.tsx +0 -295
  28. package/src/VideoPlayer/Settings/Episodes.tsx +0 -297
  29. package/src/VideoPlayer/Settings/SettingModal.tsx +0 -127
  30. package/src/VideoPlayer/Settings/SpeedControls.tsx +0 -130
  31. package/src/VideoPlayer/Settings/VideoPlayerSettings.tsx +0 -141
  32. package/src/VideoPlayer/VideoPlayerCore.tsx +0 -356
  33. package/src/VideoPlayer/components/ProgressBar.tsx +0 -211
  34. package/src/VideoPlayer/components/SkipAndNextControls.tsx +0 -192
  35. package/src/VideoPlayer/components/SubtitleView.tsx +0 -53
  36. package/src/VideoPlayer/components/Toast.tsx +0 -61
  37. package/src/VideoPlayer/context/VideoPlayerConfig.tsx +0 -65
  38. package/src/VideoPlayer/context/index.ts +0 -5
  39. package/src/VideoPlayer/index.ts +0 -4
  40. package/src/VideoPlayer/store/index.ts +0 -2
  41. package/src/VideoPlayer/store/videoPlayer.type.ts +0 -214
  42. package/src/VideoPlayer/store/videoPlayerStore.ts +0 -97
  43. package/src/VideoPlayer/styles/globalStyles.ts +0 -73
  44. package/src/VideoPlayer/utils/display/Display.ts +0 -10
  45. package/src/VideoPlayer/utils/display/index.ts +0 -1
  46. package/src/VideoPlayer/utils/format/index.ts +0 -1
  47. package/src/VideoPlayer/utils/format/timeFormatter.ts +0 -44
  48. package/src/VideoPlayer/utils/hooks/index.ts +0 -5
  49. package/src/VideoPlayer/utils/hooks/useAdEventHandler.ts +0 -95
  50. package/src/VideoPlayer/utils/hooks/useOrientationLock.ts +0 -29
  51. package/src/VideoPlayer/utils/hooks/usePauseVideoOnAd.ts +0 -46
  52. package/src/VideoPlayer/utils/hooks/useVideoPlayerBack.ts +0 -66
  53. package/src/VideoPlayer/utils/hooks/useVideoResolutions.ts +0 -125
  54. package/src/VideoPlayer/utils/index.ts +0 -6
  55. package/src/VideoPlayer/utils/platform/PlatformSelector.ts +0 -13
  56. package/src/VideoPlayer/utils/platform/index.ts +0 -2
  57. package/src/VideoPlayer/utils/platform/lockOrientation.ts +0 -40
  58. package/src/VideoPlayer/utils/player/index.ts +0 -2
  59. package/src/VideoPlayer/utils/player/playerEvents.ts +0 -97
  60. package/src/VideoPlayer/utils/player/useWatchReporter.ts +0 -105
  61. package/src/VideoPlayer/utils/video/index.ts +0 -5
  62. package/src/VideoPlayer/utils/video/videoControl.ts +0 -185
  63. package/src/VideoPlayer/utils/video/videoRef.ts +0 -21
  64. package/src/VideoPlayer/utils/video/videoResume.ts +0 -23
  65. package/src/VideoPlayer/utils/video/videoSource.ts +0 -23
  66. package/src/VideoPlayer.tsx +0 -181
  67. package/src/index.tsx +0 -3
@@ -1,295 +0,0 @@
1
- import React, { useMemo } from 'react';
2
- import {
3
- FlatList,
4
- Platform,
5
- TouchableOpacity,
6
- View,
7
- Text,
8
- StyleSheet,
9
- } from 'react-native';
10
- import { scale, verticalScale } from 'react-native-size-matters';
11
- import { RFValue } from 'react-native-responsive-fontsize';
12
- import type { OnLoadData } from 'react-native-video';
13
- import {
14
- useVideoPlayerStore,
15
- type SubtitleTrack,
16
- type TrackSelection,
17
- } from '../store';
18
- import { useVideoPlayerConfig } from '../context';
19
-
20
- const ALLOWED_SUBTITLE_TYPES = [
21
- 'application/x-subrip',
22
- 'text/vtt',
23
- 'application/ttml+xml',
24
- ];
25
-
26
- const mapSubtitleType = (mimeType: string): 'srt' | 'ttml' | 'vtt' => {
27
- switch (mimeType) {
28
- case 'application/x-subrip':
29
- return 'srt';
30
- case 'application/ttml+xml':
31
- return 'ttml';
32
- case 'text/vtt':
33
- return 'vtt';
34
- default:
35
- return 'srt';
36
- }
37
- };
38
-
39
- const Separator = () => <View style={styles.separator} />;
40
-
41
- interface AudioItemProps {
42
- item: OnLoadData['audioTracks'][0];
43
- isSelected: boolean;
44
- onPress: () => void;
45
- colors: { text: string };
46
- }
47
- const AudioItem: React.FC<AudioItemProps> = ({
48
- item,
49
- isSelected,
50
- onPress,
51
- colors,
52
- }) => (
53
- <TouchableOpacity onPress={onPress}>
54
- <Text
55
- style={[
56
- styles.listItem,
57
- { color: colors.text },
58
- isSelected && styles.selected,
59
- ]}
60
- >
61
- {item.title}
62
- </Text>
63
- </TouchableOpacity>
64
- );
65
-
66
- interface SubtitleItemProps {
67
- item: TrackSelection;
68
- isSelected: boolean;
69
- onPress: () => void;
70
- colors: { text: string };
71
- }
72
- const SubtitleItem: React.FC<SubtitleItemProps> = ({
73
- item,
74
- isSelected,
75
- onPress,
76
- colors,
77
- }) => (
78
- <TouchableOpacity onPress={onPress}>
79
- <Text
80
- style={[
81
- styles.listItem,
82
- { color: colors.text },
83
- isSelected && styles.selected,
84
- ]}
85
- >
86
- {item.title || 'Unknown'}
87
- </Text>
88
- </TouchableOpacity>
89
- );
90
-
91
- const AudioAndSubtitles: React.FC = () => {
92
- const {
93
- setSettingsModal,
94
- onLoad,
95
- setSelectedAudioTrack,
96
- setSelectedSubtitleTrack,
97
- selectedAudioTrack,
98
- selectedSubtitleTrack,
99
- setActiveSubtitle,
100
- activeTrack,
101
- activeSubtitle,
102
- setIsPaused,
103
- } = useVideoPlayerStore();
104
- const { colors } = useVideoPlayerConfig();
105
-
106
- const audioTracks = useMemo(() => {
107
- return (
108
- onLoad?.audioTracks?.filter(
109
- (item: OnLoadData['audioTracks'][0]) =>
110
- item.title !== null && item.title !== ''
111
- ) || []
112
- );
113
- }, [onLoad?.audioTracks]);
114
-
115
- const subtitleTracks = useMemo(() => {
116
- const internal =
117
- onLoad?.textTracks
118
- ?.filter((track) => {
119
- const type = String(track.type);
120
- const isCEA608 =
121
- Platform.OS === 'android' && type === 'application/cea-608';
122
- return (
123
- !isCEA608 &&
124
- ALLOWED_SUBTITLE_TYPES.includes(type) &&
125
- track.title?.trim()
126
- );
127
- })
128
- .map((track, idx) => ({
129
- index: idx,
130
- title: track.title,
131
- language: track.language,
132
- type: track.type ? mapSubtitleType(String(track.type)) : 'srt',
133
- value: idx,
134
- isExternal: false,
135
- })) || [];
136
-
137
- const external =
138
- activeTrack?.subtitles?.map((item, idx) => ({
139
- index: internal.length + idx,
140
- type: mapSubtitleType(item.type),
141
- value: internal.length + idx,
142
- language: item.language,
143
- title: item.title || item.language || `Subtitle ${idx + 1}`,
144
- uri: item.url,
145
- isExternal: true,
146
- })) || [];
147
-
148
- return [...internal, ...external];
149
- }, [activeTrack?.subtitles, onLoad?.textTracks]);
150
-
151
- const handleSelectAudio = (item: OnLoadData['audioTracks'][0]) => {
152
- setSelectedAudioTrack({
153
- type: 'index',
154
- value: item.index,
155
- isExternal: false,
156
- });
157
- setSettingsModal({ isVisible: false, action: 'none' });
158
- };
159
-
160
- const handleSelectSubtitle = (item: TrackSelection) => {
161
- if (item.isExternal) {
162
- setActiveSubtitle({
163
- type: item.type as SubtitleTrack['type'],
164
- url: item.uri!,
165
- language: item.language,
166
- title: item.title,
167
- });
168
- setSelectedSubtitleTrack({
169
- type: 'index',
170
- value: 'Off',
171
- isExternal: false,
172
- });
173
- } else {
174
- setSelectedSubtitleTrack({
175
- type: 'index',
176
- value: Number(item.value),
177
- isExternal: false,
178
- });
179
- setActiveSubtitle(null);
180
- }
181
- setIsPaused(false);
182
- setSettingsModal({ isVisible: false, action: 'none' });
183
- };
184
-
185
- const handleDisableSubtitles = () => {
186
- setSelectedSubtitleTrack({
187
- type: 'index',
188
- value: 'Off',
189
- isExternal: false,
190
- });
191
- setActiveSubtitle(null);
192
- setIsPaused(false);
193
- setSettingsModal({ isVisible: false, action: 'none' });
194
- };
195
-
196
- return (
197
- <View style={styles.container}>
198
- <View style={styles.section}>
199
- {audioTracks.length > 0 && (
200
- <>
201
- <Text style={[styles.sectionTitle, { color: colors.text }]}>
202
- Audio
203
- </Text>
204
- <FlatList
205
- data={audioTracks}
206
- keyExtractor={({ index }) => `audio-${index}`}
207
- ItemSeparatorComponent={Separator}
208
- renderItem={({ item }) => (
209
- <AudioItem
210
- item={item}
211
- isSelected={selectedAudioTrack?.value === item.index}
212
- onPress={() => handleSelectAudio(item)}
213
- colors={colors}
214
- />
215
- )}
216
- />
217
- </>
218
- )}
219
- </View>
220
-
221
- <View style={styles.section}>
222
- <Text style={[styles.sectionTitle, { color: colors.text }]}>
223
- Subtitles
224
- </Text>
225
- <TouchableOpacity onPress={handleDisableSubtitles}>
226
- <Text
227
- style={[
228
- styles.listItem,
229
- { color: colors.text },
230
- (!activeSubtitle && selectedSubtitleTrack?.value === 'Off') ||
231
- selectedSubtitleTrack === null
232
- ? styles.selected
233
- : null,
234
- ]}
235
- >
236
- Off
237
- </Text>
238
- </TouchableOpacity>
239
- <Separator />
240
- <FlatList
241
- data={subtitleTracks}
242
- keyExtractor={({ index }) => `sub-${index}`}
243
- ItemSeparatorComponent={Separator}
244
- renderItem={({ item }) => (
245
- <SubtitleItem
246
- item={item}
247
- isSelected={
248
- selectedSubtitleTrack?.value === item.index ||
249
- activeSubtitle?.language === item.language
250
- }
251
- onPress={() => handleSelectSubtitle(item)}
252
- colors={{ text: colors.text }}
253
- />
254
- )}
255
- />
256
- </View>
257
- </View>
258
- );
259
- };
260
-
261
- export default AudioAndSubtitles;
262
-
263
- const styles = StyleSheet.create({
264
- container: {
265
- flex: 1,
266
- flexDirection: 'row',
267
- padding: scale(8),
268
- gap: scale(12),
269
- },
270
- section: {
271
- flex: 1,
272
- borderRadius: scale(12),
273
- padding: scale(8),
274
- },
275
- sectionTitle: {
276
- fontSize: RFValue(20),
277
- fontWeight: '600',
278
- marginBottom: verticalScale(8),
279
- letterSpacing: 0.6,
280
- },
281
- listItem: {
282
- fontSize: RFValue(17),
283
- paddingVertical: verticalScale(4),
284
- paddingHorizontal: scale(12),
285
- borderRadius: scale(8),
286
- opacity: 0.7,
287
- },
288
- selected: {
289
- opacity: 1,
290
- fontWeight: '700',
291
- },
292
- separator: {
293
- height: verticalScale(8),
294
- },
295
- });
@@ -1,297 +0,0 @@
1
- /* eslint-disable react/no-unstable-nested-components */
2
- import {
3
- FlatList,
4
- TouchableOpacity,
5
- View,
6
- StyleSheet,
7
- Text,
8
- } from 'react-native';
9
- import React from 'react';
10
- import FastImage from 'react-native-fast-image';
11
- import { moderateScale, verticalScale } from 'react-native-size-matters';
12
- import {
13
- MenuProvider,
14
- Menu,
15
- MenuOptions,
16
- MenuOption,
17
- MenuTrigger,
18
- } from 'react-native-popup-menu';
19
- import { RFValue } from 'react-native-responsive-fontsize';
20
- import { formatDuration, getEpisodeIndex } from '../utils';
21
- import { useVideoPlayerStore } from '../store/videoPlayerStore';
22
- import { useSafeAreaInsets } from 'react-native-safe-area-context';
23
- import globalStyles from '../styles/globalStyles';
24
- import type { MediaEpisode } from '../store';
25
- import { useVideoPlayerConfig } from '../context';
26
- import type { ExtendedWatchProgress } from '../utils';
27
-
28
- type EpisodesProps = {
29
- onPressEpisode: ({ episode }: { episode: MediaEpisode }) => Promise<boolean>;
30
- reportProgress: (event: ExtendedWatchProgress['event']) => void;
31
- };
32
-
33
- const Episodes: React.FC<EpisodesProps> = ({
34
- onPressEpisode,
35
- reportProgress,
36
- }) => {
37
- const {
38
- activeSeason,
39
- activeTrack,
40
- setCurrentTrackIndex,
41
- setActiveTrack,
42
- playList,
43
- setSettingsModal,
44
- setIsPaused,
45
- contentSeasons,
46
- setActiveSeason,
47
- setSelectedSubtitleTrack,
48
- setPlayBackRate,
49
- setActiveSubtitle,
50
- setSelectedVideoTrack,
51
- setMaxBitRate,
52
- setDuration,
53
- } = useVideoPlayerStore();
54
- const { colors } = useVideoPlayerConfig();
55
- const { left } = useSafeAreaInsets();
56
-
57
- if (!activeSeason?.seasonNumber || !activeSeason?.episodes) {
58
- return (
59
- <View style={globalStyles.flexOneJustifyContentCenterAndAlignItemsCenter}>
60
- <Text style={{ color: colors.text }}>
61
- Something went wrong. Please try again.
62
- </Text>
63
- </View>
64
- );
65
- }
66
-
67
- const handleEpisodePress = async (episode: MediaEpisode) => {
68
- const isSuccess = await onPressEpisode({ episode });
69
- if (!isSuccess) return;
70
- if (episode.id === activeTrack?.episodeId) {
71
- setSettingsModal({ isVisible: false, action: 'none' });
72
- setIsPaused(false);
73
- return;
74
- }
75
- const index = getEpisodeIndex(playList, episode.id);
76
- setCurrentTrackIndex(index);
77
- setDuration(0);
78
-
79
- setActiveTrack({
80
- type: 'series',
81
- episodeId: episode.id,
82
- contentId: episode.contentId,
83
- episodeName: episode.title,
84
- episodeNumber: episode.episodeNumber,
85
- seasonNumber: activeSeason.seasonNumber ?? 0,
86
- skipIntro: episode.skipIntro ?? undefined,
87
- nextEpisodeAt: episode.nextEpisodeAt ?? undefined,
88
- sourceLink: episode.sourceLink || '',
89
- thumbnail: episode.thumbnail,
90
- subtitles: episode.subtitles || null,
91
- id: episode.id,
92
- title: episode.title,
93
- description: episode.description || '',
94
- duration: episode.duration || 0,
95
- sourceType: episode.sourceType || 'HLS',
96
- });
97
- setSelectedSubtitleTrack(null);
98
- setActiveSubtitle(null);
99
- setSelectedVideoTrack(null);
100
- setMaxBitRate(null);
101
- setSettingsModal({ isVisible: false, action: 'none' });
102
- setIsPaused(false);
103
- setPlayBackRate(1, 'Normal');
104
- };
105
-
106
- return (
107
- <MenuProvider>
108
- <View>
109
- <Menu>
110
- <MenuTrigger
111
- style={[
112
- styles.menuTrigger,
113
- { marginLeft: left + moderateScale(8) },
114
- ]}
115
- customStyles={{
116
- triggerText: {
117
- ...menuTriggerCustomStyles.triggerText,
118
- color: colors.text,
119
- },
120
- }}
121
- text={`Season ${activeSeason?.seasonNumber}`}
122
- />
123
- <MenuOptions optionsContainerStyle={styles.menuOptionsContainer}>
124
- {contentSeasons?.map((season, index) => (
125
- <MenuOption
126
- style={styles.menuOption}
127
- customStyles={menuOptionCustomStyles}
128
- key={index}
129
- onSelect={() => setActiveSeason(season)}
130
- text={`Season ${season.seasonNumber}`}
131
- />
132
- ))}
133
- </MenuOptions>
134
- </Menu>
135
-
136
- <View style={styles.episodesContainer}>
137
- <FlatList
138
- data={activeSeason?.episodes}
139
- horizontal
140
- showsHorizontalScrollIndicator={false}
141
- ItemSeparatorComponent={() => (
142
- <View style={{ width: moderateScale(12) }} />
143
- )}
144
- ListHeaderComponent={() => (
145
- <View style={{ width: left + moderateScale(8) }} />
146
- )}
147
- ListFooterComponent={() => (
148
- <View style={{ width: moderateScale(16) }} />
149
- )}
150
- renderItem={({ item }) => (
151
- <TouchableOpacity
152
- onPress={() => {
153
- reportProgress('EPISODE_CHANGE');
154
- handleEpisodePress(item);
155
- }}
156
- style={styles.episodeItem}
157
- activeOpacity={0.8}
158
- >
159
- <FastImage
160
- source={{ uri: item?.thumbnail }}
161
- style={styles.episodeThumbnail}
162
- resizeMode={FastImage.resizeMode.cover}
163
- />
164
- {item.id === activeTrack?.episodeId && (
165
- <Text
166
- style={[
167
- styles.nowPlayingBadge,
168
- { color: colors.text, backgroundColor: colors.primary },
169
- ]}
170
- >
171
- Now Playing
172
- </Text>
173
- )}
174
-
175
- {/* Episode title */}
176
- <Text
177
- numberOfLines={1}
178
- style={[styles.episodeTitle, { color: colors.text }]}
179
- >
180
- {item?.title}
181
- </Text>
182
-
183
- {/* Season & Episode Number + Duration in one row */}
184
- <View style={styles.episodeInfoRow}>
185
- <Text style={styles.episodeInfoText}>
186
- {[
187
- item.episodeNumber &&
188
- `S${String(activeSeason?.seasonNumber).padStart(
189
- 2,
190
- '0'
191
- )} E${String(item.episodeNumber).padStart(2, '0')}`,
192
- item.publishDate && `(${item.publishDate})`,
193
- item?.duration && formatDuration(item?.duration, true),
194
- ]
195
- .filter(Boolean)
196
- .join(' | ')}
197
- </Text>
198
- </View>
199
- {/* Description */}
200
- {item.description && (
201
- <Text style={styles.episodeDescription} numberOfLines={3}>
202
- {item?.description}
203
- </Text>
204
- )}
205
- </TouchableOpacity>
206
- )}
207
- keyExtractor={(item) => item?.id}
208
- />
209
- </View>
210
- </View>
211
- </MenuProvider>
212
- );
213
- };
214
-
215
- const styles = StyleSheet.create({
216
- menuTrigger: {
217
- alignSelf: 'flex-start',
218
- backgroundColor: 'gray',
219
- paddingHorizontal: moderateScale(16),
220
- paddingVertical: moderateScale(8),
221
- marginTop: verticalScale(20),
222
- borderRadius: moderateScale(8),
223
- },
224
- menuOptionsContainer: {
225
- marginTop: verticalScale(60),
226
- width: moderateScale(150),
227
- },
228
- menuOption: {
229
- paddingHorizontal: moderateScale(16),
230
- paddingVertical: moderateScale(12),
231
- },
232
- episodesContainer: {
233
- marginTop: verticalScale(20),
234
- paddingBottom: verticalScale(20),
235
- },
236
- episodeItem: {
237
- width: moderateScale(240),
238
- backgroundColor: '#373737ff',
239
- borderRadius: moderateScale(12),
240
- overflow: 'hidden',
241
- },
242
- episodeThumbnail: {
243
- width: '100%',
244
- height: undefined,
245
- aspectRatio: 16 / 9,
246
- backgroundColor: '#444', // slightly lighter fallback
247
- },
248
- nowPlayingBadge: {
249
- position: 'absolute',
250
- top: moderateScale(6),
251
- left: moderateScale(6),
252
- paddingHorizontal: moderateScale(8),
253
- paddingVertical: moderateScale(4), // slightly bigger
254
- borderRadius: moderateScale(4),
255
- fontSize: RFValue(12),
256
- fontWeight: '600',
257
- overflow: 'hidden',
258
- },
259
- episodeTitle: {
260
- fontSize: RFValue(14), // bigger
261
- fontWeight: '600', // bolder
262
- marginHorizontal: moderateScale(8),
263
- marginTop: verticalScale(3),
264
- },
265
- episodeInfoRow: {
266
- flexDirection: 'row',
267
- justifyContent: 'flex-start', // better alignment
268
- marginHorizontal: moderateScale(8),
269
- marginTop: verticalScale(2),
270
- alignItems: 'center',
271
- },
272
- episodeInfoText: {
273
- color: '#f1f1f1ff',
274
- fontSize: RFValue(10.5),
275
- },
276
- episodeDescription: {
277
- marginHorizontal: moderateScale(8),
278
- marginVertical: verticalScale(5),
279
- color: '#f1f1f1ff',
280
- fontSize: RFValue(10),
281
- lineHeight: RFValue(10),
282
- },
283
- });
284
-
285
- const menuTriggerCustomStyles = {
286
- triggerText: {
287
- fontSize: RFValue(16),
288
- },
289
- };
290
-
291
- const menuOptionCustomStyles = {
292
- optionText: {
293
- fontSize: RFValue(16),
294
- },
295
- };
296
-
297
- export default Episodes;
@@ -1,127 +0,0 @@
1
- import { StyleSheet, TouchableOpacity, View } from 'react-native';
2
- import React from 'react';
3
- import { moderateScale } from 'react-native-size-matters';
4
- import { X } from 'lucide-react-native';
5
- import { useVideoPlayerStore } from '../store/videoPlayerStore';
6
-
7
- import Episodes from './Episodes';
8
- import SpeedControls from './SpeedControls';
9
- import VideoPlayerSettings from './VideoPlayerSettings';
10
- import AudioAndSubtitles from './AudioAndSubtitles';
11
- import {
12
- useVideoResolutions,
13
- type Resolution,
14
- } from '../utils/hooks/useVideoResolutions';
15
- import type { MediaEpisode, SettingsAction } from '../store/videoPlayer.type';
16
- import { useVideoPlayerConfig } from '../context';
17
- import type { ExtendedWatchProgress } from '../utils';
18
-
19
- const VisibleAction: React.FC<{
20
- settingModalAction: SettingsAction;
21
- onPressEpisode: ({ episode }: { episode: MediaEpisode }) => Promise<boolean>;
22
- reportProgress: (event: ExtendedWatchProgress['event']) => void;
23
- resolutions?: Resolution[];
24
- }> = ({ settingModalAction, onPressEpisode, reportProgress, resolutions }) => {
25
- switch (settingModalAction) {
26
- case 'speed':
27
- return <SpeedControls />;
28
- case 'audioOrSubtitle':
29
- return <AudioAndSubtitles />;
30
- case 'settings':
31
- return <VideoPlayerSettings resolutions={resolutions || []} />;
32
- case 'episodes':
33
- return (
34
- <Episodes
35
- onPressEpisode={onPressEpisode}
36
- reportProgress={reportProgress}
37
- />
38
- );
39
- default:
40
- return null;
41
- }
42
- };
43
-
44
- type SettingModalProps = {
45
- onPressEpisode: ({ episode }: { episode: MediaEpisode }) => Promise<boolean>;
46
- reportProgress: (event: ExtendedWatchProgress['event']) => void;
47
- };
48
-
49
- const SettingModal: React.FC<SettingModalProps> = ({
50
- onPressEpisode,
51
- reportProgress,
52
- }) => {
53
- const {
54
- settingsModal,
55
- setSettingsModal,
56
- setIsPaused,
57
- setControlsVisible,
58
- activeTrack,
59
- } = useVideoPlayerStore();
60
- const { colors } = useVideoPlayerConfig();
61
- const resolutions = useVideoResolutions(activeTrack);
62
-
63
- const onClose = () => {
64
- setSettingsModal({ isVisible: false, action: 'none' });
65
- setIsPaused(false);
66
- setControlsVisible(true);
67
- };
68
-
69
- if (!settingsModal.isVisible) return null;
70
-
71
- const containerStyle =
72
- settingsModal.action === 'audioOrSubtitle'
73
- ? [styles.contentContainer, styles.audioOrSubtitlePadding]
74
- : styles.contentContainer;
75
-
76
- return (
77
- <View style={StyleSheet.absoluteFill}>
78
- {/* Dark overlay */}
79
- <View style={styles.overlay} />
80
-
81
- {/* Close button */}
82
- <TouchableOpacity onPress={onClose} style={styles.closeButton}>
83
- <X color={colors.text} size={moderateScale(25)} />
84
- </TouchableOpacity>
85
-
86
- {/* Modal content */}
87
- <View style={containerStyle}>
88
- <VisibleAction
89
- settingModalAction={settingsModal.action}
90
- resolutions={resolutions}
91
- onPressEpisode={onPressEpisode}
92
- reportProgress={reportProgress}
93
- />
94
- </View>
95
- </View>
96
- );
97
- };
98
-
99
- export default SettingModal;
100
-
101
- const styles = StyleSheet.create({
102
- overlay: {
103
- ...StyleSheet.absoluteFillObject,
104
- backgroundColor: 'rgba(0,0,0,0.9)',
105
- pointerEvents: 'none',
106
- },
107
- closeButton: {
108
- position: 'absolute',
109
- top: moderateScale(20),
110
- right: moderateScale(60),
111
- width: moderateScale(50),
112
- height: moderateScale(50),
113
- justifyContent: 'center',
114
- alignItems: 'center',
115
- borderRadius: moderateScale(50),
116
- zIndex: 10000,
117
- },
118
- contentContainer: {
119
- flex: 1,
120
- zIndex: 1000,
121
- },
122
- audioOrSubtitlePadding: {
123
- paddingLeft: moderateScale(16),
124
- paddingRight: moderateScale(16),
125
- paddingTop: moderateScale(8),
126
- },
127
- });