created the slider with text

This commit is contained in:
Reinaldo Neto 2023-08-07 20:57:15 -03:00
parent 186ab338e4
commit f344c9e97f
3 changed files with 166 additions and 62 deletions

View File

@ -0,0 +1,98 @@
import React from 'react';
import { LayoutChangeEvent, View, TextInput } from 'react-native';
import { PanGestureHandler } from 'react-native-gesture-handler';
import Animated, {
useAnimatedGestureHandler,
useAnimatedProps,
useAnimatedStyle,
useDerivedValue,
useSharedValue
} from 'react-native-reanimated';
import styles from './styles';
import { useTheme } from '../../../../theme';
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput);
const Slider = ({ currentTime = 0, duration = 120, thumbColor = '' }) => {
console.log('🚀 ~ file: Slider.tsx:18 ~ Slider ~ currentTime:', currentTime);
const { colors } = useTheme();
const maxWidth = useSharedValue(1);
const x = useSharedValue(currentTime);
const current = useSharedValue('00:00');
const scale = useSharedValue(1);
const styleLine = useAnimatedStyle(() => ({
width: x.value,
zIndex: 2
}));
const styleThumb = useAnimatedStyle(() => ({
transform: [{ translateX: x.value }, { scale: scale.value }]
}));
const onLayout = (event: LayoutChangeEvent) => {
const { width } = event.nativeEvent.layout;
maxWidth.value = width - 12;
};
const gestureHandler = useAnimatedGestureHandler({
onStart: (_, ctx: any) => {
ctx.startX = x.value;
},
onActive: (event, ctx: any) => {
const moveInX: number = ctx.startX + event.translationX;
if (moveInX < 0) {
x.value = 0;
} else if (moveInX > maxWidth.value) {
x.value = maxWidth.value;
} else {
x.value = moveInX;
}
scale.value = 1.3;
},
onEnd: () => {
scale.value = 1;
// SEND A CALLBACK TO CHANGE THE PROGRESS OF THE AUDIO
}
});
useDerivedValue(() => {
const cTime = (x.value * duration) / maxWidth.value;
const minutes = Math.floor(cTime / 60);
const remainingSeconds = Math.floor(cTime % 60);
const formattedMinutes = String(minutes).padStart(2, '0');
const formattedSeconds = String(remainingSeconds).padStart(2, '0');
current.value = `${formattedMinutes}:${formattedSeconds}`;
}, [x, maxWidth, duration]);
const getCurrentTime = useAnimatedProps(
() =>
({
text: current.value
} as any),
[current]
);
return (
<View style={styles.sliderContainer}>
<AnimatedTextInput
defaultValue={'00:00'}
editable={false}
style={[styles.duration, { color: colors.bodyText }]}
animatedProps={getCurrentTime}
/>
<View style={styles.slider} onLayout={onLayout}>
<View style={[styles.line, { backgroundColor: colors.auxiliaryText }]} />
<Animated.View style={[styles.line, styleLine, { backgroundColor: colors.tintColor }]} />
<PanGestureHandler onGestureEvent={gestureHandler}>
<Animated.View style={[styles.thumbSlider, { backgroundColor: thumbColor }, styleThumb]} />
</PanGestureHandler>
</View>
</View>
);
};
export default Slider;

View File

