import React from 'react'; import PropTypes from 'prop-types'; import { View, StyleSheet, Text, Easing } from 'react-native'; import Video from 'react-native-video'; import Slider from 'react-native-slider'; import moment from 'moment'; import equal from 'deep-equal'; import Touchable from 'react-native-platform-touchable'; import Markdown from './Markdown'; import { CustomIcon } from '../../lib/Icons'; import sharedStyles from '../../views/Styles'; import { COLOR_BACKGROUND_CONTAINER, COLOR_BORDER, COLOR_PRIMARY } from '../../constants/colors'; const styles = StyleSheet.create({ audioContainer: { flex: 1, flexDirection: 'row', alignItems: 'center', height: 56, backgroundColor: COLOR_BACKGROUND_CONTAINER, borderColor: COLOR_BORDER, borderWidth: 1, borderRadius: 4, marginBottom: 6 }, playPauseButton: { marginHorizontal: 10, alignItems: 'center', backgroundColor: 'transparent' }, playPauseImage: { color: COLOR_PRIMARY }, slider: { flex: 1 }, duration: { marginHorizontal: 12, fontSize: 14, ...sharedStyles.textColorNormal, ...sharedStyles.textRegular }, thumbStyle: { width: 12, height: 12 }, trackStyle: { height: 2 } }); const formatTime = seconds => moment.utc(seconds * 1000).format('mm:ss'); const BUTTON_HIT_SLOP = { top: 12, right: 12, bottom: 12, left: 12 }; const sliderAnimationConfig = { duration: 250, easing: Easing.linear, delay: 0 }; const Button = React.memo(({ paused, onPress }) => ( <Touchable style={styles.playPauseButton} onPress={onPress} hitSlop={BUTTON_HIT_SLOP} background={Touchable.SelectableBackgroundBorderless()} > <CustomIcon name={paused ? 'play' : 'pause'} size={36} style={styles.playPauseImage} /> </Touchable> )); Button.propTypes = { paused: PropTypes.bool, onPress: PropTypes.func }; Button.displayName = 'MessageAudioButton'; export default class Audio extends React.Component { static propTypes = { file: PropTypes.object.isRequired, baseUrl: PropTypes.string.isRequired, user: PropTypes.object.isRequired, useMarkdown: PropTypes.bool, getCustomEmoji: PropTypes.func } constructor(props) { super(props); const { baseUrl, file, user } = props; this.state = { currentTime: 0, duration: 0, paused: true, uri: `${ baseUrl }${ file.audio_url }?rc_uid=${ user.id }&rc_token=${ user.token }` }; } shouldComponentUpdate(nextProps, nextState) { const { currentTime, duration, paused, uri } = this.state; const { file } = this.props; if (nextState.currentTime !== currentTime) { return true; } if (nextState.duration !== duration) { return true; } if (nextState.paused !== paused) { return true; } if (nextState.uri !== uri) { return true; } if (!equal(nextProps.file, file)) { return true; } return false; } onLoad = (data) => { this.setState({ duration: data.duration > 0 ? data.duration : 0 }); } onProgress = (data) => { const { duration } = this.state; if (data.currentTime <= duration) { this.setState({ currentTime: data.currentTime }); } } onEnd = () => { this.setState({ paused: true, currentTime: 0 }); requestAnimationFrame(() => { this.player.seek(0); }); } get duration() { const { duration } = this.state; return formatTime(duration); } setRef = ref => this.player = ref; togglePlayPause = () => { const { paused } = this.state; this.setState({ paused: !paused }); } onValueChange = value => this.setState({ currentTime: value }); render() { const { uri, paused, currentTime, duration } = this.state; const { user, baseUrl, file, getCustomEmoji, useMarkdown } = this.props; const { description } = file; if (!baseUrl) { return null; } return ( <React.Fragment> <View style={styles.audioContainer}> <Video ref={this.setRef} source={{ uri }} onLoad={this.onLoad} onProgress={this.onProgress} onEnd={this.onEnd} paused={paused} repeat={false} /> <Button paused={paused} onPress={this.togglePlayPause} /> <Slider style={styles.slider} value={currentTime} maximumValue={duration} minimumValue={0} animateTransitions animationConfig={sliderAnimationConfig} thumbTintColor={COLOR_PRIMARY} minimumTrackTintColor={COLOR_PRIMARY} onValueChange={this.onValueChange} thumbStyle={styles.thumbStyle} trackStyle={styles.trackStyle} /> <Text style={styles.duration}>{this.duration}</Text> </View> <Markdown msg={description} baseUrl={baseUrl} username={user.username} getCustomEmoji={getCustomEmoji} useMarkdown={useMarkdown} /> </React.Fragment> ); } }