Rocket.Chat.ReactNative/app/containers/AudioPlayer/index.tsx

139 lines
3.4 KiB
TypeScript

import React, { useEffect, useRef, useState } from 'react';
import { View } from 'react-native';
import { AVPlaybackStatus } from 'expo-av';
import { activateKeepAwake, deactivateKeepAwake } from 'expo-keep-awake';
import { useSharedValue } from 'react-native-reanimated';
import { useTheme } from '../../theme';
import styles from './styles';
import Seek from './Seek';
import PlaybackSpeed from './PlaybackSpeed';
import PlayButton from './PlayButton';
import audioPlayer from '../../lib/methods/audioPlayer';
interface IAudioPlayerProps {
fileUri: string;
loading: boolean;
isReadyToPlay: boolean;
disabled?: boolean;
onPressCallback?: Function;
}
const AudioPlayer = ({
fileUri,
disabled = false,
loading = true,
isReadyToPlay = false,
onPressCallback = () => {}
}: IAudioPlayerProps) => {
const [paused, setPaused] = useState(true);
const [rate, setRate] = useState(1);
const duration = useSharedValue(0);
const currentTime = useSharedValue(0);
const { colors } = useTheme();
const audioUri = useRef<string>('');
const onPlaybackStatusUpdate = (status: AVPlaybackStatus) => {
if (status) {
onPlaying(status);
handlePlaybackStatusUpdate(status);
onEnd(status);
}
};
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 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 onPress = () => {
onPressCallback();
if (loading) {
return;
}
if (isReadyToPlay) {
togglePlayPause();
}
};
useEffect(() => {
const loadAudio = async (audio: string) => {
await audioPlayer.loadAudio(audio);
audioUri.current = audio;
audioPlayer.setOnPlaybackStatusUpdate(audio, onPlaybackStatusUpdate);
};
loadAudio(fileUri);
}, [fileUri]);
useEffect(() => {
if (paused) {
deactivateKeepAwake();
} else {
activateKeepAwake();
}
}, [paused]);
return (
<>
<View style={[styles.audioContainer, { backgroundColor: colors.surfaceTint, borderColor: colors.strokeExtraLight }]}>
<PlayButton disabled={disabled} loading={loading} paused={paused} isReadyToPlay={isReadyToPlay} onPress={onPress} />
<Seek currentTime={currentTime} duration={duration} loaded={!disabled && isReadyToPlay} onChangeTime={setPosition} />
<PlaybackSpeed onChange={onChangeRate} loaded={!disabled && isReadyToPlay} rate={rate} />
</View>
</>
);
};
export default AudioPlayer;