diff --git a/app/containers/message/Components/Audio/Loading.tsx b/app/containers/AudioPlayer/Loading.tsx
similarity index 87%
rename from app/containers/message/Components/Audio/Loading.tsx
rename to app/containers/AudioPlayer/Loading.tsx
index 34baf68f2..90acf2c53 100644
--- a/app/containers/message/Components/Audio/Loading.tsx
+++ b/app/containers/AudioPlayer/Loading.tsx
@@ -1,8 +1,8 @@
import React, { useEffect } from 'react';
import Animated, { Easing, useAnimatedStyle, useSharedValue, withRepeat, withTiming } from 'react-native-reanimated';
-import { CustomIcon } from '../../../CustomIcon';
-import { useTheme } from '../../../../theme';
+import { CustomIcon } from '../CustomIcon';
+import { useTheme } from '../../theme';
const Loading = () => {
const rotation = useSharedValue(0);
diff --git a/app/containers/message/Components/Audio/PlayButton.tsx b/app/containers/AudioPlayer/PlayButton.tsx
similarity index 89%
rename from app/containers/message/Components/Audio/PlayButton.tsx
rename to app/containers/AudioPlayer/PlayButton.tsx
index 913e81e2d..80501596e 100644
--- a/app/containers/message/Components/Audio/PlayButton.tsx
+++ b/app/containers/AudioPlayer/PlayButton.tsx
@@ -1,8 +1,8 @@
import React from 'react';
-import Touchable from '../../Touchable';
-import { CustomIcon } from '../../../CustomIcon';
-import { useTheme } from '../../../../theme';
+import Touchable from '../message/Touchable';
+import { CustomIcon } from '../CustomIcon';
+import { useTheme } from '../../theme';
import styles from './styles';
import Loading from './Loading';
diff --git a/app/containers/message/Components/Audio/AudioRate.tsx b/app/containers/AudioPlayer/PlaybackSpeed.tsx
similarity index 56%
rename from app/containers/message/Components/Audio/AudioRate.tsx
rename to app/containers/AudioPlayer/PlaybackSpeed.tsx
index 2fddb1d55..0d14ed934 100644
--- a/app/containers/message/Components/Audio/AudioRate.tsx
+++ b/app/containers/AudioPlayer/PlaybackSpeed.tsx
@@ -2,10 +2,10 @@ import React from 'react';
import { Text } from 'react-native';
import styles from './styles';
-import { useTheme } from '../../../../theme';
-import Touchable from '../../Touchable';
+import { useTheme } from '../../theme';
+import Touchable from '../message/Touchable';
-const AudioRate = ({
+const PlaybackSpeed = ({
onChange,
loaded = false,
rate = 1
@@ -25,11 +25,11 @@ const AudioRate = ({
- {rate}x
+ {rate}x
);
};
-export default AudioRate;
+export default PlaybackSpeed;
diff --git a/app/containers/message/Components/Audio/Slider.tsx b/app/containers/AudioPlayer/Seek.tsx
similarity index 90%
rename from app/containers/message/Components/Audio/Slider.tsx
rename to app/containers/AudioPlayer/Seek.tsx
index 6f8977ce1..30babadba 100644
--- a/app/containers/message/Components/Audio/Slider.tsx
+++ b/app/containers/AudioPlayer/Seek.tsx
@@ -12,11 +12,11 @@ import Animated, {
} from 'react-native-reanimated';
import styles from './styles';
-import { useTheme } from '../../../../theme';
+import { useTheme } from '../../theme';
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput);
-interface ISlider {
+interface ISeek {
duration: SharedValue;
currentTime: SharedValue;
loaded: boolean;
@@ -30,7 +30,7 @@ const BUTTON_HIT_SLOP = {
left: 8
};
-const Slider = ({ currentTime, duration, loaded = false, onChangeTime }: ISlider) => {
+const Seek = ({ currentTime, duration, loaded = false, onChangeTime }: ISeek) => {
const { colors } = useTheme();
const maxWidth = useSharedValue(1);
@@ -90,6 +90,7 @@ const Slider = ({ currentTime, duration, loaded = false, onChangeTime }: ISlider
}
});
+ // https://docs.swmansion.com/react-native-reanimated/docs/2.x/fundamentals/worklets/
const formatTime = (ms: number) => {
'worklet';
@@ -129,22 +130,22 @@ const Slider = ({ currentTime, duration, loaded = false, onChangeTime }: ISlider
const thumbColor = loaded ? colors.buttonBackgroundPrimaryDefault : colors.tintDisabled;
return (
-
+
-
+
-
+
);
};
-export default Slider;
+export default Seek;
diff --git a/app/containers/AudioPlayer/index.tsx b/app/containers/AudioPlayer/index.tsx
new file mode 100644
index 000000000..459dfff39
--- /dev/null
+++ b/app/containers/AudioPlayer/index.tsx
@@ -0,0 +1,208 @@
+import React, { useEffect, useRef, useState } from 'react';
+import { StyleProp, TextStyle, View } from 'react-native';
+import { AVPlaybackStatus } from 'expo-av';
+import { activateKeepAwake, deactivateKeepAwake } from 'expo-keep-awake';
+import { useSharedValue } from 'react-native-reanimated';
+
+import { IAttachment, IUserMessage } from '../../definitions';
+import { useTheme } from '../../theme';
+import { downloadMediaFile, getMediaCache } from '../../lib/methods/handleMediaDownload';
+import { fetchAutoDownloadEnabled } from '../../lib/methods/autoDownloadPreference';
+import styles from './styles';
+import Seek from './Seek';
+import PlaybackSpeed from './PlaybackSpeed';
+import PlayButton from './PlayButton';
+import audioPlayer from '../../lib/methods/audioPlayer';
+
+interface IAudioPlayerProps {
+ file: IAttachment;
+ isReply?: boolean;
+ style?: StyleProp[];
+ author?: IUserMessage;
+ msg?: string;
+ baseUrl: string;
+ user: any;
+}
+
+const AudioPlayer = ({ file, author, isReply = false, baseUrl, user }: IAudioPlayerProps) => {
+ const [loading, setLoading] = useState(true);
+ const [paused, setPaused] = useState(true);
+ const [cached, setCached] = useState(false);
+ const [rate, setRate] = useState(1);
+
+ const duration = useSharedValue(0);
+ const currentTime = useSharedValue(0);
+
+ const { colors } = useTheme();
+
+ const audioUri = useRef('');
+
+ const onPlaybackStatusUpdate = (status: AVPlaybackStatus) => {
+ if (status) {
+ onPlaying(status);
+ handlePlaybackStatusUpdate(status);
+ onEnd(status);
+ }
+ };
+
+ const loadAudio = async (audio: string) => {
+ await audioPlayer.loadAudio(audio);
+ audioUri.current = audio;
+ audioPlayer.setOnPlaybackStatusUpdate(audio, onPlaybackStatusUpdate);
+ };
+
+ const onPlaying = (data: AVPlaybackStatus) => {
+ if (data.isLoaded && data.isPlaying) {
+ setPaused(false);
+ } else {
+ setPaused(true);
+ }
+ };
+
+ const handlePlaybackStatusUpdate = (data: AVPlaybackStatus) => {
+ if (data.isLoaded && data.durationMillis) {
+ const durationSeconds = data.durationMillis / 1000;
+ duration.value = durationSeconds > 0 ? durationSeconds : 0;
+ const currentSecond = data.positionMillis / 1000;
+ if (currentSecond <= durationSeconds) {
+ currentTime.value = currentSecond;
+ }
+ setRate(data.rate);
+ }
+ };
+
+ const onEnd = (data: AVPlaybackStatus) => {
+ if (data.isLoaded) {
+ if (data.didJustFinish) {
+ try {
+ setPaused(true);
+ currentTime.value = 0;
+ } catch {
+ // do nothing
+ }
+ }
+ }
+ };
+
+ const setPosition = async (time: number) => {
+ await audioPlayer.setPositionAsync(audioUri.current, time);
+ };
+
+ const getUrl = () => {
+ let url = file.audio_url;
+ if (url && !url.startsWith('http')) {
+ url = `${baseUrl}${file.audio_url}`;
+ }
+ return url;
+ };
+
+ const togglePlayPause = async () => {
+ try {
+ if (!paused) {
+ await audioPlayer.pauseAudio(audioUri.current);
+ } else {
+ await audioPlayer.playAudio(audioUri.current);
+ }
+ } catch {
+ // Do nothing
+ }
+ };
+
+ const onChangeRate = async (value = 1.0) => {
+ await audioPlayer.setRateAsync(audioUri.current, value);
+ };
+
+ const handleDownload = async () => {
+ setLoading(true);
+ try {
+ const url = getUrl();
+ if (url) {
+ const audio = await downloadMediaFile({
+ downloadUrl: `${url}?rc_uid=${user.id}&rc_token=${user.token}`,
+ type: 'audio',
+ mimeType: file.audio_type
+ });
+ await loadAudio(audio);
+ setLoading(false);
+ setCached(true);
+ }
+ } catch {
+ setLoading(false);
+ setCached(false);
+ }
+ };
+
+ const handleAutoDownload = async () => {
+ const url = getUrl();
+ try {
+ if (url) {
+ const isCurrentUserAuthor = author?._id === user.id;
+ const isAutoDownloadEnabled = fetchAutoDownloadEnabled('audioPreferenceDownload');
+ if (isAutoDownloadEnabled || isCurrentUserAuthor) {
+ await handleDownload();
+ return;
+ }
+ setLoading(false);
+ setCached(false);
+ }
+ } catch {
+ // Do nothing
+ }
+ };
+
+ const onPress = () => {
+ if (loading) {
+ return;
+ }
+ if (cached) {
+ togglePlayPause();
+ return;
+ }
+ handleDownload();
+ };
+
+ useEffect(() => {
+ const handleCache = async () => {
+ const cachedAudioResult = await getMediaCache({
+ type: 'audio',
+ mimeType: file.audio_type,
+ urlToCache: getUrl()
+ });
+ if (cachedAudioResult?.exists) {
+ await loadAudio(cachedAudioResult.uri);
+ setLoading(false);
+ setCached(true);
+ return;
+ }
+ if (isReply) {
+ setLoading(false);
+ return;
+ }
+ await handleAutoDownload();
+ };
+ handleCache();
+ }, []);
+
+ useEffect(() => {
+ if (paused) {
+ deactivateKeepAwake();
+ } else {
+ activateKeepAwake();
+ }
+ }, [paused]);
+
+ if (!baseUrl) {
+ return null;
+ }
+ return (
+ <>
+
+
+
+
+
+ >
+ );
+};
+
+export default AudioPlayer;
diff --git a/app/containers/message/Components/Audio/styles.ts b/app/containers/AudioPlayer/styles.ts
similarity index 87%
rename from app/containers/message/Components/Audio/styles.ts
rename to app/containers/AudioPlayer/styles.ts
index 5fbbf934e..c85ec1809 100644
--- a/app/containers/message/Components/Audio/styles.ts
+++ b/app/containers/AudioPlayer/styles.ts
@@ -1,6 +1,6 @@
import { StyleSheet } from 'react-native';
-import sharedStyles from '../../../../views/Styles';
+import sharedStyles from '../../views/Styles';
const styles = StyleSheet.create({
audioContainer: {
@@ -20,13 +20,13 @@ const styles = StyleSheet.create({
borderRadius: 4,
justifyContent: 'center'
},
- sliderContainer: {
+ seekContainer: {
flexDirection: 'row',
flex: 1,
alignItems: 'center',
height: '100%'
},
- slider: {
+ seek: {
marginRight: 12,
height: '100%',
justifyContent: 'center',
@@ -45,13 +45,13 @@ const styles = StyleSheet.create({
fontSize: 14,
...sharedStyles.textRegular
},
- thumbSlider: {
+ thumbSeek: {
height: 12,
width: 12,
borderRadius: 6,
zIndex: 3
},
- containerAudioRate: {
+ containerPlaybackSpeed: {
width: 36,
height: 24,
borderRadius: 4,
@@ -60,7 +60,7 @@ const styles = StyleSheet.create({
alignItems: 'center',
overflow: 'hidden'
},
- audioRateText: {
+ playbackSpeedText: {
fontSize: 14,
...sharedStyles.textBold
}
diff --git a/app/containers/message/Components/Audio/index.tsx b/app/containers/message/Components/Audio/index.tsx
index 50ed3e586..5425105f8 100644
--- a/app/containers/message/Components/Audio/index.tsx
+++ b/app/containers/message/Components/Audio/index.tsx
@@ -1,21 +1,11 @@
-import React, { useContext, useEffect, useRef, useState } from 'react';
-import { StyleProp, TextStyle, View } from 'react-native';
-import { AVPlaybackStatus } from 'expo-av';
-import { activateKeepAwake, deactivateKeepAwake } from 'expo-keep-awake';
-import { useSharedValue } from 'react-native-reanimated';
+import React, { useContext } from 'react';
+import { StyleProp, TextStyle } from 'react-native';
import Markdown from '../../../markdown';
import MessageContext from '../../Context';
import { TGetCustomEmoji } from '../../../../definitions/IEmoji';
import { IAttachment, IUserMessage } from '../../../../definitions';
-import { useTheme } from '../../../../theme';
-import { downloadMediaFile, getMediaCache } from '../../../../lib/methods/handleMediaDownload';
-import { fetchAutoDownloadEnabled } from '../../../../lib/methods/autoDownloadPreference';
-import styles from './styles';
-import Slider from './Slider';
-import AudioRate from './AudioRate';
-import PlayButton from './PlayButton';
-import audioPlayer from '../../../../lib/methods/audioPlayer';
+import AudioPlayer from '../../../AudioPlayer';
interface IMessageAudioProps {
file: IAttachment;
@@ -27,172 +17,7 @@ interface IMessageAudioProps {
}
const MessageAudio = ({ file, getCustomEmoji, author, isReply, style, msg }: IMessageAudioProps) => {
- const [loading, setLoading] = useState(true);
- const [paused, setPaused] = useState(true);
- const [cached, setCached] = useState(false);
- const [rate, setRate] = useState(1);
-
- const duration = useSharedValue(0);
- const currentTime = useSharedValue(0);
-
const { baseUrl, user } = useContext(MessageContext);
- const { colors } = useTheme();
-
- const audioUri = useRef('');
-
- const onPlaybackStatusUpdate = (status: AVPlaybackStatus) => {
- if (status) {
- onPlaying(status);
- handlePlaybackStatusUpdate(status);
- onEnd(status);
- }
- };
-
- const loadAudio = async (audio: string) => {
- await audioPlayer.loadAudio(audio);
- audioUri.current = audio;
- audioPlayer.setOnPlaybackStatusUpdate(audio, onPlaybackStatusUpdate);
- };
-
- const onPlaying = (data: AVPlaybackStatus) => {
- if (data.isLoaded && data.isPlaying) {
- setPaused(false);
- } else {
- setPaused(true);
- }
- };
-
- const handlePlaybackStatusUpdate = (data: AVPlaybackStatus) => {
- if (data.isLoaded && data.durationMillis) {
- const durationSeconds = data.durationMillis / 1000;
- duration.value = durationSeconds > 0 ? durationSeconds : 0;
- const currentSecond = data.positionMillis / 1000;
- if (currentSecond <= durationSeconds) {
- currentTime.value = currentSecond;
- }
- setRate(data.rate);
- }
- };
-
- const onEnd = (data: AVPlaybackStatus) => {
- if (data.isLoaded) {
- if (data.didJustFinish) {
- try {
- setPaused(true);
- currentTime.value = 0;
- } catch {
- // do nothing
- }
- }
- }
- };
-
- const setPosition = async (time: number) => {
- await audioPlayer.setPositionAsync(audioUri.current, time);
- };
-
- const getUrl = () => {
- let url = file.audio_url;
- if (url && !url.startsWith('http')) {
- url = `${baseUrl}${file.audio_url}`;
- }
- return url;
- };
-
- const togglePlayPause = async () => {
- try {
- if (!paused) {
- await audioPlayer.pauseAudio(audioUri.current);
- } else {
- await audioPlayer.playAudio(audioUri.current);
- }
- } catch {
- // Do nothing
- }
- };
-
- const onChangeRate = async (value = 1.0) => {
- await audioPlayer.setRateAsync(audioUri.current, value);
- };
-
- const handleDownload = async () => {
- setLoading(true);
- try {
- const url = getUrl();
- if (url) {
- const audio = await downloadMediaFile({
- downloadUrl: `${url}?rc_uid=${user.id}&rc_token=${user.token}`,
- type: 'audio',
- mimeType: file.audio_type
- });
- await loadAudio(audio);
- setLoading(false);
- setCached(true);
- }
- } catch {
- setLoading(false);
- setCached(false);
- }
- };
-
- const handleAutoDownload = async () => {
- const url = getUrl();
- try {
- if (url) {
- const isCurrentUserAuthor = author?._id === user.id;
- const isAutoDownloadEnabled = fetchAutoDownloadEnabled('audioPreferenceDownload');
- if (isAutoDownloadEnabled || isCurrentUserAuthor) {
- await handleDownload();
- return;
- }
- setLoading(false);
- setCached(false);
- }
- } catch {
- // Do nothing
- }
- };
-
- const onPress = () => {
- if (loading) {
- return;
- }
- if (cached) {
- togglePlayPause();
- return;
- }
- handleDownload();
- };
-
- useEffect(() => {
- const handleCache = async () => {
- const cachedAudioResult = await getMediaCache({
- type: 'audio',
- mimeType: file.audio_type,
- urlToCache: getUrl()
- });
- if (cachedAudioResult?.exists) {
- await loadAudio(cachedAudioResult.uri);
- setLoading(false);
- setCached(true);
- return;
- }
- if (isReply) {
- setLoading(false);
- return;
- }
- await handleAutoDownload();
- };
- handleCache();
- }, []);
-
- useEffect(() => {
- if (paused) {
- deactivateKeepAwake();
- } else {
- activateKeepAwake();
- }
- }, [paused]);
if (!baseUrl) {
return null;
@@ -200,11 +25,7 @@ const MessageAudio = ({ file, getCustomEmoji, author, isReply, style, msg }: IMe
return (
<>
-
-
-
-
-
+
>
);
};