import React from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import Link from './Link'; import ProgressSlider from './Fields/ProgressSlider'; import VolumeControl from './Fields/VolumeControl'; import MuteControl from './Fields/MuteControl'; import OutputControl from './Fields/OutputControl'; import Dater from './Dater'; import LinksSentence from './LinksSentence'; import Thumbnail from './Thumbnail'; import Icon from './Icon'; import Stream from './Stream'; import { scrollTo } from '../util/helpers'; import * as uiActions from '../services/ui/actions'; import * as coreActions from '../services/core/actions'; import * as mopidyActions from '../services/mopidy/actions'; class PlaybackControls extends React.Component { constructor(props) { super(props); this.stream = null; this.state = { expanded: false, current_track: null, transition_track: null, transition_direction: null, }; } static getDerivedStateFromProps({ current_track }, state) { return { ...state, current_track, }; } handleTouchStart = (e) => { const { touch_enabled } = this.props; if (!touch_enabled) return; const timestamp = Math.floor(Date.now()); // Save touch start details this.start_time = timestamp; this.start_position = { x: e.touches[0].clientX, }; return false; } handleTouchEnd = (e) => { const { touch_enabled } = this.props; if (!touch_enabled) return; const timestamp = Math.floor(Date.now()); const tap_distance_threshold = 10; // Max distance (px) between touchstart and touchend to qualify as a tap const tap_time_threshold = 200; // Max time (ms) between touchstart and touchend to qualify as a tap const end_position = { x: e.changedTouches[0].clientX, }; // Too long between touchstart and touchend if (this.start_time + tap_time_threshold < timestamp) { return false; } // Make sure there's enough distance between start and end before we handle // this event as a 'tap' if (this.start_position.x + tap_distance_threshold > end_position.x && this.start_position.x - tap_distance_threshold < end_position.x) { // Scroll to top (without smooth_scroll) scrollTo(null, false); this.props.history.push('/queue'); } else { // Swipe to the left = previous track if (this.start_position.x < end_position.x) { this.setTransition('previous'); this.props.mopidyActions.previous(); // Swipe to the right = skip track } else if (this.start_position.x > end_position.x) { this.setTransition('next'); this.props.mopidyActions.next(); } } this.end_time = timestamp; e.preventDefault(); } setTransition(direction) { this.setState({ transition_track: this.state.current_track, transition_direction: direction, }); // Allow time for the animation to complete, then remove // the transitioning track from state setTimeout(() => { this.setState({ transition_track: null, }); }, 250); } renderPlayButton() { let button = ; if (this.props.play_state == 'playing') { button = ; } return button; } renderConsumeButton() { let button = ( ); if (this.props.consume) { button = ( ); } return button; } renderRandomButton() { let button = ( ); if (this.props.random) { button = ( ); } return button; } renderRepeatButton() { let button = ( ); if (this.props.repeat) { button = ( ); } return button; } render() { const { next_track, touch_enabled, time_position, } = this.props; const { current_track, expanded, transition_track, transition_direction, } = this.state; return (
{next_track && next_track.images ? : null}
{transition_track && transition_direction && (
{transition_track.name}
)} {current_track && (!transition_track || transition_track.tlid !== current_track.tlid) ? (
{current_track ? current_track.name : -}
{current_track ? : }
) : (
 
 
)}
{ this.renderPlayButton() }
{time_position ? : '-'}
{current_track ? : '-'}
{this.renderConsumeButton()} {this.renderRandomButton()} {this.renderRepeatButton()}
this.props.mopidyActions.setMute(mute)} /> this.props.mopidyActions.setVolume(percent)} />
); } } const mapStateToProps = (state) => ({ snapcast_enabled: state.pusher.config.snapcast_enabled, current_track: (state.core.current_track && state.core.tracks[state.core.current_track.uri] !== undefined ? state.core.tracks[state.core.current_track.uri] : null), next_track: (state.core.next_track_uri && state.core.tracks[state.core.next_track_uri] !== undefined ? state.core.tracks[state.core.next_track_uri] : null), radio_enabled: (!!(state.ui.radio && state.ui.radio.enabled)), play_state: state.mopidy.play_state, time_position: state.mopidy.time_position, consume: state.mopidy.consume, repeat: state.mopidy.repeat, random: state.mopidy.random, volume: state.mopidy.volume, mute: state.mopidy.mute, sidebar_open: state.ui.sidebar_open, slim_mode: state.ui.slim_mode, touch_enabled: state.ui.playback_controls_touch_enabled, }); const mapDispatchToProps = (dispatch) => ({ coreActions: bindActionCreators(coreActions, dispatch), uiActions: bindActionCreators(uiActions, dispatch), mopidyActions: bindActionCreators(mopidyActions, dispatch), }); export default connect(mapStateToProps, mapDispatchToProps)(PlaybackControls);