@ -1,17 +1,13 @@
import React, { useContext, useEffect, useRef, useState } from 'react'; import React, { useContext, useEffect, useRef, useState } from 'react';
import { StyleProp, StyleSheet, Text, TextStyle, View, useWindowDimensions } from 'react-native'; import { StyleProp, TextStyle, View } from 'react-native';
import { Audio, AVPlaybackStatus, InterruptionModeAndroid, InterruptionModeIOS } from 'expo-av'; import { Audio, AVPlaybackStatus, InterruptionModeAndroid, InterruptionModeIOS } from 'expo-av';
import Slider from '@react-native-community/slider';
import moment from 'moment';
import { activateKeepAwakeAsync, deactivateKeepAwake } from 'expo-keep-awake'; import { activateKeepAwakeAsync, deactivateKeepAwake } from 'expo-keep-awake';
import { Sound } from 'expo-av/build/Audio/Sound'; import { Sound } from 'expo-av/build/Audio/Sound';
import Touchable from '../../Touchable'; import Touchable from '../../Touchable';
import Markdown from '../../../markdown'; import Markdown from '../../../markdown';
import { CustomIcon } from '../../../CustomIcon'; import { CustomIcon } from '../../../CustomIcon';
import sharedStyles from '../../../../views/Styles';
import { themes } from '../../../../lib/constants'; import { themes } from '../../../../lib/constants';
import { isAndroid, isIOS } from '../../../../lib/methods/helpers';
import MessageContext from '../../Context'; import MessageContext from '../../Context';
import ActivityIndicator from '../../../ActivityIndicator'; import ActivityIndicator from '../../../ActivityIndicator';
import { TGetCustomEmoji } from '../../../../definitions/IEmoji'; import { TGetCustomEmoji } from '../../../../definitions/IEmoji';
@ -21,6 +17,8 @@ import { downloadMediaFile, getMediaCache } from '../../../../lib/methods/handle
import EventEmitter from '../../../../lib/methods/helpers/events'; import EventEmitter from '../../../../lib/methods/helpers/events';
import { PAUSE_AUDIO } from '../../constants'; import { PAUSE_AUDIO } from '../../constants';
import { fetchAutoDownloadEnabled } from '../../../../lib/methods/autoDownloadPreference'; import { fetchAutoDownloadEnabled } from '../../../../lib/methods/autoDownloadPreference';
import styles from './styles';
import Slider from './Slider';
interface IButton { interface IButton {
loading: boolean; loading: boolean;
@ -48,36 +46,6 @@ const mode = {
interruptionModeAndroid: InterruptionModeAndroid.DoNotMix interruptionModeAndroid: InterruptionModeAndroid.DoNotMix
}; };
const styles = StyleSheet.create({
audioContainer: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
height: 56,
borderWidth: 1,
borderRadius: 4,
marginBottom: 6
},
playPauseButton: {
marginHorizontal: 10,
alignItems: 'center',
backgroundColor: 'transparent'
},
audioLoading: {
marginHorizontal: 8
},
slider: {
flex: 1
},
duration: {
marginHorizontal: 12,
fontSize: 14,
...sharedStyles.textRegular
}
});
const formatTime = (seconds: number) => moment.utc(seconds * 1000).format('mm:ss');
const BUTTON_HIT_SLOP = { top: 12, right: 12, bottom: 12, left: 12 }; const BUTTON_HIT_SLOP = { top: 12, right: 12, bottom: 12, left: 12 };
const Button = React.memo(({ loading, paused, onPress, disabled, cached }: IButton) => { const Button = React.memo(({ loading, paused, onPress, disabled, cached }: IButton) => {
@ -114,7 +82,6 @@ const MessageAudio = ({ file, getCustomEmoji, author, isReply, style }: IMessage
const [cached, setCached] = useState(false); const [cached, setCached] = useState(false);
const { baseUrl, user } = useContext(MessageContext); const { baseUrl, user } = useContext(MessageContext);
const { scale } = useWindowDimensions();
const { theme } = useTheme(); const { theme } = useTheme();
const sound = useRef<Sound | null>(null); const sound = useRef<Sound | null>(null);
@ -150,6 +117,7 @@ const MessageAudio = ({ file, getCustomEmoji, author, isReply, style }: IMessage
const onProgress = (data: AVPlaybackStatus) => { const onProgress = (data: AVPlaybackStatus) => {
if (data.isLoaded) { if (data.isLoaded) {
const currentTime = data.positionMillis / 1000; const currentTime = data.positionMillis / 1000;
console.log('🚀 ~ file: index.tsx:120 ~ onProgress ~ currentTime:', currentTime);
if (currentTime <= duration) { if (currentTime <= duration) {
setCurrentTime(currentTime); setCurrentTime(currentTime);
} }
@ -171,8 +139,6 @@ const MessageAudio = ({ file, getCustomEmoji, author, isReply, style }: IMessage
} }
}; };
const getDuration = () => formatTime(currentTime || duration);
const togglePlayPause = () => { const togglePlayPause = () => {
setPaused(!paused); setPaused(!paused);
playPause(!paused); playPause(!paused);
@ -240,15 +206,6 @@ const MessageAudio = ({ file, getCustomEmoji, author, isReply, style }: IMessage
handleDownload(); handleDownload();
}; };
const onValueChange = async (value: number) => {
try {
setCurrentTime(value);
await sound.current?.setPositionAsync(value * 1000);
} catch {
// Do nothing
}
};
useEffect(() => { useEffect(() => {
sound.current = new Audio.Sound(); sound.current = new Audio.Sound();
sound.current?.setOnPlaybackStatusUpdate(onPlaybackStatusUpdate); sound.current?.setOnPlaybackStatusUpdate(onPlaybackStatusUpdate);
@ -296,9 +253,9 @@ const MessageAudio = ({ file, getCustomEmoji, author, isReply, style }: IMessage
} }
let thumbColor; let thumbColor;
if (isAndroid && isReply) { if (isReply) {
thumbColor = themes[theme].tintDisabled; thumbColor = themes[theme].tintDisabled;
} else if (isAndroid) { } else {
thumbColor = themes[theme].tintColor; thumbColor = themes[theme].tintColor;
} }
@ -318,19 +275,8 @@ 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} />
<Text style={[styles.duration, { color: themes[theme].auxiliaryText }]}>{getDuration()}</Text> <Slider currentTime={currentTime} duration={duration} thumbColor={thumbColor} />
<Slider <View style={{ width: 36, height: 24, backgroundColor: '#999', borderRadius: 4, marginRight: 16 }} />
disabled={isReply}
style={styles.slider}
value={currentTime}
maximumValue={duration}
minimumValue={0}
thumbTintColor={thumbColor}
minimumTrackTintColor={themes[theme].tintColor}
maximumTrackTintColor={themes[theme].auxiliaryText}
onValueChange={onValueChange}
thumbImage={isIOS ? { uri: 'audio_thumb', scale } : undefined}
/>
</View> </View>
</> </>
); );

View File

@ -0,0 +1,60 @@
import { StyleSheet } from 'react-native';
import sharedStyles from '../../../../views/Styles';
const styles = StyleSheet.create({
audioContainer: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
height: 56,
borderWidth: 1,
borderRadius: 4,
marginBottom: 6
},
playPauseButton: {
alignItems: 'center',
backgroundColor: '#929',
marginLeft: 16,
height: 32,
width: 32,
borderRadius: 4,
justifyContent: 'center'
},
audioLoading: {
marginHorizontal: 8
},
sliderContainer: {
flexDirection: 'row',
flex: 1,
alignItems: 'center',
height: '100%'
},
slider: {
marginRight: 12,
height: '100%',
justifyContent: 'center',
flex: 1
},
line: {
height: 4,
borderRadius: 2,
zIndex: 1,
position: 'absolute',
width: '100%'
},
duration: {
width: 40,
marginHorizontal: 12,
fontSize: 14,
...sharedStyles.textRegular
},
thumbSlider: {
height: 12,
width: 12,
borderRadius: 6,
zIndex: 3
}
});
export default styles;