play/pause button, currentTime equal the sound, can change the slider and reflect to the sound
This commit is contained in:
parent
f344c9e97f
commit
5bb14cb1e3
|
@ -1,27 +1,80 @@
|
||||||
import React from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { LayoutChangeEvent, View, TextInput } from 'react-native';
|
import { LayoutChangeEvent, View, TextInput } from 'react-native';
|
||||||
import { PanGestureHandler } from 'react-native-gesture-handler';
|
import { PanGestureHandler } from 'react-native-gesture-handler';
|
||||||
import Animated, {
|
import Animated, {
|
||||||
|
runOnJS,
|
||||||
useAnimatedGestureHandler,
|
useAnimatedGestureHandler,
|
||||||
useAnimatedProps,
|
useAnimatedProps,
|
||||||
useAnimatedStyle,
|
useAnimatedStyle,
|
||||||
useDerivedValue,
|
useDerivedValue,
|
||||||
useSharedValue
|
useSharedValue
|
||||||
} from 'react-native-reanimated';
|
} from 'react-native-reanimated';
|
||||||
|
import { Sound } from 'expo-av/build/Audio/Sound';
|
||||||
|
import { AVPlaybackStatus } from 'expo-av';
|
||||||
|
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { useTheme } from '../../../../theme';
|
import { useTheme } from '../../../../theme';
|
||||||
|
|
||||||
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput);
|
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput);
|
||||||
|
|
||||||
const Slider = ({ currentTime = 0, duration = 120, thumbColor = '' }) => {
|
interface ISlider {
|
||||||
console.log('🚀 ~ file: Slider.tsx:18 ~ Slider ~ currentTime:', currentTime);
|
currentTime: number;
|
||||||
|
duration: number;
|
||||||
|
thumbColor: string;
|
||||||
|
sound: Sound | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Slider = ({ thumbColor = '', sound }: ISlider) => {
|
||||||
|
const [loaded, setLoaded] = useState(false);
|
||||||
|
|
||||||
const { colors } = useTheme();
|
const { colors } = useTheme();
|
||||||
|
|
||||||
|
const duration = useSharedValue(0);
|
||||||
|
const currentTime = useSharedValue(0);
|
||||||
const maxWidth = useSharedValue(1);
|
const maxWidth = useSharedValue(1);
|
||||||
const x = useSharedValue(currentTime);
|
const x = useSharedValue(0);
|
||||||
const current = useSharedValue('00:00');
|
const current = useSharedValue('00:00');
|
||||||
const scale = useSharedValue(1);
|
const scale = useSharedValue(1);
|
||||||
|
const isHandlePan = useSharedValue(false);
|
||||||
|
const onEndGestureHandler = useSharedValue(false);
|
||||||
|
|
||||||
|
const onPlaybackStatusUpdate = (status: AVPlaybackStatus) => {
|
||||||
|
console.log('🚀 ~ file: Slider.tsx:42 ~ onPlaybackStatusUpdate ~ status:', status);
|
||||||
|
if (status) {
|
||||||
|
onLoad(status);
|
||||||
|
onProgress(status);
|
||||||
|
onEnd(status);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (sound) {
|
||||||
|
sound.setOnPlaybackStatusUpdate(onPlaybackStatusUpdate);
|
||||||
|
}
|
||||||
|
}, [onPlaybackStatusUpdate, sound]);
|
||||||
|
|
||||||
|
const onLoad = (data: AVPlaybackStatus) => {
|
||||||
|
if (data.isLoaded && data.durationMillis) {
|
||||||
|
const durationSeconds = data.durationMillis / 1000;
|
||||||
|
duration.value = durationSeconds > 0 ? durationSeconds : 0;
|
||||||
|
setLoaded(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onProgress = (data: AVPlaybackStatus) => {
|
||||||
|
if (data.isLoaded) {
|
||||||
|
const currentSecond = data.positionMillis / 1000;
|
||||||
|
if (currentSecond <= duration.value) {
|
||||||
|
console.log('🚀 ~ file: Slider.tsx:68 ~ onProgress ~ duration.value:', duration.value);
|
||||||
|
console.log('🚀 ~ file: Slider.tsx:70 ~ onProgress ~ currentTime.value:', currentTime.value);
|
||||||
|
currentTime.value = currentSecond;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onEnd = (data: AVPlaybackStatus) => {
|
||||||
|
// FIX HERE
|
||||||
|
};
|
||||||
|
|
||||||
const styleLine = useAnimatedStyle(() => ({
|
const styleLine = useAnimatedStyle(() => ({
|
||||||
width: x.value,
|
width: x.value,
|
||||||
|
@ -40,6 +93,7 @@ const Slider = ({ currentTime = 0, duration = 120, thumbColor = '' }) => {
|
||||||
const gestureHandler = useAnimatedGestureHandler({
|
const gestureHandler = useAnimatedGestureHandler({
|
||||||
onStart: (_, ctx: any) => {
|
onStart: (_, ctx: any) => {
|
||||||
ctx.startX = x.value;
|
ctx.startX = x.value;
|
||||||
|
isHandlePan.value = true;
|
||||||
},
|
},
|
||||||
onActive: (event, ctx: any) => {
|
onActive: (event, ctx: any) => {
|
||||||
const moveInX: number = ctx.startX + event.translationX;
|
const moveInX: number = ctx.startX + event.translationX;
|
||||||
|
@ -55,18 +109,41 @@ const Slider = ({ currentTime = 0, duration = 120, thumbColor = '' }) => {
|
||||||
},
|
},
|
||||||
onEnd: () => {
|
onEnd: () => {
|
||||||
scale.value = 1;
|
scale.value = 1;
|
||||||
|
isHandlePan.value = false;
|
||||||
|
onEndGestureHandler.value = true;
|
||||||
// SEND A CALLBACK TO CHANGE THE PROGRESS OF THE AUDIO
|
// SEND A CALLBACK TO CHANGE THE PROGRESS OF THE AUDIO
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const wrapper = async (time: number) => {
|
||||||
|
await sound?.setPositionAsync(Math.round(time * 1000));
|
||||||
|
onEndGestureHandler.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
useDerivedValue(() => {
|
useDerivedValue(() => {
|
||||||
const cTime = (x.value * duration) / maxWidth.value;
|
if (onEndGestureHandler.value) {
|
||||||
const minutes = Math.floor(cTime / 60);
|
runOnJS(wrapper)(currentTime.value);
|
||||||
const remainingSeconds = Math.floor(cTime % 60);
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useDerivedValue(() => {
|
||||||
|
let minutes;
|
||||||
|
let remainingSeconds;
|
||||||
|
if (isHandlePan.value) {
|
||||||
|
const cTime = (x.value * duration.value) / maxWidth.value || 0;
|
||||||
|
currentTime.value = cTime;
|
||||||
|
minutes = Math.floor(cTime / 60);
|
||||||
|
remainingSeconds = Math.floor(cTime % 60);
|
||||||
|
} else {
|
||||||
|
const xTime = (currentTime.value * maxWidth.value) / duration.value || 0;
|
||||||
|
x.value = xTime;
|
||||||
|
minutes = Math.floor(currentTime.value / 60);
|
||||||
|
remainingSeconds = Math.floor(currentTime.value % 60);
|
||||||
|
}
|
||||||
const formattedMinutes = String(minutes).padStart(2, '0');
|
const formattedMinutes = String(minutes).padStart(2, '0');
|
||||||
const formattedSeconds = String(remainingSeconds).padStart(2, '0');
|
const formattedSeconds = String(remainingSeconds).padStart(2, '0');
|
||||||
current.value = `${formattedMinutes}:${formattedSeconds}`;
|
current.value = `${formattedMinutes}:${formattedSeconds}`;
|
||||||
}, [x, maxWidth, duration]);
|
}, [x, maxWidth, duration, isHandlePan, currentTime]);
|
||||||
|
|
||||||
const getCurrentTime = useAnimatedProps(
|
const getCurrentTime = useAnimatedProps(
|
||||||
() =>
|
() =>
|
||||||
|
@ -87,7 +164,7 @@ const Slider = ({ currentTime = 0, duration = 120, thumbColor = '' }) => {
|
||||||
<View style={styles.slider} onLayout={onLayout}>
|
<View style={styles.slider} onLayout={onLayout}>
|
||||||
<View style={[styles.line, { backgroundColor: colors.auxiliaryText }]} />
|
<View style={[styles.line, { backgroundColor: colors.auxiliaryText }]} />
|
||||||
<Animated.View style={[styles.line, styleLine, { backgroundColor: colors.tintColor }]} />
|
<Animated.View style={[styles.line, styleLine, { backgroundColor: colors.tintColor }]} />
|
||||||
<PanGestureHandler onGestureEvent={gestureHandler}>
|
<PanGestureHandler enabled={loaded} onGestureEvent={gestureHandler}>
|
||||||
<Animated.View style={[styles.thumbSlider, { backgroundColor: thumbColor }, styleThumb]} />
|
<Animated.View style={[styles.thumbSlider, { backgroundColor: thumbColor }, styleThumb]} />
|
||||||
</PanGestureHandler>
|
</PanGestureHandler>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -77,7 +77,6 @@ Button.displayName = 'MessageAudioButton';
|
||||||
const MessageAudio = ({ file, getCustomEmoji, author, isReply, style }: IMessageAudioProps) => {
|
const MessageAudio = ({ file, getCustomEmoji, author, isReply, style }: IMessageAudioProps) => {
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [currentTime, setCurrentTime] = useState(0);
|
const [currentTime, setCurrentTime] = useState(0);
|
||||||
const [duration, setDuration] = useState(0);
|
|
||||||
const [paused, setPaused] = useState(true);
|
const [paused, setPaused] = useState(true);
|
||||||
const [cached, setCached] = useState(false);
|
const [cached, setCached] = useState(false);
|
||||||
|
|
||||||
|
@ -93,8 +92,8 @@ const MessageAudio = ({ file, getCustomEmoji, author, isReply, style }: IMessage
|
||||||
|
|
||||||
const onPlaybackStatusUpdate = (status: AVPlaybackStatus) => {
|
const onPlaybackStatusUpdate = (status: AVPlaybackStatus) => {
|
||||||
if (status) {
|
if (status) {
|
||||||
onLoad(status);
|
// onLoad(status);
|
||||||
onProgress(status);
|
// onProgress(status);
|
||||||
onEnd(status);
|
onEnd(status);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -107,23 +106,6 @@ const MessageAudio = ({ file, getCustomEmoji, author, isReply, style }: IMessage
|
||||||
return url;
|
return url;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onLoad = (data: AVPlaybackStatus) => {
|
|
||||||
if (data.isLoaded && data.durationMillis) {
|
|
||||||
const duration = data.durationMillis / 1000;
|
|
||||||
setDuration(duration > 0 ? duration : 0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onProgress = (data: AVPlaybackStatus) => {
|
|
||||||
if (data.isLoaded) {
|
|
||||||
const currentTime = data.positionMillis / 1000;
|
|
||||||
console.log('🚀 ~ file: index.tsx:120 ~ onProgress ~ currentTime:', currentTime);
|
|
||||||
if (currentTime <= duration) {
|
|
||||||
setCurrentTime(currentTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onEnd = async (data: AVPlaybackStatus) => {
|
const onEnd = async (data: AVPlaybackStatus) => {
|
||||||
if (data.isLoaded) {
|
if (data.isLoaded) {
|
||||||
if (data.didJustFinish) {
|
if (data.didJustFinish) {
|
||||||
|
@ -275,11 +257,11 @@ const MessageAudio = ({ file, getCustomEmoji, author, isReply, style }: IMessage
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Button disabled={isReply} loading={loading} paused={paused} cached={cached} onPress={onPress} />
|
<Button disabled={isReply} loading={loading} paused={paused} cached={cached} onPress={onPress} />
|
||||||
<Slider currentTime={currentTime} duration={duration} thumbColor={thumbColor} />
|
<Slider sound={sound.current} currentTime={currentTime} thumbColor={thumbColor} />
|
||||||
<View style={{ width: 36, height: 24, backgroundColor: '#999', borderRadius: 4, marginRight: 16 }} />
|
<View style={{ width: 36, height: 24, backgroundColor: '#999', borderRadius: 4, marginRight: 16 }} />
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};;
|
||||||
|
|
||||||
export default MessageAudio;
|
export default MessageAudio;
|
||||||
|
|
Loading…
Reference in New Issue