@quintype/native-components 2.28.5-beta.1 → 2.28.5-beta.2
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/package.json
CHANGED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import React, { memo } from "react";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import { useProgress, usePlaybackState, State } from "react-native-track-player";
|
|
4
|
+
import { TouchableOpacity, ActivityIndicator, View, ViewPropTypes } from "react-native";
|
|
5
|
+
import Icon from "react-native-vector-icons/AntDesign";
|
|
6
|
+
import MaterialCommunityIcons from "react-native-vector-icons/MaterialCommunityIcons";
|
|
7
|
+
import { playerStyles } from "./styles";
|
|
8
|
+
import Slider from "@react-native-community/slider";
|
|
9
|
+
import { AppTheme } from "../../utils";
|
|
10
|
+
import { useContext } from "react";
|
|
11
|
+
import {ResponsiveHeight, ResponsiveWidth} from '../../utils/screenUtils'
|
|
12
|
+
import { Text } from '../index';
|
|
13
|
+
|
|
14
|
+
const ProgressBar = (props) => {
|
|
15
|
+
const progress = useProgress();
|
|
16
|
+
const { theme } = useContext(AppTheme);
|
|
17
|
+
const { COLORS} = theme;
|
|
18
|
+
const {progressCompleted, progressBar } = playerStyles();
|
|
19
|
+
const stylesWithFlexValue = (flex, styles) => ({ flex: flex, ...styles });
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<View style={progressBar}>
|
|
23
|
+
<Slider
|
|
24
|
+
style={stylesWithFlexValue(progress.position, progressCompleted)}
|
|
25
|
+
minimumValue={0}
|
|
26
|
+
maximumValue={progress.duration}
|
|
27
|
+
minimumTrackTintColor={COLORS.BRAND_BLACK}
|
|
28
|
+
maximumTrackTintColor={COLORS.MONO_6}
|
|
29
|
+
value={progress.position}
|
|
30
|
+
onValueChange={(val) => props.seekTo(val)}
|
|
31
|
+
thumbTintColor={COLORS.BRAND_BLACK}
|
|
32
|
+
thumbStyle={{size:50}}
|
|
33
|
+
/>
|
|
34
|
+
</View>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const ControlButton = ({ iconName, onPress, FONT_SIZE }) => {
|
|
39
|
+
const { controlButtonContainer, controlButton } = playerStyles();
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<TouchableOpacity style={controlButtonContainer} onPress={onPress} >
|
|
43
|
+
<Icon name={iconName} size={FONT_SIZE.title} style={controlButton} />
|
|
44
|
+
</TouchableOpacity>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const PlayerBase = (props) => {
|
|
49
|
+
const { theme } = useContext(AppTheme);
|
|
50
|
+
const { COLORS,translate, FONT_SIZE, DARK_MODE } = theme;
|
|
51
|
+
const { card, container, subContainer, playerControlStyle, playbackSpeedText, playerContainer } = playerStyles();
|
|
52
|
+
const { style, onTogglePlayback, testID, seekTo, rewindOrForward, isFinishedPlaying, restart, closeButtonHandler } = props;
|
|
53
|
+
|
|
54
|
+
const playbackState = usePlaybackState();
|
|
55
|
+
|
|
56
|
+
const getControlButton = (state) => {
|
|
57
|
+
switch (state?.state) {
|
|
58
|
+
case State.Buffering:
|
|
59
|
+
return <ActivityIndicator size="large" />;
|
|
60
|
+
case State.Playing:
|
|
61
|
+
return (
|
|
62
|
+
<ControlButton
|
|
63
|
+
iconName={"pausecircleo"}
|
|
64
|
+
onPress={() => {
|
|
65
|
+
onTogglePlayback();
|
|
66
|
+
}}
|
|
67
|
+
FONT_SIZE={FONT_SIZE}
|
|
68
|
+
/>
|
|
69
|
+
);
|
|
70
|
+
default:
|
|
71
|
+
return <ControlButton iconName={"playcircleo"} onPress={onTogglePlayback} FONT_SIZE={FONT_SIZE}/>;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
function formatNumber(num) {
|
|
76
|
+
return num % 1 === 0 ? num.toFixed(1) : num.toString();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<View testID={testID}>
|
|
81
|
+
<View
|
|
82
|
+
style={container}
|
|
83
|
+
>
|
|
84
|
+
<Text
|
|
85
|
+
style={subContainer}
|
|
86
|
+
>
|
|
87
|
+
{translate("Listen to this article")}
|
|
88
|
+
</Text>
|
|
89
|
+
<View style={playerControlStyle}>
|
|
90
|
+
<TouchableOpacity style={{ marginHorizontal: 10 }} onPress={() => rewindOrForward("rewind")}>
|
|
91
|
+
<MaterialCommunityIcons color={COLORS.BRAND_BLACK} name={"rewind-10"} size={FONT_SIZE.title} />
|
|
92
|
+
</TouchableOpacity>
|
|
93
|
+
<TouchableOpacity style={{ marginHorizontal: 10 }} onPress={() => rewindOrForward("forward")}>
|
|
94
|
+
<MaterialCommunityIcons color={COLORS.BRAND_BLACK} name={"fast-forward-10"} size={FONT_SIZE.title} />
|
|
95
|
+
</TouchableOpacity>
|
|
96
|
+
<TouchableOpacity
|
|
97
|
+
style={playbackSpeedText}
|
|
98
|
+
onPress={() => props.setShowModal(true)}
|
|
99
|
+
>
|
|
100
|
+
<Text style={{ fontSize: FONT_SIZE.h3, color: DARK_MODE ? COLORS.BRAND_WHITE : COLORS.BRAND_BLACK}}>{`${formatNumber(props.audioRate)}x`}</Text>
|
|
101
|
+
</TouchableOpacity>
|
|
102
|
+
</View>
|
|
103
|
+
</View>
|
|
104
|
+
|
|
105
|
+
<View
|
|
106
|
+
style={[card,style]}
|
|
107
|
+
>
|
|
108
|
+
<View style={playerContainer}>
|
|
109
|
+
{ isFinishedPlaying ?
|
|
110
|
+
<ControlButton iconName={"reload1"} onPress={() => restart()} FONT_SIZE={FONT_SIZE} /> :
|
|
111
|
+
getControlButton(playbackState)}
|
|
112
|
+
</View>
|
|
113
|
+
<ProgressBar seekTo={seekTo} />
|
|
114
|
+
<MaterialCommunityIcons
|
|
115
|
+
color={COLORS.BRAND_BLACK}
|
|
116
|
+
onPress={()=>closeButtonHandler()}
|
|
117
|
+
size={FONT_SIZE.title}
|
|
118
|
+
name="close"
|
|
119
|
+
/>
|
|
120
|
+
</View>
|
|
121
|
+
</View>
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
ControlButton.propTypes = {
|
|
126
|
+
iconName: PropTypes.string.isRequired,
|
|
127
|
+
onPress: PropTypes.func.isRequired,
|
|
128
|
+
testID: PropTypes.string,
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
PlayerBase.propTypes = {
|
|
132
|
+
style: ViewPropTypes.style,
|
|
133
|
+
onTogglePlayback: PropTypes.func.isRequired,
|
|
134
|
+
testID: PropTypes.string,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
PlayerBase.defaultProps = {
|
|
138
|
+
style: {},
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export const Player = memo(PlayerBase);
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { StyleSheet } from "react-native";
|
|
2
|
+
import { AppTheme } from "../../utils";
|
|
3
|
+
import { useContext } from "react";
|
|
4
|
+
import { ResponsiveHeight, ResponsiveWidth } from "../../utils/screenUtils";
|
|
5
|
+
export const playerStyles = () => {
|
|
6
|
+
const { theme } = useContext(AppTheme);
|
|
7
|
+
const { COLORS, DARK_MODE,FONT_SIZE } = theme;
|
|
8
|
+
|
|
9
|
+
return StyleSheet.create({
|
|
10
|
+
container:{
|
|
11
|
+
display: "flex",
|
|
12
|
+
flexDirection: "row",
|
|
13
|
+
justifyContent: "space-between",
|
|
14
|
+
marginHorizontal: 10,
|
|
15
|
+
alignItems: "flex-start",
|
|
16
|
+
marginTop: 15,
|
|
17
|
+
marginBottom: 16,
|
|
18
|
+
},
|
|
19
|
+
subContainer:{
|
|
20
|
+
fontSize: FONT_SIZE.h2,
|
|
21
|
+
color: COLORS.BRAND_BLACK,
|
|
22
|
+
},
|
|
23
|
+
playerControlStyle:{ display: "flex", flexDirection: "row", alignItems: "center" },
|
|
24
|
+
playbackSpeedText:{
|
|
25
|
+
backgroundColor: COLORS.MONO5,
|
|
26
|
+
minWidth: ResponsiveWidth(13),
|
|
27
|
+
height: ResponsiveHeight(3),
|
|
28
|
+
borderRadius: 5,
|
|
29
|
+
alignItems: "center",
|
|
30
|
+
justifyContent: "center",
|
|
31
|
+
marginHorizontal: 10,
|
|
32
|
+
},
|
|
33
|
+
card: {
|
|
34
|
+
backgroundColor: DARK_MODE ? COLORS.MONO6 : COLORS.BRAND_WHITE,
|
|
35
|
+
elevation: 10,
|
|
36
|
+
shadowColor: COLORS.BRAND_BLACK,
|
|
37
|
+
shadowOffset: { width: 0, height: -1 },
|
|
38
|
+
shadowOpacity: 0.1,
|
|
39
|
+
shadowRadius: 1,
|
|
40
|
+
width: "95%",
|
|
41
|
+
minHeight: 70,
|
|
42
|
+
borderBottomWidth: 2,
|
|
43
|
+
borderColor: COLORS.MONO6,
|
|
44
|
+
marginBottom: 7,
|
|
45
|
+
display: "flex",
|
|
46
|
+
flexDirection: "row",
|
|
47
|
+
alignItems: "center",
|
|
48
|
+
marginHorizontal: ResponsiveWidth(2.5),
|
|
49
|
+
borderRadius:5
|
|
50
|
+
},
|
|
51
|
+
playerContainer:{width:ResponsiveWidth(12),display:'flex', flexDirection:'row',alignItems:'center',justifyContent:'center'},
|
|
52
|
+
controlButton: {
|
|
53
|
+
alignItems: "center",
|
|
54
|
+
color: COLORS.BRAND_BLACK,
|
|
55
|
+
},
|
|
56
|
+
controlButtonContainer: {
|
|
57
|
+
alignItems: "center",
|
|
58
|
+
flex: 1,
|
|
59
|
+
},
|
|
60
|
+
controls: {},
|
|
61
|
+
progressBar: { width: ResponsiveWidth(70), marginHorizontal: ResponsiveWidth(1) },
|
|
62
|
+
progressCompleted: {
|
|
63
|
+
backgroundColor: DARK_MODE ? COLORS.MONO6 : COLORS.BRAND_WHITE,
|
|
64
|
+
},
|
|
65
|
+
progressRemaining: {
|
|
66
|
+
backgroundColor: COLORS.BRAND_BLACK,
|
|
67
|
+
opacity: 0.1,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
};
|
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
import { hexToRgb } from '@quintype/native-components/src/utils/colorUtils';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import React, { useContext, useEffect } from 'react';
|
|
4
|
-
import { I18nManager, View } from 'react-native';
|
|
3
|
+
import React, { useContext, useEffect, useState, useFocusEffect } from 'react';
|
|
4
|
+
import { I18nManager, TouchableOpacity, View, StyleSheet, AppState } from 'react-native';
|
|
5
5
|
import LinearGradient from "react-native-linear-gradient";
|
|
6
6
|
import { ClockIcon } from '../../Icons/ClockIcon';
|
|
7
7
|
import { AppTheme, getTimeInFormat } from '../../utils';
|
|
8
8
|
import { isMiddleIndexOfArray } from '../../utils/arrayUtils';
|
|
9
|
-
import { getFirstVideoElement, isStoryFree } from '../../utils/story';
|
|
9
|
+
import { getFirstVideoElement, isStoryFree, getStoryHeadline } from '../../utils/story';
|
|
10
|
+
import { getImageSlug } from '../../utils';
|
|
10
11
|
import { STORY_TYPES } from '../../utils/story-types';
|
|
11
12
|
import { FBCommentsRow } from '../FBCommentsRow';
|
|
12
13
|
import { StoryContent } from '../StoryContent';
|
|
13
14
|
import { StoryHeader } from '../StoryHeader';
|
|
14
15
|
import { TagsRow } from '../TagsRow/TagsRow';
|
|
15
16
|
import { ShareButton, Text } from '../index';
|
|
16
|
-
import { storyStyles } from './styles';
|
|
17
|
+
import { storyStyles, modalStyles } from './styles';
|
|
18
|
+
import { Player } from '../AudioPlayer';
|
|
19
|
+
import Icon from 'react-native-vector-icons/AntDesign';
|
|
20
|
+
import TrackPlayer, { usePlaybackState, Capability, State, AppKilledPlaybackBehavior, useProgress } from "react-native-track-player";
|
|
21
|
+
import MaterialIcon from "react-native-vector-icons/MaterialCommunityIcons";
|
|
22
|
+
import FontAwesomeIcon from "react-native-vector-icons/FontAwesome";
|
|
23
|
+
|
|
24
|
+
import Modal from 'react-native-modal';
|
|
17
25
|
|
|
18
26
|
const getLiveBlogTimeStamp = (card, DATE_FORMAT, story, styles, locale) => {
|
|
19
27
|
const slug = `?cardId=${card.id}`;
|
|
@@ -140,12 +148,151 @@ export const Story = ({
|
|
|
140
148
|
linkedStories,
|
|
141
149
|
showWall,
|
|
142
150
|
userObjectLength,
|
|
143
|
-
numberOfVisibleStoryCard
|
|
151
|
+
numberOfVisibleStoryCard,
|
|
152
|
+
audioS3Key,
|
|
153
|
+
ttsButtonState,
|
|
154
|
+
setShowTTSPlayer,
|
|
155
|
+
gaTrigerredTTS
|
|
144
156
|
}) => {
|
|
145
157
|
const { theme } = useContext(AppTheme);
|
|
146
|
-
const { COLORS, FONT_SIZE, locale, DARK_MODE } = theme;
|
|
147
|
-
const styles = storyStyles(COLORS, FONT_SIZE);
|
|
158
|
+
const { COLORS, FONT_SIZE, locale, DARK_MODE,translate } = theme;
|
|
159
|
+
const styles = storyStyles(COLORS, FONT_SIZE, DARK_MODE);
|
|
160
|
+
const Modalstyles = modalStyles(COLORS, FONT_SIZE, DARK_MODE);
|
|
148
161
|
const cards = story?.cards ?? [];
|
|
162
|
+
const [showModal, setShowModal] = useState(false);
|
|
163
|
+
const [audioRate, setAudioRate] = useState(1);
|
|
164
|
+
const playbackState = usePlaybackState();
|
|
165
|
+
const progress = useProgress();
|
|
166
|
+
const [isFinishedPlaying, setFinishedPlaying] = useState(false);
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
const setup = async () => {
|
|
170
|
+
//If TrackPlayer is not initialized, initialize it
|
|
171
|
+
try{
|
|
172
|
+
let activeTrack = await TrackPlayer.getActiveTrack();
|
|
173
|
+
} catch(err){
|
|
174
|
+
await TrackPlayer.setupPlayer({});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
await TrackPlayer.updateOptions({
|
|
178
|
+
capabilities: [Capability.Play, Capability.Pause, Capability.Stop],
|
|
179
|
+
compactCapabilities: [Capability.Play, Capability.Pause],
|
|
180
|
+
notificationCapabilities: [Capability.Play, Capability.Pause, Capability.Stop],
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const addTrack = async () => {
|
|
185
|
+
setFinishedPlaying(false)
|
|
186
|
+
let activeTrack = await TrackPlayer.getActiveTrack();
|
|
187
|
+
const storyAudioURL = `${cdn}/${audioS3Key}`;
|
|
188
|
+
|
|
189
|
+
if (!activeTrack || activeTrack?.id !== audioS3Key) {
|
|
190
|
+
await TrackPlayer.reset();
|
|
191
|
+
const headline = getStoryHeadline(story);
|
|
192
|
+
|
|
193
|
+
await TrackPlayer.add({
|
|
194
|
+
id: audioS3Key,
|
|
195
|
+
url: storyAudioURL,
|
|
196
|
+
title: headline,
|
|
197
|
+
album: cdn + "/" + getImageSlug(story),
|
|
198
|
+
artwork: cdn + "/" + getImageSlug(story),
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const initialSetup = async () => {
|
|
204
|
+
await setup();
|
|
205
|
+
await addTrack();
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
React.useEffect(() => {
|
|
209
|
+
const unsubscribe = navigation.addListener('blur', async() => {
|
|
210
|
+
await TrackPlayer.pause();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const unsubscribe2 = navigation.addListener('focus', async() => {
|
|
214
|
+
addTrack();
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
const appStateListener = AppState.addEventListener('change', async (nextAppState) => {
|
|
218
|
+
if (nextAppState !== 'active') {
|
|
219
|
+
await TrackPlayer.pause();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
return () => {
|
|
225
|
+
unsubscribe();
|
|
226
|
+
unsubscribe2();
|
|
227
|
+
};
|
|
228
|
+
}, [navigation]);
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
const resetPlayer = async () => {
|
|
233
|
+
setAudioRate(1);
|
|
234
|
+
setShowTTSPlayer(false);
|
|
235
|
+
|
|
236
|
+
await TrackPlayer.stop();
|
|
237
|
+
await TrackPlayer.setRate(1);
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
useEffect(() => {
|
|
241
|
+
return async() => await TrackPlayer.pause();
|
|
242
|
+
}, []);
|
|
243
|
+
|
|
244
|
+
useEffect(()=>{
|
|
245
|
+
if(progress?.duration < progress.position + 1 && progress.position !== 0){
|
|
246
|
+
setFinishedPlaying(true);
|
|
247
|
+
}
|
|
248
|
+
},[progress])
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
useEffect(() => {
|
|
253
|
+
if (audioS3Key) {
|
|
254
|
+
initialSetup();
|
|
255
|
+
}
|
|
256
|
+
}, [audioS3Key]);
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
const restartPlayer = async () => {
|
|
260
|
+
setFinishedPlaying(false);
|
|
261
|
+
await TrackPlayer.seekTo(0);
|
|
262
|
+
await TrackPlayer.play();
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const togglePlayback = async () => {
|
|
266
|
+
console.log('risi - tts toggle playback', playbackState.state, TrackPlayer)
|
|
267
|
+
if (playbackState?.state === State.Playing) {
|
|
268
|
+
await TrackPlayer.pause();
|
|
269
|
+
} else {
|
|
270
|
+
await TrackPlayer.play();
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
const rewindOrForward = async (type) => {
|
|
274
|
+
const duration = await TrackPlayer.getProgress();
|
|
275
|
+
|
|
276
|
+
if (type === "rewind") {
|
|
277
|
+
await TrackPlayer.seekTo(duration.position - 10);
|
|
278
|
+
} else {
|
|
279
|
+
await TrackPlayer.seekTo(duration.position + 10);
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const updateSpeedRate = async (rate) => {
|
|
284
|
+
await TrackPlayer.setRate(rate);
|
|
285
|
+
setAudioRate(rate);
|
|
286
|
+
setShowModal(false);
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
useEffect(()=>{
|
|
290
|
+
if(ttsButtonState && storyHasAccess === 'granted' && !showPlayer){
|
|
291
|
+
setShowPlayer(true)
|
|
292
|
+
togglePlayback()
|
|
293
|
+
|
|
294
|
+
}
|
|
295
|
+
},[ttsButtonState])
|
|
149
296
|
|
|
150
297
|
|
|
151
298
|
const firstVideoElement = story['story-template'] === STORY_TYPES.VIDEO_STORY
|
|
@@ -198,6 +345,28 @@ export const Story = ({
|
|
|
198
345
|
);
|
|
199
346
|
};
|
|
200
347
|
|
|
348
|
+
const [showPlayer, setShowPlayer] = useState(false);
|
|
349
|
+
const closeButtonHandler = async() =>{
|
|
350
|
+
setFinishedPlaying(false);
|
|
351
|
+
setShowTTSPlayer(false);
|
|
352
|
+
setShowPlayer(false);
|
|
353
|
+
try{
|
|
354
|
+
await TrackPlayer.seekTo(0);
|
|
355
|
+
await resetPlayer();
|
|
356
|
+
}catch(err) {
|
|
357
|
+
console.log(err)
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const ModalContent = ({rate, updateSpeedRate}) => {
|
|
362
|
+
return <Text
|
|
363
|
+
onPress={() => updateSpeedRate(rate)}
|
|
364
|
+
style={styles.textStyle}
|
|
365
|
+
>
|
|
366
|
+
{rate}
|
|
367
|
+
</Text>
|
|
368
|
+
}
|
|
369
|
+
|
|
201
370
|
return (
|
|
202
371
|
<>
|
|
203
372
|
<View>
|
|
@@ -209,7 +378,33 @@ export const Story = ({
|
|
|
209
378
|
onAuthorPress={onAuthorPress}
|
|
210
379
|
onSectionPress={onSectionPress}
|
|
211
380
|
/>
|
|
212
|
-
|
|
381
|
+
{ storyHasAccess === 'granted' && audioS3Key && (!showPlayer ?
|
|
382
|
+
(<TouchableOpacity onPress={async()=>{
|
|
383
|
+
gaTrigerredTTS({ slug: story.slug });
|
|
384
|
+
setShowPlayer(true)
|
|
385
|
+
await togglePlayback()
|
|
386
|
+
}}
|
|
387
|
+
style={styles.ttsStoryButtonStyle}
|
|
388
|
+
>
|
|
389
|
+
<MaterialIcon name='account-voice' size={FONT_SIZE.h2} color={COLORS.BRAND_BLACK}/>
|
|
390
|
+
<Text style={styles.ttsTextStyle}>{translate('Listen to this article')}</Text>
|
|
391
|
+
</TouchableOpacity>) :
|
|
392
|
+
(<View
|
|
393
|
+
style={styles.ttsPlayerContainer}
|
|
394
|
+
>
|
|
395
|
+
<Player
|
|
396
|
+
onTogglePlayback={togglePlayback}
|
|
397
|
+
seekTo={TrackPlayer.seekTo}
|
|
398
|
+
rewindOrForward={rewindOrForward}
|
|
399
|
+
setShowTTSPlayer={setShowPlayer}
|
|
400
|
+
setShowModal={setShowModal}
|
|
401
|
+
audioRate={audioRate}
|
|
402
|
+
resetPlayer={resetPlayer}
|
|
403
|
+
isFinishedPlaying={isFinishedPlaying}
|
|
404
|
+
closeButtonHandler={closeButtonHandler}
|
|
405
|
+
restart={restartPlayer}
|
|
406
|
+
/>
|
|
407
|
+
</View>))}
|
|
213
408
|
|
|
214
409
|
|
|
215
410
|
{getAd()}
|
|
@@ -247,10 +442,39 @@ export const Story = ({
|
|
|
247
442
|
<View>
|
|
248
443
|
<TagsRow tags={story.tags} onPress={onTagsPress} />
|
|
249
444
|
</View>
|
|
445
|
+
<Modal
|
|
446
|
+
isVisible={showModal}
|
|
447
|
+
transparent={true}
|
|
448
|
+
onBackdropPress={() => setShowModal(false)}
|
|
449
|
+
style={Modalstyles.modalContainer}
|
|
450
|
+
swipeDirection="down"
|
|
451
|
+
onSwipeComplete={() => setShowModal(false)}
|
|
452
|
+
>
|
|
453
|
+
<View style={[Modalstyles.modalContent, styles.modalBackgound]}>
|
|
454
|
+
<View style={Modalstyles.header}>
|
|
455
|
+
<Text style={[Modalstyles.title, styles.textStyle]}>{translate("Playback Speed")}</Text>
|
|
456
|
+
<TouchableOpacity onPress={() => setShowModal(false)}>
|
|
457
|
+
<MaterialIcon name="close" size={25} color={styles?.textStyle?.color} />
|
|
458
|
+
</TouchableOpacity>
|
|
459
|
+
</View>
|
|
460
|
+
<View style={Modalstyles.modalBody}>
|
|
461
|
+
<ModalContent rate={0.25} updateSpeedRate={updateSpeedRate}/>
|
|
462
|
+
<ModalContent rate={0.5} updateSpeedRate={updateSpeedRate}/>
|
|
463
|
+
<ModalContent rate={0.75} updateSpeedRate={updateSpeedRate}/>
|
|
464
|
+
<ModalContent rate={1} updateSpeedRate={updateSpeedRate}/>
|
|
465
|
+
<ModalContent rate={1.25} updateSpeedRate={updateSpeedRate}/>
|
|
466
|
+
<ModalContent rate={1.55} updateSpeedRate={updateSpeedRate}/>
|
|
467
|
+
<ModalContent rate={1.75} updateSpeedRate={updateSpeedRate}/>
|
|
468
|
+
<ModalContent rate={2} updateSpeedRate={updateSpeedRate}/>
|
|
469
|
+
</View>
|
|
470
|
+
</View>
|
|
471
|
+
</Modal>
|
|
250
472
|
</>
|
|
251
473
|
);
|
|
252
474
|
};
|
|
253
475
|
|
|
476
|
+
|
|
477
|
+
|
|
254
478
|
Story.propTypes = {
|
|
255
479
|
story: PropTypes.any.isRequired,
|
|
256
480
|
cdn: PropTypes.string.isRequired,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { StyleSheet } from 'react-native';
|
|
2
2
|
|
|
3
|
-
export const storyStyles = (COLORS, FONT_SIZE) => StyleSheet.create({
|
|
3
|
+
export const storyStyles = (COLORS, FONT_SIZE, DARK_MODE) => StyleSheet.create({
|
|
4
4
|
container: {
|
|
5
5
|
padding: 10,
|
|
6
6
|
flexDirection: 'row',
|
|
@@ -39,5 +39,81 @@ export const storyStyles = (COLORS, FONT_SIZE) => StyleSheet.create({
|
|
|
39
39
|
},
|
|
40
40
|
overlay:{
|
|
41
41
|
marginTop:-220
|
|
42
|
-
}
|
|
42
|
+
},
|
|
43
|
+
modalBackgound: {
|
|
44
|
+
backgroundColor: COLORS.BRAND_WHITE,
|
|
45
|
+
position:'absolute',
|
|
46
|
+
bottom:0,
|
|
47
|
+
width:'100%',
|
|
48
|
+
|
|
49
|
+
},
|
|
50
|
+
textStyle: {
|
|
51
|
+
color: COLORS.BRAND_BLACK,
|
|
52
|
+
marginVertical: 10,
|
|
53
|
+
fontSize: FONT_SIZE.h3
|
|
54
|
+
},
|
|
55
|
+
ttsStoryButtonStyle:{
|
|
56
|
+
flexDirection:'row',
|
|
57
|
+
marginLeft:10,
|
|
58
|
+
alignItems:'center',
|
|
59
|
+
padding:10,
|
|
60
|
+
borderRadius:35,
|
|
61
|
+
paddingLeft:15,
|
|
62
|
+
alignSelf:'flex-start',
|
|
63
|
+
backgroundColor:COLORS.MONO6,
|
|
64
|
+
backgroundColor: DARK_MODE ? COLORS.MONO6 : COLORS.BRAND_WHITE,
|
|
65
|
+
elevation: 5,
|
|
66
|
+
shadowColor: COLORS.BRAND_BLACK,
|
|
67
|
+
shadowOffset: { width: 0, height: -1 },
|
|
68
|
+
shadowOpacity: 0.1,
|
|
69
|
+
shadowRadius: 1,
|
|
70
|
+
borderWidth:1,
|
|
71
|
+
borderColor: COLORS.MONO6,
|
|
72
|
+
marginBottom: 7},
|
|
73
|
+
ttsTextStyle:{marginLeft:10, fontSize:FONT_SIZE.h3, color:COLORS.BRAND_BLACK},
|
|
74
|
+
ttsPlayerContainer:{
|
|
75
|
+
width: "100%",
|
|
76
|
+
alignSelf: "center",
|
|
77
|
+
zIndex: 1,
|
|
78
|
+
marginBottom: 5,
|
|
79
|
+
marginVertical:10,
|
|
80
|
+
|
|
81
|
+
}
|
|
43
82
|
});
|
|
83
|
+
|
|
84
|
+
export const modalStyles = (COLORS, FONT_SIZE, DARK_MODE) => StyleSheet.create({
|
|
85
|
+
container: {
|
|
86
|
+
flex: 1,
|
|
87
|
+
justifyContent: "center",
|
|
88
|
+
alignItems: "center",
|
|
89
|
+
backgroundColor:'green'
|
|
90
|
+
},
|
|
91
|
+
modalContainer: {
|
|
92
|
+
justifyContent: "flex-end",
|
|
93
|
+
margin: 0,
|
|
94
|
+
position:'absolute',
|
|
95
|
+
bottom:0,
|
|
96
|
+
width:'100%',
|
|
97
|
+
},
|
|
98
|
+
modalContent: {
|
|
99
|
+
borderTopLeftRadius: 15,
|
|
100
|
+
borderTopRightRadius: 15,
|
|
101
|
+
padding: 32,
|
|
102
|
+
},
|
|
103
|
+
header: {
|
|
104
|
+
flexDirection: "row",
|
|
105
|
+
justifyContent: "space-between",
|
|
106
|
+
alignItems: "center",
|
|
107
|
+
borderBottomWidth: 1,
|
|
108
|
+
borderBottomColor: COLORS.MONO5,
|
|
109
|
+
paddingBottom: 10,
|
|
110
|
+
},
|
|
111
|
+
title: {
|
|
112
|
+
fontSize: 18,
|
|
113
|
+
fontWeight: "bold",
|
|
114
|
+
},
|
|
115
|
+
modalBody: {
|
|
116
|
+
marginTop: 15,
|
|
117
|
+
fontSize: 16,
|
|
118
|
+
},
|
|
119
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import get from "lodash/get";
|
|
2
|
+
import { useNavigationState } from "@react-navigation/native";
|
|
3
|
+
import { Dimensions } from "react-native";
|
|
4
|
+
|
|
5
|
+
export const getScreenCount = (screenName) => {
|
|
6
|
+
/* Note: To figure out if there are screen instances in the stack */
|
|
7
|
+
const navigationState = useNavigationState((state) => state) || {};
|
|
8
|
+
return get(navigationState, ["routes"], []).reduce((acc, route) => {
|
|
9
|
+
if (route.name === screenName) {
|
|
10
|
+
acc = acc + 1;
|
|
11
|
+
}
|
|
12
|
+
return acc;
|
|
13
|
+
}, 0);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const ResponsiveHeight = (height) => {
|
|
17
|
+
const deviceHeight = Dimensions.get("window").height;
|
|
18
|
+
const responsiveHeight = (deviceHeight * height) / 1000;
|
|
19
|
+
return responsiveHeight;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const ResponsiveWidth = (width) => {
|
|
23
|
+
const deviceWidth = Dimensions.get("window").width;
|
|
24
|
+
const responsiveWidth = (deviceWidth * width) / 1000;
|
|
25
|
+
return responsiveWidth;
|
|
26
|
+
};
|