diff --git a/app/containers/message/Components/Audio/index.tsx b/app/containers/message/Components/Audio/index.tsx index 56d0ad867..a35f328ac 100644 --- a/app/containers/message/Components/Audio/index.tsx +++ b/app/containers/message/Components/Audio/index.tsx @@ -4,6 +4,8 @@ import moment from 'moment'; import { activateKeepAwake, deactivateKeepAwake } from 'expo-keep-awake'; // import Slider from '@react-native-community/slider'; import TrackPlayer, { Event, useTrackPlayerEvents, State, useProgress } from 'react-native-track-player'; +import { Easing } from 'react-native-reanimated'; +import { useMMKVStorage } from 'react-native-mmkv-storage'; import Touchable from '../../Touchable'; import Markdown from '../../../markdown'; @@ -21,6 +23,7 @@ import { IAttachment } from '../../../../definitions'; import { TSupportedThemes } from '../../../../theme'; import { setupService } from './services'; import Slider from './Slider'; +import { TracksStorage, addTrack, clearTracks, getCurrentTrack, setCurrentTrack } from './tracks'; interface IButton { loading: boolean; @@ -106,40 +109,48 @@ IMessageAudioProps) => { const { baseUrl, user } = useContext(MessageContext); - const { position, duration } = useProgress(); + const { duration } = useProgress(); - useEffect(() => { - setCurrentTime(position); - }, [position]); + const [tracks] = useMMKVStorage('tracks', TracksStorage); + + let url = file.audio_url; + if (url && !url.startsWith('http')) { + url = `${baseUrl}${file.audio_url}`; + } + + const track = { + id: `${url}?rc_uid=${user.id}&rc_token=${user.token}`, + url: `${url}?rc_uid=${user.id}&rc_token=${user.token}`, + title: file.title, + artist: file.author_name, + duration + }; useEffect(() => { const setup = async () => { - setupService(); - let url = file.audio_url; - if (url && !url.startsWith('http')) { - url = `${baseUrl}${file.audio_url}`; - } - setLoading(true); - try { - await TrackPlayer.add([ - { - url: `${url}?rc_uid=${user.id}&rc_token=${user.token}`, - title: file.title, - artist: file.author_name, - duration - } - ]); - } catch { - // Do nothing - } + await setupService(); + addTrack({ trackId: track.id, title: file.title, artist: file.author_name, isPlaying: false }); + setLoading(false); }; setup(); return () => { - TrackPlayer.stop(); + TrackPlayer.reset(); + clearTracks(); }; }, []); + useEffect(() => { + const currentTrack = getCurrentTrack(); + if (currentTrack && currentTrack.trackId !== track.id) { + setPaused(true); + } + }, [tracks]); + + // useEffect(() => { + // setCurrentTime(position); + // }, [position]); + useEffect(() => { playPause(); }, [paused]); @@ -161,7 +172,6 @@ IMessageAudioProps) => { // do nothing } } - if (state === State.Ready) setLoading(false); }); const getDuration = () => formatTime(currentTime || duration); @@ -171,11 +181,19 @@ IMessageAudioProps) => { }; const playPause = () => { + const currentPlaying = getCurrentTrack(); try { if (paused) { - TrackPlayer.pause(); - } else { + if (currentPlaying?.trackId === track.id) { + TrackPlayer.pause(); + } + } else if (currentPlaying?.trackId === track.id) { TrackPlayer.play(); + } else { + TrackPlayer.reset(); + TrackPlayer.add(track); + TrackPlayer.play(); + setCurrentTrack(track.id); } } catch { // Do nothing @@ -183,9 +201,17 @@ IMessageAudioProps) => { }; const onValueChange = async (value: number) => { + const currentTrack = getCurrentTrack(); try { setCurrentTime(value); - await TrackPlayer.seekTo(value); + if (currentTrack && currentTrack.trackId === track.id) { + await TrackPlayer.seekTo(value); + } else { + TrackPlayer.reset(); + TrackPlayer.add(track); + TrackPlayer.seekTo(value); + setCurrentTrack(track.id); + } } catch { // Do nothing } @@ -204,6 +230,11 @@ IMessageAudioProps) => { thumbColor = themes[theme].tintColor; } + const sliderAnimatedConfig = { + duration: 250, + easing: Easing.linear + }; + return ( <> { minimumTrackTintColor={themes[theme].tintColor} disabled={isReply} maximumTrackTintColor={themes[theme].auxiliaryText} + animationConfig={sliderAnimatedConfig} // thumbImage={isIOS ? { uri: 'audio_thumb', scale } : undefined} /> {/* { export const setupService = async () => { try { + await TrackPlayer.getCurrentTrack(); + } catch { await TrackPlayer.setupPlayer(); await TrackPlayer.updateOptions({ stopWithApp: false, @@ -66,7 +68,5 @@ export const setupService = async () => { // Capability.SkipToNext ] }); - } catch { - // Do nothing } }; diff --git a/app/containers/message/Components/Audio/tracks.ts b/app/containers/message/Components/Audio/tracks.ts new file mode 100644 index 000000000..4355d4b57 --- /dev/null +++ b/app/containers/message/Components/Audio/tracks.ts @@ -0,0 +1,58 @@ +import MMKVStorage from 'react-native-mmkv-storage'; + +export const TracksStorage = new MMKVStorage.Loader().withInstanceID('tracks').initialize(); + +interface Track { + trackId: string; + isPlaying: boolean; + title?: string; + artist?: string; +} + +export const initializeTracks = () => { + const tracks = TracksStorage.getArray('tracks'); + if (!tracks) TracksStorage.setArray('tracks', []); +}; + +export const addTrack = (track: Track) => { + initializeTracks(); + const tracks: Track[] = TracksStorage.getArray('tracks') || []; + if (tracks.find((t: Track) => t.trackId === track.trackId)) { + return; + } + TracksStorage.setArray('tracks', [...tracks, track]); +}; + +export const getTrack = (track: Track) => { + const tracks: Track[] = TracksStorage.getArray('tracks') || []; + return tracks.find((t: Track) => t.trackId === track.trackId); +}; + +export const updateTrack = (track: Track) => { + const tracks: Track[] = TracksStorage.getArray('tracks') || []; + const index = tracks.findIndex((t: Track) => t.trackId === track.trackId); + if (index !== -1) { + tracks[index] = track; + } + TracksStorage.setArray('tracks', tracks); +}; + +export const clearTracks = () => { + TracksStorage.setArray('tracks', []); +}; + +export const getCurrentTrack: () => Track | undefined = () => { + const tracks: Track[] = TracksStorage.getArray('tracks') || []; + const currentTrack = tracks.find((t: Track) => t.isPlaying); + return currentTrack; +}; + +export const setCurrentTrack = (trackId: string) => { + const tracks: Track[] = TracksStorage.getArray('tracks') || []; + const currentTrack = tracks.find((t: Track) => t.isPlaying); + const trackToToggle = tracks.find((t: Track) => t.trackId === trackId); + currentTrack && updateTrack({ ...currentTrack, isPlaying: false }); + if (trackToToggle) { + updateTrack({ ...trackToToggle, isPlaying: true }); + } +}; diff --git a/app/containers/message/styles.ts b/app/containers/message/styles.ts index 50eda1524..0f0d6cb74 100644 --- a/app/containers/message/styles.ts +++ b/app/containers/message/styles.ts @@ -173,7 +173,8 @@ export default StyleSheet.create({ marginLeft: 12 }, sliderContainer: { - flex: 1 + flex: 1, + paddingHorizontal: 10 }, track: { justifyContent: 'center' @@ -185,7 +186,7 @@ export default StyleSheet.create({ sliderThumb: { height: SLIDER_THUMB_RADIUS * 2, width: SLIDER_THUMB_RADIUS * 2, - borderRadius: SLIDER_THUMB_RADIUS, + borderRadius: 2 * SLIDER_THUMB_RADIUS, position: 'absolute', zIndex: 2 }