Add global unified state for audio files
This commit is contained in:
parent
aa0b9f164e
commit
0f8920203d
|
@ -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
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 });
|
||||||
|
}
|
||||||
|
};
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue