Add global unified state for audio files

This commit is contained in:
Danish Ahmed Mirza 2022-06-17 03:09:20 +05:30 committed by Danish
parent aa0b9f164e
commit 0f8920203d
4 changed files with 122 additions and 31 deletions

View File

@ -4,6 +4,8 @@ import moment from 'moment';
import { activateKeepAwake, deactivateKeepAwake } from 'expo-keep-awake'; import { activateKeepAwake, deactivateKeepAwake } from 'expo-keep-awake';
// import Slider from '@react-native-community/slider'; // import Slider from '@react-native-community/slider';
import TrackPlayer, { Event, useTrackPlayerEvents, State, useProgress } from 'react-native-track-player'; 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 Touchable from '../../Touchable';
import Markdown from '../../../markdown'; import Markdown from '../../../markdown';
@ -21,6 +23,7 @@ import { IAttachment } from '../../../../definitions';
import { TSupportedThemes } from '../../../../theme'; import { TSupportedThemes } from '../../../../theme';
import { setupService } from './services'; import { setupService } from './services';
import Slider from './Slider'; import Slider from './Slider';
import { TracksStorage, addTrack, clearTracks, getCurrentTrack, setCurrentTrack } from './tracks';
interface IButton { interface IButton {
loading: boolean; loading: boolean;
@ -106,40 +109,48 @@ IMessageAudioProps) => {
const { baseUrl, user } = useContext(MessageContext); const { baseUrl, user } = useContext(MessageContext);
const { position, duration } = useProgress(); const { duration } = useProgress();
useEffect(() => { const [tracks] = useMMKVStorage('tracks', TracksStorage);
setCurrentTime(position);
}, [position]);
useEffect(() => {
const setup = async () => {
setupService();
let url = file.audio_url; let url = file.audio_url;
if (url && !url.startsWith('http')) { if (url && !url.startsWith('http')) {
url = `${baseUrl}${file.audio_url}`; url = `${baseUrl}${file.audio_url}`;
} }
setLoading(true); const track = {
try { id: `${url}?rc_uid=${user.id}&rc_token=${user.token}`,
await TrackPlayer.add([
{
url: `${url}?rc_uid=${user.id}&rc_token=${user.token}`, url: `${url}?rc_uid=${user.id}&rc_token=${user.token}`,
title: file.title, title: file.title,
artist: file.author_name, artist: file.author_name,
duration duration
} };
]);
} catch { useEffect(() => {
// Do nothing const setup = async () => {
} setLoading(true);
await setupService();
addTrack({ trackId: track.id, title: file.title, artist: file.author_name, isPlaying: false });
setLoading(false);
}; };
setup(); setup();
return () => { return () => {
TrackPlayer.stop(); TrackPlayer.reset();
clearTracks();
}; };
}, []); }, []);
useEffect(() => {
const currentTrack = getCurrentTrack();
if (currentTrack && currentTrack.trackId !== track.id) {
setPaused(true);
}
}, [tracks]);
// useEffect(() => {
// setCurrentTime(position);
// }, [position]);
useEffect(() => { useEffect(() => {
playPause(); playPause();
}, [paused]); }, [paused]);
@ -161,7 +172,6 @@ IMessageAudioProps) => {
// do nothing // do nothing
} }
} }
if (state === State.Ready) setLoading(false);
}); });
const getDuration = () => formatTime(currentTime || duration); const getDuration = () => formatTime(currentTime || duration);
@ -171,11 +181,19 @@ IMessageAudioProps) => {
}; };
const playPause = () => { const playPause = () => {
const currentPlaying = getCurrentTrack();
try { try {
if (paused) { if (paused) {
if (currentPlaying?.trackId === track.id) {
TrackPlayer.pause(); TrackPlayer.pause();
} else { }
} else if (currentPlaying?.trackId === track.id) {
TrackPlayer.play(); TrackPlayer.play();
} else {
TrackPlayer.reset();
TrackPlayer.add(track);
TrackPlayer.play();
setCurrentTrack(track.id);
} }
} catch { } catch {
// Do nothing // Do nothing
@ -183,9 +201,17 @@ IMessageAudioProps) => {
}; };
const onValueChange = async (value: number) => { const onValueChange = async (value: number) => {
const currentTrack = getCurrentTrack();
try { try {
setCurrentTime(value); setCurrentTime(value);
if (currentTrack && currentTrack.trackId === track.id) {
await TrackPlayer.seekTo(value); await TrackPlayer.seekTo(value);
} else {
TrackPlayer.reset();
TrackPlayer.add(track);
TrackPlayer.seekTo(value);
setCurrentTrack(track.id);
}
} catch { } catch {
// Do nothing // Do nothing
} }
@ -204,6 +230,11 @@ IMessageAudioProps) => {
thumbColor = themes[theme].tintColor; thumbColor = themes[theme].tintColor;
} }
const sliderAnimatedConfig = {
duration: 250,
easing: Easing.linear
};
return ( return (
<> <>
<Markdown <Markdown
@ -228,6 +259,7 @@ IMessageAudioProps) => {
minimumTrackTintColor={themes[theme].tintColor} minimumTrackTintColor={themes[theme].tintColor}
disabled={isReply} disabled={isReply}
maximumTrackTintColor={themes[theme].auxiliaryText} maximumTrackTintColor={themes[theme].auxiliaryText}
animationConfig={sliderAnimatedConfig}
// thumbImage={isIOS ? { uri: 'audio_thumb', scale } : undefined} // thumbImage={isIOS ? { uri: 'audio_thumb', scale } : undefined}
/> />
{/* <Slider {/* <Slider

View File

@ -50,6 +50,8 @@ export const playbackService = async () => {
export const setupService = async () => { export const setupService = async () => {
try { try {
await TrackPlayer.getCurrentTrack();
} catch {
await TrackPlayer.setupPlayer(); await TrackPlayer.setupPlayer();
await TrackPlayer.updateOptions({ await TrackPlayer.updateOptions({
stopWithApp: false, stopWithApp: false,
@ -66,7 +68,5 @@ export const setupService = async () => {
// Capability.SkipToNext // Capability.SkipToNext
] ]
}); });
} catch {
// Do nothing
} }
}; };

View File

@ -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 });
}
};

View File

@ -173,7 +173,8 @@ export default StyleSheet.create({
marginLeft: 12 marginLeft: 12
}, },
sliderContainer: { sliderContainer: {
flex: 1 flex: 1,
paddingHorizontal: 10
}, },
track: { track: {
justifyContent: 'center' justifyContent: 'center'
@ -185,7 +186,7 @@ export default StyleSheet.create({
sliderThumb: { sliderThumb: {
height: SLIDER_THUMB_RADIUS * 2, height: SLIDER_THUMB_RADIUS * 2,
width: SLIDER_THUMB_RADIUS * 2, width: SLIDER_THUMB_RADIUS * 2,
borderRadius: SLIDER_THUMB_RADIUS, borderRadius: 2 * SLIDER_THUMB_RADIUS,
position: 'absolute', position: 'absolute',
zIndex: 2 zIndex: 2
} }