@zezosoft/zezo-ott-react-native-video-player 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +308 -0
- package/lib/module/VideoPlayer/MediaControls/BottomControls.js +156 -0
- package/lib/module/VideoPlayer/MediaControls/BottomControls.js.map +1 -0
- package/lib/module/VideoPlayer/MediaControls/MediaControls.js +27 -0
- package/lib/module/VideoPlayer/MediaControls/MediaControls.js.map +1 -0
- package/lib/module/VideoPlayer/MediaControls/MediaControlsProvider.js +85 -0
- package/lib/module/VideoPlayer/MediaControls/MediaControlsProvider.js.map +1 -0
- package/lib/module/VideoPlayer/MediaControls/MiddleControls.js +208 -0
- package/lib/module/VideoPlayer/MediaControls/MiddleControls.js.map +1 -0
- package/lib/module/VideoPlayer/MediaControls/TopControls.js +94 -0
- package/lib/module/VideoPlayer/MediaControls/TopControls.js.map +1 -0
- package/lib/module/VideoPlayer/Styles/fonts.js +58 -0
- package/lib/module/VideoPlayer/Styles/fonts.js.map +1 -0
- package/lib/module/VideoPlayer/Styles/globalStyles.js +75 -0
- package/lib/module/VideoPlayer/Styles/globalStyles.js.map +1 -0
- package/lib/module/VideoPlayer/VideoPlayer.js +180 -0
- package/lib/module/VideoPlayer/VideoPlayer.js.map +1 -0
- package/lib/module/VideoPlayer/components/ProgressBar.js +84 -0
- package/lib/module/VideoPlayer/components/ProgressBar.js.map +1 -0
- package/lib/module/VideoPlayer/components/SkipAndNextControls.js +154 -0
- package/lib/module/VideoPlayer/components/SkipAndNextControls.js.map +1 -0
- package/lib/module/VideoPlayer/components/SubtitleView.js +57 -0
- package/lib/module/VideoPlayer/components/SubtitleView.js.map +1 -0
- package/lib/module/VideoPlayer/context/VideoPlayerConfig.js +47 -0
- package/lib/module/VideoPlayer/context/VideoPlayerConfig.js.map +1 -0
- package/lib/module/VideoPlayer/context/index.js +4 -0
- package/lib/module/VideoPlayer/context/index.js.map +1 -0
- package/lib/module/VideoPlayer/index.js +7 -0
- package/lib/module/VideoPlayer/index.js.map +1 -0
- package/lib/module/VideoPlayer/model/AudioAndSubtitles.js +257 -0
- package/lib/module/VideoPlayer/model/AudioAndSubtitles.js.map +1 -0
- package/lib/module/VideoPlayer/model/Episodes.js +272 -0
- package/lib/module/VideoPlayer/model/Episodes.js.map +1 -0
- package/lib/module/VideoPlayer/model/SettingModal.js +115 -0
- package/lib/module/VideoPlayer/model/SettingModal.js.map +1 -0
- package/lib/module/VideoPlayer/model/SpeedControls.js +140 -0
- package/lib/module/VideoPlayer/model/SpeedControls.js.map +1 -0
- package/lib/module/VideoPlayer/model/VideoPlayerSettings.js +113 -0
- package/lib/module/VideoPlayer/model/VideoPlayerSettings.js.map +1 -0
- package/lib/module/VideoPlayer/store/index.js +5 -0
- package/lib/module/VideoPlayer/store/index.js.map +1 -0
- package/lib/module/VideoPlayer/store/videoPlayer.type.js +4 -0
- package/lib/module/VideoPlayer/store/videoPlayer.type.js.map +1 -0
- package/lib/module/VideoPlayer/store/videoPlayerStore.js +147 -0
- package/lib/module/VideoPlayer/store/videoPlayerStore.js.map +1 -0
- package/lib/module/VideoPlayer/utils/Display.js +22 -0
- package/lib/module/VideoPlayer/utils/Display.js.map +1 -0
- package/lib/module/VideoPlayer/utils/PlatformSelector.js +18 -0
- package/lib/module/VideoPlayer/utils/PlatformSelector.js.map +1 -0
- package/lib/module/VideoPlayer/utils/hooks/index.js +5 -0
- package/lib/module/VideoPlayer/utils/hooks/index.js.map +1 -0
- package/lib/module/VideoPlayer/utils/hooks/useVideoPlayerBack.js +48 -0
- package/lib/module/VideoPlayer/utils/hooks/useVideoPlayerBack.js.map +1 -0
- package/lib/module/VideoPlayer/utils/hooks/useVideoResolutions.js +88 -0
- package/lib/module/VideoPlayer/utils/hooks/useVideoResolutions.js.map +1 -0
- package/lib/module/VideoPlayer/utils/index.js +10 -0
- package/lib/module/VideoPlayer/utils/index.js.map +1 -0
- package/lib/module/VideoPlayer/utils/lockOrientation.js +31 -0
- package/lib/module/VideoPlayer/utils/lockOrientation.js.map +1 -0
- package/lib/module/VideoPlayer/utils/playerEvents.js +79 -0
- package/lib/module/VideoPlayer/utils/playerEvents.js.map +1 -0
- package/lib/module/VideoPlayer/utils/timeFormatter.js +35 -0
- package/lib/module/VideoPlayer/utils/timeFormatter.js.map +1 -0
- package/lib/module/VideoPlayer/utils/useWatchReporter.js +68 -0
- package/lib/module/VideoPlayer/utils/useWatchReporter.js.map +1 -0
- package/lib/module/VideoPlayer/utils/videoControl.js +166 -0
- package/lib/module/VideoPlayer/utils/videoControl.js.map +1 -0
- package/lib/module/VideoPlayer/utils/videoRef.js +5 -0
- package/lib/module/VideoPlayer/utils/videoRef.js.map +1 -0
- package/lib/module/VideoPlayer/utils/videoSource.js +17 -0
- package/lib/module/VideoPlayer/utils/videoSource.js.map +1 -0
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/VideoPlayer/MediaControls/BottomControls.d.ts +3 -0
- package/lib/typescript/src/VideoPlayer/MediaControls/BottomControls.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/MediaControls/MediaControls.d.ts +6 -0
- package/lib/typescript/src/VideoPlayer/MediaControls/MediaControls.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/MediaControls/MediaControlsProvider.d.ts +14 -0
- package/lib/typescript/src/VideoPlayer/MediaControls/MediaControlsProvider.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/MediaControls/MiddleControls.d.ts +3 -0
- package/lib/typescript/src/VideoPlayer/MediaControls/MiddleControls.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/MediaControls/TopControls.d.ts +7 -0
- package/lib/typescript/src/VideoPlayer/MediaControls/TopControls.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/Styles/fonts.d.ts +54 -0
- package/lib/typescript/src/VideoPlayer/Styles/fonts.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/Styles/globalStyles.d.ts +70 -0
- package/lib/typescript/src/VideoPlayer/Styles/globalStyles.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/VideoPlayer.d.ts +22 -0
- package/lib/typescript/src/VideoPlayer/VideoPlayer.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/components/ProgressBar.d.ts +12 -0
- package/lib/typescript/src/VideoPlayer/components/ProgressBar.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/components/SkipAndNextControls.d.ts +12 -0
- package/lib/typescript/src/VideoPlayer/components/SkipAndNextControls.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/components/SubtitleView.d.ts +3 -0
- package/lib/typescript/src/VideoPlayer/components/SubtitleView.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/context/VideoPlayerConfig.d.ts +25 -0
- package/lib/typescript/src/VideoPlayer/context/VideoPlayerConfig.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/context/index.d.ts +3 -0
- package/lib/typescript/src/VideoPlayer/context/index.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/index.d.ts +5 -0
- package/lib/typescript/src/VideoPlayer/index.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/model/AudioAndSubtitles.d.ts +4 -0
- package/lib/typescript/src/VideoPlayer/model/AudioAndSubtitles.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/model/Episodes.d.ts +12 -0
- package/lib/typescript/src/VideoPlayer/model/Episodes.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/model/SettingModal.d.ts +12 -0
- package/lib/typescript/src/VideoPlayer/model/SettingModal.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/model/SpeedControls.d.ts +4 -0
- package/lib/typescript/src/VideoPlayer/model/SpeedControls.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/model/VideoPlayerSettings.d.ts +7 -0
- package/lib/typescript/src/VideoPlayer/model/VideoPlayerSettings.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/store/index.d.ts +3 -0
- package/lib/typescript/src/VideoPlayer/store/index.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/store/videoPlayer.type.d.ts +144 -0
- package/lib/typescript/src/VideoPlayer/store/videoPlayer.type.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/store/videoPlayerStore.d.ts +20 -0
- package/lib/typescript/src/VideoPlayer/store/videoPlayerStore.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/utils/Display.d.ts +8 -0
- package/lib/typescript/src/VideoPlayer/utils/Display.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/utils/PlatformSelector.d.ts +11 -0
- package/lib/typescript/src/VideoPlayer/utils/PlatformSelector.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/utils/hooks/index.d.ts +3 -0
- package/lib/typescript/src/VideoPlayer/utils/hooks/index.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/utils/hooks/useVideoPlayerBack.d.ts +6 -0
- package/lib/typescript/src/VideoPlayer/utils/hooks/useVideoPlayerBack.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/utils/hooks/useVideoResolutions.d.ts +12 -0
- package/lib/typescript/src/VideoPlayer/utils/hooks/useVideoResolutions.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/utils/index.d.ts +8 -0
- package/lib/typescript/src/VideoPlayer/utils/index.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/utils/lockOrientation.d.ts +3 -0
- package/lib/typescript/src/VideoPlayer/utils/lockOrientation.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/utils/playerEvents.d.ts +18 -0
- package/lib/typescript/src/VideoPlayer/utils/playerEvents.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/utils/timeFormatter.d.ts +9 -0
- package/lib/typescript/src/VideoPlayer/utils/timeFormatter.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/utils/useWatchReporter.d.ts +26 -0
- package/lib/typescript/src/VideoPlayer/utils/useWatchReporter.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/utils/videoControl.d.ts +17 -0
- package/lib/typescript/src/VideoPlayer/utils/videoControl.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/utils/videoRef.d.ts +3 -0
- package/lib/typescript/src/VideoPlayer/utils/videoRef.d.ts.map +1 -0
- package/lib/typescript/src/VideoPlayer/utils/videoSource.d.ts +10 -0
- package/lib/typescript/src/VideoPlayer/utils/videoSource.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +2 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/package.json +191 -0
- package/src/VideoPlayer/MediaControls/BottomControls.tsx +185 -0
- package/src/VideoPlayer/MediaControls/MediaControls.tsx +29 -0
- package/src/VideoPlayer/MediaControls/MediaControlsProvider.tsx +99 -0
- package/src/VideoPlayer/MediaControls/MiddleControls.tsx +232 -0
- package/src/VideoPlayer/MediaControls/TopControls.tsx +92 -0
- package/src/VideoPlayer/Styles/fonts.ts +106 -0
- package/src/VideoPlayer/Styles/globalStyles.ts +74 -0
- package/src/VideoPlayer/VideoPlayer.tsx +217 -0
- package/src/VideoPlayer/components/ProgressBar.tsx +98 -0
- package/src/VideoPlayer/components/SkipAndNextControls.tsx +195 -0
- package/src/VideoPlayer/components/SubtitleView.tsx +53 -0
- package/src/VideoPlayer/context/VideoPlayerConfig.tsx +65 -0
- package/src/VideoPlayer/context/index.ts +5 -0
- package/src/VideoPlayer/index.ts +4 -0
- package/src/VideoPlayer/model/AudioAndSubtitles.tsx +302 -0
- package/src/VideoPlayer/model/Episodes.tsx +294 -0
- package/src/VideoPlayer/model/SettingModal.tsx +128 -0
- package/src/VideoPlayer/model/SpeedControls.tsx +134 -0
- package/src/VideoPlayer/model/VideoPlayerSettings.tsx +141 -0
- package/src/VideoPlayer/store/index.ts +2 -0
- package/src/VideoPlayer/store/videoPlayer.type.ts +192 -0
- package/src/VideoPlayer/store/videoPlayerStore.ts +101 -0
- package/src/VideoPlayer/utils/Display.ts +14 -0
- package/src/VideoPlayer/utils/PlatformSelector.ts +18 -0
- package/src/VideoPlayer/utils/hooks/index.ts +2 -0
- package/src/VideoPlayer/utils/hooks/useVideoPlayerBack.ts +66 -0
- package/src/VideoPlayer/utils/hooks/useVideoResolutions.ts +119 -0
- package/src/VideoPlayer/utils/index.ts +7 -0
- package/src/VideoPlayer/utils/lockOrientation.ts +34 -0
- package/src/VideoPlayer/utils/playerEvents.ts +97 -0
- package/src/VideoPlayer/utils/timeFormatter.ts +47 -0
- package/src/VideoPlayer/utils/useWatchReporter.ts +104 -0
- package/src/VideoPlayer/utils/videoControl.ts +192 -0
- package/src/VideoPlayer/utils/videoRef.ts +4 -0
- package/src/VideoPlayer/utils/videoSource.ts +23 -0
- package/src/index.tsx +1 -0
|
@@ -0,0 +1,294 @@
|
|
|
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/useWatchReporter';
|
|
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(2, '0')} E${String(item.episodeNumber).padStart(2, '0')}`,
|
|
189
|
+
item.publishDate && `(${item.publishDate})`,
|
|
190
|
+
item?.duration && formatDuration(item?.duration, true),
|
|
191
|
+
]
|
|
192
|
+
.filter(Boolean)
|
|
193
|
+
.join(' | ')}
|
|
194
|
+
</Text>
|
|
195
|
+
</View>
|
|
196
|
+
{/* Description */}
|
|
197
|
+
{item.description && (
|
|
198
|
+
<Text style={styles.episodeDescription} numberOfLines={3}>
|
|
199
|
+
{item?.description}
|
|
200
|
+
</Text>
|
|
201
|
+
)}
|
|
202
|
+
</TouchableOpacity>
|
|
203
|
+
)}
|
|
204
|
+
keyExtractor={(item) => item?.id}
|
|
205
|
+
/>
|
|
206
|
+
</View>
|
|
207
|
+
</View>
|
|
208
|
+
</MenuProvider>
|
|
209
|
+
);
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const styles = StyleSheet.create({
|
|
213
|
+
menuTrigger: {
|
|
214
|
+
alignSelf: 'flex-start',
|
|
215
|
+
backgroundColor: 'gray',
|
|
216
|
+
paddingHorizontal: moderateScale(16),
|
|
217
|
+
paddingVertical: moderateScale(8),
|
|
218
|
+
marginTop: verticalScale(20),
|
|
219
|
+
borderRadius: moderateScale(8),
|
|
220
|
+
},
|
|
221
|
+
menuOptionsContainer: {
|
|
222
|
+
marginTop: verticalScale(60),
|
|
223
|
+
width: moderateScale(150),
|
|
224
|
+
},
|
|
225
|
+
menuOption: {
|
|
226
|
+
paddingHorizontal: moderateScale(16),
|
|
227
|
+
paddingVertical: moderateScale(12),
|
|
228
|
+
},
|
|
229
|
+
episodesContainer: {
|
|
230
|
+
marginTop: verticalScale(20),
|
|
231
|
+
paddingBottom: verticalScale(20),
|
|
232
|
+
},
|
|
233
|
+
episodeItem: {
|
|
234
|
+
width: moderateScale(240),
|
|
235
|
+
backgroundColor: '#373737ff',
|
|
236
|
+
borderRadius: moderateScale(12),
|
|
237
|
+
overflow: 'hidden',
|
|
238
|
+
},
|
|
239
|
+
episodeThumbnail: {
|
|
240
|
+
width: '100%',
|
|
241
|
+
height: undefined,
|
|
242
|
+
aspectRatio: 16 / 9,
|
|
243
|
+
backgroundColor: '#444', // slightly lighter fallback
|
|
244
|
+
},
|
|
245
|
+
nowPlayingBadge: {
|
|
246
|
+
position: 'absolute',
|
|
247
|
+
top: moderateScale(6),
|
|
248
|
+
left: moderateScale(6),
|
|
249
|
+
paddingHorizontal: moderateScale(8),
|
|
250
|
+
paddingVertical: moderateScale(4), // slightly bigger
|
|
251
|
+
borderRadius: moderateScale(4),
|
|
252
|
+
fontSize: RFValue(12),
|
|
253
|
+
fontWeight: '600',
|
|
254
|
+
overflow: 'hidden',
|
|
255
|
+
},
|
|
256
|
+
episodeTitle: {
|
|
257
|
+
fontSize: RFValue(14), // bigger
|
|
258
|
+
fontWeight: '600', // bolder
|
|
259
|
+
marginHorizontal: moderateScale(8),
|
|
260
|
+
marginTop: verticalScale(3),
|
|
261
|
+
},
|
|
262
|
+
episodeInfoRow: {
|
|
263
|
+
flexDirection: 'row',
|
|
264
|
+
justifyContent: 'flex-start', // better alignment
|
|
265
|
+
marginHorizontal: moderateScale(8),
|
|
266
|
+
marginTop: verticalScale(2),
|
|
267
|
+
alignItems: 'center',
|
|
268
|
+
},
|
|
269
|
+
episodeInfoText: {
|
|
270
|
+
color: '#f1f1f1ff',
|
|
271
|
+
fontSize: RFValue(10.5),
|
|
272
|
+
},
|
|
273
|
+
episodeDescription: {
|
|
274
|
+
marginHorizontal: moderateScale(8),
|
|
275
|
+
marginVertical: verticalScale(5),
|
|
276
|
+
color: '#f1f1f1ff',
|
|
277
|
+
fontSize: RFValue(10),
|
|
278
|
+
lineHeight: RFValue(10),
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
const menuTriggerCustomStyles = {
|
|
283
|
+
triggerText: {
|
|
284
|
+
fontSize: RFValue(16),
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const menuOptionCustomStyles = {
|
|
289
|
+
optionText: {
|
|
290
|
+
fontSize: RFValue(16),
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
export default Episodes;
|
|
@@ -0,0 +1,128 @@
|
|
|
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/useWatchReporter';
|
|
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
|
+
// Fetch available resolutions for current track
|
|
62
|
+
const resolutions = useVideoResolutions(activeTrack);
|
|
63
|
+
|
|
64
|
+
const onClose = () => {
|
|
65
|
+
setSettingsModal({ isVisible: false, action: 'none' });
|
|
66
|
+
setIsPaused(false);
|
|
67
|
+
setControlsVisible(true);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
if (!settingsModal.isVisible) return null;
|
|
71
|
+
|
|
72
|
+
const containerStyle =
|
|
73
|
+
settingsModal.action === 'audioOrSubtitle'
|
|
74
|
+
? [styles.contentContainer, styles.audioOrSubtitlePadding]
|
|
75
|
+
: styles.contentContainer;
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<View style={StyleSheet.absoluteFill}>
|
|
79
|
+
{/* Dark overlay */}
|
|
80
|
+
<View style={styles.overlay} />
|
|
81
|
+
|
|
82
|
+
{/* Close button */}
|
|
83
|
+
<TouchableOpacity onPress={onClose} style={styles.closeButton}>
|
|
84
|
+
<X color={colors.text} size={moderateScale(25)} />
|
|
85
|
+
</TouchableOpacity>
|
|
86
|
+
|
|
87
|
+
{/* Modal content */}
|
|
88
|
+
<View style={containerStyle}>
|
|
89
|
+
<VisibleAction
|
|
90
|
+
settingModalAction={settingsModal.action}
|
|
91
|
+
resolutions={resolutions}
|
|
92
|
+
onPressEpisode={onPressEpisode}
|
|
93
|
+
reportProgress={reportProgress}
|
|
94
|
+
/>
|
|
95
|
+
</View>
|
|
96
|
+
</View>
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export default SettingModal;
|
|
101
|
+
|
|
102
|
+
const styles = StyleSheet.create({
|
|
103
|
+
overlay: {
|
|
104
|
+
...StyleSheet.absoluteFillObject,
|
|
105
|
+
backgroundColor: 'rgba(0,0,0,0.9)',
|
|
106
|
+
pointerEvents: 'none',
|
|
107
|
+
},
|
|
108
|
+
closeButton: {
|
|
109
|
+
position: 'absolute',
|
|
110
|
+
top: moderateScale(20),
|
|
111
|
+
right: moderateScale(60),
|
|
112
|
+
width: moderateScale(50),
|
|
113
|
+
height: moderateScale(50),
|
|
114
|
+
justifyContent: 'center',
|
|
115
|
+
alignItems: 'center',
|
|
116
|
+
borderRadius: moderateScale(50),
|
|
117
|
+
zIndex: 10000,
|
|
118
|
+
},
|
|
119
|
+
contentContainer: {
|
|
120
|
+
flex: 1,
|
|
121
|
+
zIndex: 1000,
|
|
122
|
+
},
|
|
123
|
+
audioOrSubtitlePadding: {
|
|
124
|
+
paddingLeft: moderateScale(16),
|
|
125
|
+
paddingRight: moderateScale(16),
|
|
126
|
+
paddingTop: moderateScale(8),
|
|
127
|
+
},
|
|
128
|
+
});
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
FlatList,
|
|
4
|
+
TouchableOpacity,
|
|
5
|
+
View,
|
|
6
|
+
StyleSheet,
|
|
7
|
+
Text,
|
|
8
|
+
} from 'react-native';
|
|
9
|
+
import { verticalScale } from 'react-native-size-matters';
|
|
10
|
+
import { RFValue } from 'react-native-responsive-fontsize';
|
|
11
|
+
|
|
12
|
+
import { useVideoPlayerStore } from '../store/videoPlayerStore';
|
|
13
|
+
import globalStyles from '../Styles/globalStyles';
|
|
14
|
+
import { useVideoPlayerConfig } from '../context';
|
|
15
|
+
|
|
16
|
+
// --- Constants ---
|
|
17
|
+
const SPEED_OPTIONS = [
|
|
18
|
+
{ name: '0.25x', value: 0.25 },
|
|
19
|
+
{ name: '0.5x', value: 0.5 },
|
|
20
|
+
{ name: '0.75x', value: 0.75 },
|
|
21
|
+
{ name: 'Normal', value: 1 },
|
|
22
|
+
{ name: '1.25x', value: 1.25 },
|
|
23
|
+
{ name: '1.5x', value: 1.5 },
|
|
24
|
+
{ name: '1.75x', value: 1.75 },
|
|
25
|
+
{ name: '2x', value: 2 },
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
// --- UI Components ---
|
|
29
|
+
const ListHeader = () => <View style={styles.listHeader} />;
|
|
30
|
+
const ListFooter = () => <View style={styles.listFooter} />;
|
|
31
|
+
|
|
32
|
+
interface SpeedItemProps {
|
|
33
|
+
name: string;
|
|
34
|
+
value: number;
|
|
35
|
+
isSelected: boolean;
|
|
36
|
+
onPress: ({ value, name }: { value: number; name: string }) => void;
|
|
37
|
+
colors: {
|
|
38
|
+
text: string;
|
|
39
|
+
background: string;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const SpeedItem: React.FC<SpeedItemProps> = ({
|
|
44
|
+
name,
|
|
45
|
+
value,
|
|
46
|
+
isSelected,
|
|
47
|
+
onPress,
|
|
48
|
+
colors,
|
|
49
|
+
}) => (
|
|
50
|
+
<TouchableOpacity
|
|
51
|
+
onPress={() => onPress({ value, name })}
|
|
52
|
+
style={styles.itemTouchable}
|
|
53
|
+
activeOpacity={0.7}
|
|
54
|
+
>
|
|
55
|
+
<Text
|
|
56
|
+
style={[
|
|
57
|
+
styles.itemText,
|
|
58
|
+
{ color: colors.text },
|
|
59
|
+
isSelected ? styles.itemTextSelected : styles.itemTextUnselected,
|
|
60
|
+
]}
|
|
61
|
+
>
|
|
62
|
+
{name}
|
|
63
|
+
</Text>
|
|
64
|
+
</TouchableOpacity>
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// --- Main Component ---
|
|
68
|
+
const SpeedControls: React.FC = () => {
|
|
69
|
+
const {
|
|
70
|
+
setIsPaused,
|
|
71
|
+
playBackRate,
|
|
72
|
+
setPlayBackRate,
|
|
73
|
+
setControlsVisible,
|
|
74
|
+
setSettingsModal,
|
|
75
|
+
} = useVideoPlayerStore();
|
|
76
|
+
const { colors } = useVideoPlayerConfig();
|
|
77
|
+
const speeds = useMemo(() => SPEED_OPTIONS, []);
|
|
78
|
+
|
|
79
|
+
const handleSelectSpeed = (value: number, name: string) => {
|
|
80
|
+
setPlayBackRate(value, name);
|
|
81
|
+
setSettingsModal({ action: 'none', isVisible: false });
|
|
82
|
+
setControlsVisible(true);
|
|
83
|
+
setIsPaused(false);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<View style={globalStyles.flexOneJustifyContentCenterAndAlignItemsCenter}>
|
|
88
|
+
<FlatList
|
|
89
|
+
data={speeds}
|
|
90
|
+
showsVerticalScrollIndicator={false}
|
|
91
|
+
keyExtractor={(item) => item.name}
|
|
92
|
+
ListHeaderComponent={ListHeader}
|
|
93
|
+
ListFooterComponent={ListFooter}
|
|
94
|
+
renderItem={({ item }) => (
|
|
95
|
+
<SpeedItem
|
|
96
|
+
name={item.name}
|
|
97
|
+
value={item.value}
|
|
98
|
+
isSelected={playBackRate === item.value}
|
|
99
|
+
onPress={({ value, name }) => handleSelectSpeed(value, name)}
|
|
100
|
+
colors={colors}
|
|
101
|
+
/>
|
|
102
|
+
)}
|
|
103
|
+
/>
|
|
104
|
+
</View>
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export default SpeedControls;
|
|
109
|
+
|
|
110
|
+
// --- Styles ---
|
|
111
|
+
const styles = StyleSheet.create({
|
|
112
|
+
listHeader: {
|
|
113
|
+
height: verticalScale(20),
|
|
114
|
+
},
|
|
115
|
+
listFooter: {
|
|
116
|
+
height: verticalScale(50),
|
|
117
|
+
},
|
|
118
|
+
itemTouchable: {
|
|
119
|
+
paddingVertical: verticalScale(12),
|
|
120
|
+
paddingHorizontal: verticalScale(30),
|
|
121
|
+
alignItems: 'center',
|
|
122
|
+
},
|
|
123
|
+
itemText: {
|
|
124
|
+
textAlign: 'center',
|
|
125
|
+
fontSize: RFValue(17), // better readability
|
|
126
|
+
fontWeight: '600', // semi-bold for better emphasis
|
|
127
|
+
},
|
|
128
|
+
itemTextSelected: {
|
|
129
|
+
opacity: 1,
|
|
130
|
+
},
|
|
131
|
+
itemTextUnselected: {
|
|
132
|
+
opacity: 0.6,
|
|
133
|
+
},
|
|
134
|
+
});
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/* eslint-disable react/no-unstable-nested-components */
|
|
2
|
+
import { useCallback } from 'react';
|
|
3
|
+
import {
|
|
4
|
+
FlatList,
|
|
5
|
+
TouchableOpacity,
|
|
6
|
+
View,
|
|
7
|
+
StyleSheet,
|
|
8
|
+
Text,
|
|
9
|
+
} from 'react-native';
|
|
10
|
+
import { scale, verticalScale } from 'react-native-size-matters';
|
|
11
|
+
import { RFValue } from 'react-native-responsive-fontsize';
|
|
12
|
+
import { SelectedVideoTrackType } from 'react-native-video';
|
|
13
|
+
import globalStyles from '../Styles/globalStyles';
|
|
14
|
+
import type { Resolution } from '../utils';
|
|
15
|
+
import { useVideoPlayerStore } from '../store';
|
|
16
|
+
import { useVideoPlayerConfig } from '../context';
|
|
17
|
+
|
|
18
|
+
interface VideoPlayerSettingsProps {
|
|
19
|
+
resolutions: Resolution[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const VideoPlayerSettings = ({ resolutions }: VideoPlayerSettingsProps) => {
|
|
23
|
+
const {
|
|
24
|
+
setMaxBitRate,
|
|
25
|
+
setSelectedVideoTrack,
|
|
26
|
+
setIsPaused,
|
|
27
|
+
setSettingsModal,
|
|
28
|
+
setControlsVisible,
|
|
29
|
+
maxBitRate,
|
|
30
|
+
} = useVideoPlayerStore();
|
|
31
|
+
const { colors } = useVideoPlayerConfig();
|
|
32
|
+
const handleSelect = useCallback(
|
|
33
|
+
(item: Resolution) => {
|
|
34
|
+
if (item.height === 'auto') {
|
|
35
|
+
setSelectedVideoTrack(null);
|
|
36
|
+
setMaxBitRate(null);
|
|
37
|
+
} else {
|
|
38
|
+
setSelectedVideoTrack({
|
|
39
|
+
type: SelectedVideoTrackType.RESOLUTION,
|
|
40
|
+
value: Number(item.height),
|
|
41
|
+
});
|
|
42
|
+
setMaxBitRate(item.bandwidth ?? null);
|
|
43
|
+
}
|
|
44
|
+
setIsPaused(false);
|
|
45
|
+
setSettingsModal({ isVisible: false, action: 'none' });
|
|
46
|
+
setControlsVisible(false);
|
|
47
|
+
},
|
|
48
|
+
[
|
|
49
|
+
setMaxBitRate,
|
|
50
|
+
setSelectedVideoTrack,
|
|
51
|
+
setIsPaused,
|
|
52
|
+
setSettingsModal,
|
|
53
|
+
setControlsVisible,
|
|
54
|
+
]
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const keyExtractor = useCallback(
|
|
58
|
+
(item: Resolution, index: number) => `${item.height}-${index}`,
|
|
59
|
+
[]
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const renderItem = useCallback(
|
|
63
|
+
({ item }: { item: Resolution }) => {
|
|
64
|
+
const isSelected = maxBitRate === item.bandwidth;
|
|
65
|
+
return (
|
|
66
|
+
<TouchableOpacity
|
|
67
|
+
onPress={() => handleSelect(item)}
|
|
68
|
+
activeOpacity={0.7}
|
|
69
|
+
>
|
|
70
|
+
<Text
|
|
71
|
+
style={[
|
|
72
|
+
styles.optionText,
|
|
73
|
+
{ color: colors.text },
|
|
74
|
+
isSelected
|
|
75
|
+
? styles.optionTextSelected
|
|
76
|
+
: styles.optionTextUnselected,
|
|
77
|
+
]}
|
|
78
|
+
>
|
|
79
|
+
{item.height === 'auto' ? 'Auto' : `${item.height}p`}
|
|
80
|
+
</Text>
|
|
81
|
+
</TouchableOpacity>
|
|
82
|
+
);
|
|
83
|
+
},
|
|
84
|
+
[colors.text, handleSelect, maxBitRate]
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<View style={[globalStyles.flexOne, styles.container]}>
|
|
89
|
+
<Text
|
|
90
|
+
style={[
|
|
91
|
+
styles.headerText,
|
|
92
|
+
{ color: colors.text },
|
|
93
|
+
styles.marginTopBottom,
|
|
94
|
+
]}
|
|
95
|
+
>
|
|
96
|
+
Video Quality
|
|
97
|
+
</Text>
|
|
98
|
+
<FlatList
|
|
99
|
+
data={resolutions}
|
|
100
|
+
renderItem={renderItem}
|
|
101
|
+
keyExtractor={keyExtractor}
|
|
102
|
+
showsVerticalScrollIndicator={false}
|
|
103
|
+
contentContainerStyle={{ paddingBottom: verticalScale(50) }}
|
|
104
|
+
ItemSeparatorComponent={() => (
|
|
105
|
+
<View style={{ height: verticalScale(12) }} />
|
|
106
|
+
)}
|
|
107
|
+
/>
|
|
108
|
+
</View>
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export default VideoPlayerSettings;
|
|
113
|
+
|
|
114
|
+
const styles = StyleSheet.create({
|
|
115
|
+
container: {
|
|
116
|
+
alignItems: 'center',
|
|
117
|
+
paddingHorizontal: 20,
|
|
118
|
+
},
|
|
119
|
+
headerText: {
|
|
120
|
+
fontSize: RFValue(20),
|
|
121
|
+
fontWeight: '700',
|
|
122
|
+
textAlign: 'center',
|
|
123
|
+
},
|
|
124
|
+
marginTopBottom: {
|
|
125
|
+
marginBottom: verticalScale(16),
|
|
126
|
+
paddingTop: verticalScale(24),
|
|
127
|
+
},
|
|
128
|
+
optionText: {
|
|
129
|
+
fontSize: RFValue(17),
|
|
130
|
+
paddingVertical: verticalScale(4),
|
|
131
|
+
paddingHorizontal: scale(12),
|
|
132
|
+
textAlign: 'center',
|
|
133
|
+
},
|
|
134
|
+
optionTextSelected: {
|
|
135
|
+
fontWeight: '700',
|
|
136
|
+
textDecorationLine: 'underline', // selected highlight
|
|
137
|
+
},
|
|
138
|
+
optionTextUnselected: {
|
|
139
|
+
opacity: 0.8,
|
|
140
|
+
},
|
|
141
|
+
});
|