Creating sticky playback controls in foot

This commit is contained in:
James Barnsley
2017-06-16 09:37:40 +12:00
parent 5d0c365943
commit 911f07a806
17 changed files with 301 additions and 39 deletions

View File

@ -6,7 +6,7 @@ import { hashHistory, Link } from 'react-router'
import { connect } from 'react-redux'
import Sidebar from './components/Sidebar'
import MiniPlayer from './components/MiniPlayer'
import PlaybackControls from './components/PlaybackControls'
import SidebarToggleButton from './components/SidebarToggleButton'
import ContextMenu from './components/ContextMenu'
import Dragger from './components/Dragger'
@ -190,7 +190,7 @@ class App extends React.Component{
return (
<div className={className}>
<Sidebar />
<MiniPlayer />
<PlaybackControls />
<main>
{this.props.children}
</main>

View File

@ -27,8 +27,15 @@ class MiniPlayer extends React.Component{
}
render(){
var images = []
if (this.props.current_track && this.props.current_track.album && this.props.current_track.album.images){
images = this.props.current_track.album.images
}
return (
<div className="player mini-player">
<Thumbnail size="small" images={images} />
<div className="current-track">
<div className="title">{ this.props.current_track ? this.props.current_track.name : <span>-</span> }</div>

View File

@ -0,0 +1,131 @@
import React, { PropTypes } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import FontAwesome from 'react-fontawesome'
import ProgressSlider from './ProgressSlider'
import VolumeControl from './VolumeControl'
import Dater from './Dater'
import ArtistSentence from './ArtistSentence'
import Thumbnail from './Thumbnail'
import * as mopidyActions from '../services/mopidy/actions'
class PlaybackControls extends React.Component{
constructor(props) {
super(props);
}
renderPlayButton(){
var button = <a className="control play" onClick={() => this.props.mopidyActions.play()}><FontAwesome name="play" /> </a>
if( this.props.play_state == 'playing' ){
button = <a className="control play" onClick={() => this.props.mopidyActions.pause()}><FontAwesome name="pause" /> </a>
}
return button;
}
renderConsumeButton(){
var button = <a className="control has-tooltip" onClick={() => this.props.mopidyActions.instruct('tracklist.setConsume', [true])}><FontAwesome name="fire" /><span className="tooltip">Consume</span></a>
if( this.props.consume ){
button = <a className="control active has-tooltip" onClick={() => this.props.mopidyActions.instruct('tracklist.setConsume', [false])}><FontAwesome name="fire" /><span className="tooltip">Consume</span></a>
}
return button;
}
renderRandomButton(){
var button = <a className="control has-tooltip" onClick={() => this.props.mopidyActions.instruct('tracklist.setRandom', [true])}><FontAwesome name="random" /><span className="tooltip">Shuffle</span></a>
if( this.props.random ){
button = <a className="control active has-tooltip" onClick={() => this.props.mopidyActions.instruct('tracklist.setRandom', [false])}><FontAwesome name="random" /><span className="tooltip">Shuffle</span></a>
}
return button;
}
renderRepeatButton(){
var button = <a className="control has-tooltip" onClick={() => this.props.mopidyActions.instruct('tracklist.setRepeat', [true])}><FontAwesome name="repeat" /><span className="tooltip">Repeat</span></a>
if( this.props.repeat ){
button = <a className="control active has-tooltip" onClick={() => this.props.mopidyActions.instruct('tracklist.setRepeat', [false])}><FontAwesome name="repeat" /><span className="tooltip">Repeat</span></a>
}
return button;
}
render(){
var images = []
if (this.props.current_track && this.props.current_track.album && this.props.current_track.album.images){
images = this.props.current_track.album.images
}
return (
<div className="playback-controls">
<div className="current-track">
<Thumbnail size="small" images={images} />
<div className="title">
{ this.props.current_track ? this.props.current_track.name : <span>-</span> }
</div>
<div className="artist">
{ this.props.current_track ? <ArtistSentence artists={ this.props.current_track.artists } /> : <ArtistSentence /> }
</div>
</div>
<section className="playback">
<a className="control" onClick={() => this.props.mopidyActions.previous()}>
<FontAwesome name="step-backward" />
</a>
{ this.renderPlayButton() }
<a className="control" onClick={() => this.props.mopidyActions.stop()}>
<FontAwesome name="stop" />
</a>
<a className="control" onClick={() => this.props.mopidyActions.next()}>
<FontAwesome name="step-forward" />
</a>
</section>
<section className="settings">
{ this.renderConsumeButton() }
{ this.renderRandomButton() }
{ this.renderRepeatButton() }
</section>
<section className="progress">
<ProgressSlider />
<span className="current">{ this.props.time_position ? <Dater type="length" data={this.props.time_position} /> : '-' }</span>
<span className="total">{ this.props.current_track ? <Dater type="length" data={this.props.current_track.length} /> : '-' }</span>
</section>
<section className="volume">
<VolumeControl />
</section>
</div>
);
}
}
/**
* Export our component
*
* We also integrate our global store, using connect()
**/
const mapStateToProps = (state, ownProps) => {
return {
current_track: (typeof(state.ui.current_track) !== 'undefined' && typeof(state.ui.tracks) !== 'undefined' && typeof(state.ui.tracks[state.ui.current_track.uri]) !== 'undefined' ? state.ui.tracks[state.ui.current_track.uri] : null),
radio_enabled: (state.ui.radio && state.ui.radio.enabled ? true : false),
play_state: state.mopidy.play_state,
time_position: state.mopidy.time_position,
consume: state.mopidy.consume,
repeat: state.mopidy.repeat,
random: state.mopidy.random
}
}
const mapDispatchToProps = (dispatch) => {
return {
mopidyActions: bindActionCreators(mopidyActions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(PlaybackControls)

View File

@ -5,7 +5,6 @@ import { bindActionCreators } from 'redux'
import { Link } from 'react-router'
import Icon from './Icon'
import Thumbnail from './Thumbnail'
import SearchForm from './SearchForm'
import Dropzones from './Dropzones'
@ -21,8 +20,6 @@ class Sidebar extends React.Component{
render(){
return (
<aside>
{ this.props.current_track && this.props.current_track.album && this.props.current_track.album.images ? <Thumbnail size="large" images={this.props.current_track.album.images} /> : null }
<div className="liner">
@ -113,7 +110,6 @@ const mapStateToProps = (state, ownProps) => {
pusher_connected: state.pusher.connected,
spotify_connected: state.spotify.connected,
spotify_authorized: state.spotify.authorized,
current_track: (typeof(state.ui.current_track) !== 'undefined' && typeof(state.ui.tracks) !== 'undefined' && typeof(state.ui.tracks[state.ui.current_track.uri]) !== 'undefined' ? state.ui.tracks[state.ui.current_track.uri] : null),
dragger: state.ui.dragger
}
}

View File

@ -18,8 +18,8 @@ class VolumeControl extends React.Component{
var slider = e.target;
var sliderY = e.clientY - slider.getBoundingClientRect().top;
var sliderHeight = slider.getBoundingClientRect().height;
var percent = 100 - parseInt( sliderY / sliderHeight * 100 );
var sliderWidth = slider.getBoundingClientRect().width;
var percent = 100 - parseInt( sliderY / sliderWidth * 100 );
this.props.mopidyActions.setVolume( percent )
}
@ -33,14 +33,14 @@ class VolumeControl extends React.Component{
{this.props.mute ? <FontAwesome className="muted" name="volume-off" /> : <FontAwesome name="volume-down" />}
</a>
<span className="hover-trigger">
<a onClick={() => this.props.mopidyActions.setMute(!this.props.mute)}>
<span className="default">
<a className="control" onClick={() => this.props.mopidyActions.setMute(!this.props.mute)}>
{this.props.mute ? <FontAwesome className="muted" name="volume-off" /> : <FontAwesome name="volume-down" />}
</a>
<div className="slider-wrapper">
<div className={this.props.mute ? "slider vertical disabled" : "slider vertical"} onClick={ (e) => this.handleClick(e) } >
<div className={this.props.mute ? "slider horizontal disabled" : "slider horizontal"} onClick={ (e) => this.handleClick(e) } >
<div className="track">
<div className="progress" style={{ height: this.props.volume+'%' }}></div>
<div className="progress" style={{ width: this.props.volume+'%' }}></div>
</div>
</div>
</div>

View File

@ -51,7 +51,7 @@ class DiscoverFeatured extends React.Component{
<Thumbnail images={playlist.images} />
</Link>
<Link to={global.baseURL+'playlist/'+playlist.uri}>
<h1>{playlist.name}</h1>
<h2>{playlist.name}</h2>
</Link>
{playlist.description ? <h3 dangerouslySetInnerHTML={{__html: playlist.description}}></h3> : null}
<div className="actions">

View File

@ -57,7 +57,7 @@ class DiscoverNewReleases extends React.Component{
<Thumbnail images={album.images} />
</Link>
<Link to={global.baseURL+'album/'+album.uri}>
<h1>{album.name}</h1>
<h2>{album.name}</h2>
</Link>
<h3>
<ArtistSentence artists={album.artists} />

View File

@ -224,7 +224,7 @@ class Discover extends React.Component{
<div className="intro">
<Parallax image="/iris/assets/backgrounds/discover.jpg" />
<div className="liner">
<h1>Explore new music</h1>
<h2>Explore new music</h2>
<h3>
Add seeds below to build your sound. Let's start with
{this.props.authorized ? " two of your favorite artists" : " the chill genre"}.