2016-10-20 21:38:01 +13:00
import React , { PropTypes } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
2018-10-24 21:34:41 +13:00
import { hashHistory } from 'react-router'
2017-01-04 15:24:13 +13:00
import ReactGA from 'react-ga'
2016-10-20 21:38:01 +13:00
2018-10-28 21:14:54 +13:00
import Link from '../components/Link' ;
2016-10-20 21:38:01 +13:00
import TrackList from '../components/TrackList'
2016-10-31 16:49:42 +13:00
import Thumbnail from '../components/Thumbnail'
2018-10-28 21:14:54 +13:00
import Parallax from '../components/Parallax'
2016-10-31 16:49:42 +13:00
import Dater from '../components/Dater'
2018-04-19 13:34:16 +12:00
import ConfirmationButton from '../components/Fields/ConfirmationButton'
2016-11-07 18:09:42 +13:00
import LazyLoadListener from '../components/LazyLoadListener'
2018-04-19 13:34:16 +12:00
import FollowButton from '../components/Fields/FollowButton'
2017-07-17 07:16:33 +12:00
import Header from '../components/Header'
2017-02-28 16:13:27 +13:00
import ContextMenuTrigger from '../components/ContextMenuTrigger'
2017-11-10 15:56:01 +13:00
import URILink from '../components/URILink'
2018-05-21 06:01:53 +12:00
import Icon from '../components/Icon'
2016-10-23 23:48:06 +13:00
2016-11-18 13:21:30 +13:00
import * as helpers from '../helpers'
2017-08-02 21:11:22 +12:00
import * as coreActions from '../services/core/actions'
2016-11-11 12:12:50 +13:00
import * as uiActions from '../services/ui/actions'
2016-10-21 09:56:07 +13:00
import * as mopidyActions from '../services/mopidy/actions'
2016-11-11 12:12:50 +13:00
import * as spotifyActions from '../services/spotify/actions'
2016-10-20 21:38:01 +13:00
2016-10-21 09:56:07 +13:00
class Playlist extends React . Component {
2016-10-20 21:38:01 +13:00
2017-10-10 13:05:28 +13:00
constructor ( props ) {
2016-10-20 21:38:01 +13:00
super ( props ) ;
}
2018-09-17 16:21:02 +12:00
componentWillMount ( ) {
var uri = this . props . params . uri ;
// Spotify upgraded their playlists URI to remove user component (Sept 2018)
// We accept the old format, and redirect to the new one
if ( uri . includes ( "spotify:user:" ) ) {
uri = uri . replace ( /spotify:user:([^:]*?):/i , "spotify:" ) ;
hashHistory . push ( global . baseURL + 'playlist/' + encodeURIComponent ( uri ) ) ;
}
}
2016-10-20 21:38:01 +13:00
componentDidMount ( ) {
2018-06-29 14:26:06 +12:00
this . setWindowTitle ( ) ;
2018-08-31 16:04:55 +12:00
this . props . coreActions . loadPlaylist ( this . props . params . uri ) ;
2016-10-21 09:56:07 +13:00
}
2017-10-10 13:05:28 +13:00
componentWillReceiveProps ( nextProps ) {
if ( nextProps . params . uri != this . props . params . uri ) {
2018-08-31 16:04:55 +12:00
this . props . coreActions . loadPlaylist ( nextProps . params . uri ) ;
2018-06-29 14:26:06 +12:00
} else if ( ! this . props . mopidy _connected && nextProps . mopidy _connected ) {
2017-10-26 20:36:27 +13:00
if ( helpers . uriSource ( this . props . params . uri ) != 'spotify' ) {
2018-08-31 16:04:55 +12:00
this . props . coreActions . loadPlaylist ( nextProps . params . uri ) ;
2016-11-01 11:49:04 +13:00
}
2016-10-21 09:56:07 +13:00
}
2018-06-29 14:26:06 +12:00
if ( ! this . props . playlist && nextProps . playlist ) {
this . setWindowTitle ( nextProps . playlist ) ;
}
if ( this . props . params . uri !== nextProps . params . uri && nextProps . playlist ) {
this . setWindowTitle ( nextProps . playlist ) ;
}
}
setWindowTitle ( playlist = this . props . playlist ) {
if ( playlist ) {
this . props . uiActions . setWindowTitle ( playlist . name + " (playlist)" ) ;
} else {
this . props . uiActions . setWindowTitle ( "Playlist" ) ;
}
2016-10-20 21:38:01 +13:00
}
2016-11-07 18:09:42 +13:00
loadMore ( ) {
2017-11-12 09:05:34 +13:00
this . props . spotifyActions . getMore (
2017-11-09 21:04:45 +13:00
this . props . playlist . tracks _more ,
{
parent _type : 'playlist' ,
parent _key : this . props . playlist . uri ,
records _type : 'track'
}
) ;
2016-11-07 18:09:42 +13:00
}
2018-04-01 21:26:42 +12:00
handleContextMenu ( e ) {
var data = {
e : e ,
context : ( this . props . playlist . can _edit ? 'editable-playlist' : 'playlist' ) ,
items : [ this . props . playlist ] ,
uris : [ this . props . params . uri ]
}
this . props . uiActions . showContextMenu ( data )
}
2016-11-11 09:33:35 +13:00
play ( ) {
2017-04-07 10:33:54 +12:00
this . props . mopidyActions . playPlaylist ( this . props . playlist . uri )
2016-11-11 09:33:35 +13:00
}
follow ( ) {
2018-06-05 13:43:02 +12:00
if ( this . props . allow _reporting ) {
ReactGA . event ( { category : 'Playlist' , action : 'Follow' , label : this . props . playlist . uri } ) ;
}
2017-05-07 09:29:18 +12:00
this . props . spotifyActions . toggleFollowingPlaylist ( this . props . playlist . uri , 'PUT' )
2016-11-11 09:33:35 +13:00
}
2016-11-14 21:02:46 +13:00
// TODO: Once unfollowing occurs, remove playlist from global playlists list
2016-11-11 09:33:35 +13:00
unfollow ( ) {
2018-06-05 13:43:02 +12:00
if ( this . props . allow _reporting ) {
ReactGA . event ( { category : 'Playlist' , action : 'Unfollow' , label : this . props . playlist . uri } ) ;
}
2017-10-10 13:05:28 +13:00
this . props . spotifyActions . toggleFollowingPlaylist ( this . props . playlist . uri , 'DELETE' )
2016-11-11 09:33:35 +13:00
}
2016-11-14 21:02:46 +13:00
// TODO: Once deletion occurs, remove playlist from global playlists list
2016-11-11 09:33:35 +13:00
delete ( ) {
2017-11-10 15:56:01 +13:00
this . props . mopidyActions . deletePlaylist ( this . props . playlist . uri ) ;
2016-11-11 09:33:35 +13:00
}
2017-10-10 13:05:28 +13:00
reorderTracks ( indexes , index ) {
2017-11-10 15:56:01 +13:00
this . props . coreActions . reorderPlaylistTracks ( this . props . playlist . uri , indexes , index , this . props . playlist . snapshot _id ) ;
2016-11-16 13:13:35 +13:00
}
2017-10-10 13:05:28 +13:00
removeTracks ( tracks _indexes ) {
2017-11-10 15:56:01 +13:00
this . props . coreActions . removeTracksFromPlaylist ( this . props . playlist . uri , tracks _indexes ) ;
2016-11-11 12:12:50 +13:00
}
2017-03-09 21:37:16 +13:00
inLibrary ( ) {
2017-11-10 15:56:01 +13:00
var library = helpers . uriSource ( this . props . params . uri ) + '_library_playlists' ;
return ( this . props [ library ] && this . props [ library ] . indexOf ( this . props . params . uri ) > - 1 ) ;
2017-03-09 21:37:16 +13:00
}
2017-02-08 11:12:53 +13:00
renderActions ( ) {
2018-08-31 16:04:55 +12:00
switch ( helpers . uriSource ( this . props . playlist . uri ) ) {
2016-11-11 09:33:35 +13:00
case 'm3u' :
2016-11-14 17:32:36 +13:00
return (
2017-02-08 11:12:53 +13:00
< div className = "actions" >
2018-12-05 21:39:06 +13:00
< button className = "button button--primary" onClick = { e => this . play ( ) } > Play < / b u t t o n >
< Link className = "button button--secondary" to = { global . baseURL + 'playlist/' + encodeURIComponent ( this . props . params . uri ) + '/edit' } > Edit < / L i n k >
2017-10-21 21:58:53 +13:00
< ContextMenuTrigger onTrigger = { e => this . handleContextMenu ( e ) } / >
2017-02-08 11:12:53 +13:00
< / d i v >
2016-11-14 17:32:36 +13:00
)
2016-11-11 09:33:35 +13:00
2016-11-14 11:20:02 +13:00
case 'spotify' :
2017-10-10 13:05:28 +13:00
if ( this . props . playlist . can _edit ) {
2016-11-14 17:32:36 +13:00
return (
2017-02-08 11:12:53 +13:00
< div className = "actions" >
2018-12-05 21:39:06 +13:00
< button className = "button button--primary" onClick = { e => this . play ( ) } > Play < / b u t t o n >
< Link className = "button button--secondary" to = { global . baseURL + 'playlist/' + encodeURIComponent ( this . props . params . uri ) + '/edit' } > Edit < / L i n k >
2017-10-21 21:58:53 +13:00
< ContextMenuTrigger onTrigger = { e => this . handleContextMenu ( e ) } / >
2017-02-08 11:12:53 +13:00
< / d i v >
2016-11-14 17:32:36 +13:00
)
2016-11-11 09:33:35 +13:00
}
2017-02-08 11:12:53 +13:00
return (
< div className = "actions" >
2018-12-05 21:39:06 +13:00
< button className = "button button--primary" onClick = { e => this . play ( ) } > Play < / b u t t o n >
< FollowButton className = "button--secondary" uri = { this . props . params . uri } addText = "Add to library" removeText = "Remove from library" is _following = { this . inLibrary ( ) } / >
2017-10-21 21:58:53 +13:00
< ContextMenuTrigger onTrigger = { e => this . handleContextMenu ( e ) } / >
2017-02-08 11:12:53 +13:00
< / d i v >
)
2016-11-11 09:33:35 +13:00
2017-02-08 11:12:53 +13:00
default :
return (
< div className = "actions" >
2018-12-05 21:39:06 +13:00
< button className = "button button--primary" onClick = { e => this . play ( ) } > Play < / b u t t o n >
2017-10-21 21:58:53 +13:00
< ContextMenuTrigger onTrigger = { e => this . handleContextMenu ( e ) } / >
2017-02-08 11:12:53 +13:00
< / d i v >
)
2016-11-11 09:33:35 +13:00
}
}
2016-11-01 13:19:51 +13:00
render ( ) {
2018-04-10 08:01:36 +12:00
var scheme = helpers . uriSource ( this . props . params . uri ) ;
var playlist _id = helpers . getFromUri ( 'playlistid' , this . props . params . uri ) ;
if ( ! this . props . playlist ) {
2018-09-14 11:58:13 +12:00
if ( helpers . isLoading ( this . props . load _queue , [ 'spotify_playlists/' + playlist _id + '?' ] ) ) {
2018-04-10 08:01:36 +12:00
return (
< div className = "body-loader loading" >
< div className = "loader" > < / d i v >
< / d i v >
)
} else {
return null ;
}
}
2018-08-31 16:04:55 +12:00
var playlist = helpers . collate ( this . props . playlist , { tracks : this . props . tracks , users : this . props . users } ) ;
2018-04-10 08:01:36 +12:00
var context = 'playlist' ;
2018-08-31 16:04:55 +12:00
if ( playlist . can _edit ) {
2018-04-10 08:01:36 +12:00
context = 'editable-playlist' ;
2017-05-22 04:19:44 +12:00
}
2016-11-01 13:19:51 +13:00
2018-09-18 16:59:10 +12:00
if ( ! playlist . tracks _uris || ( playlist . tracks _uris && ! playlist . tracks ) || ( playlist . tracks _uris . length !== playlist . tracks . length ) ) {
2018-04-14 13:48:03 +12:00
var is _loading _tracks = true ;
} else {
var is _loading _tracks = false ;
}
2017-06-27 09:37:33 +12:00
return (
2018-10-31 21:48:32 +13:00
< div className = "view playlist-view content-wrapper preserve-3d" >
2018-10-28 21:14:54 +13:00
< Parallax image = { playlist . images ? playlist . images . huge : null } blur / >
2017-06-27 09:37:33 +12:00
< div className = "thumbnail-wrapper" >
2018-08-31 16:04:55 +12:00
< Thumbnail size = "large" canZoom images = { playlist . images } / >
2017-02-23 22:06:43 +13:00
< / d i v >
2017-06-27 09:37:33 +12:00
< div className = "title" >
2018-08-31 16:04:55 +12:00
< h1 > { playlist . name } < / h 1 >
2018-10-13 15:57:01 +13:00
{ playlist . description ? < h2 className = "description mid_grey-text" dangerouslySetInnerHTML = { { _ _html : playlist . description } } > < / h 2 > : n u l l }
2017-06-27 09:37:33 +12:00
< ul className = "details" >
2018-09-21 11:58:54 +12:00
{ ! this . props . slim _mode ? < li className = "tooltip" > < Icon type = "fontawesome" name = { helpers . sourceIcon ( playlist . uri ) } / > < span className = "tooltip__content" > { helpers . uriSource ( playlist . uri ) } playlist < / s p a n > < / l i > : n u l l }
2017-06-27 09:37:33 +12:00
< li >
2018-08-31 16:04:55 +12:00
{ playlist . tracks _total ? playlist . tracks _total : ( playlist . tracks ? playlist . tracks . length : '0' ) } tracks , & nbsp ;
< Dater type = "total-time" data = { playlist . tracks } / >
2017-06-27 09:37:33 +12:00
< / l i >
2018-12-05 21:39:06 +13:00
{ ! this . props . slim _mode && playlist . followers !== undefined ? < li > { playlist . followers . toLocaleString ( ) } followers < / l i > : n u l l }
{ ! this . props . slim _mode && playlist . last _modified _date ? < li > Edited < Dater type = "ago" data = { playlist . last _modified _date } / > < / l i > : n u l l }
{ playlist . user _uri ? < li > < URILink type = "user" uri = { playlist . user _uri } > { playlist . user ? playlist . user . name : helpers . getFromUri ( 'userid' , playlist . user _uri ) } < / U R I L i n k > < / l i > : n u l l }
2017-06-27 09:37:33 +12:00
< / u l >
2017-02-23 22:06:43 +13:00
< / d i v >
2017-06-27 09:37:33 +12:00
2018-09-20 16:29:16 +12:00
{ this . renderActions ( ) }
2017-06-27 09:37:33 +12:00
< section className = "list-wrapper" >
2018-08-31 16:04:55 +12:00
< TrackList uri = { playlist . uri } className = "playlist-track-list" context = { context } tracks = { playlist . tracks } removeTracks = { tracks _indexes => this . removeTracks ( tracks _indexes ) } reorderTracks = { ( indexes , index ) => this . reorderTracks ( indexes , index ) } / >
2018-09-03 08:24:49 +12:00
< LazyLoadListener
loadKey = { playlist . tracks _more }
2018-09-20 16:29:16 +12:00
showLoader = { is _loading _tracks || playlist . tracks _more }
2018-09-03 08:24:49 +12:00
loadMore = { ( ) => this . loadMore ( ) }
/ >
2017-06-27 09:37:33 +12:00
< / s e c t i o n >
< / d i v >
)
2016-10-20 21:38:01 +13:00
}
}
/ * *
* Export our component
*
* We also integrate our global store , using connect ( )
* * /
const mapStateToProps = ( state , ownProps ) => {
2017-10-26 20:36:27 +13:00
var uri = ownProps . params . uri ;
2016-11-09 21:13:40 +13:00
return {
2018-06-05 13:43:02 +12:00
allow _reporting : state . ui . allow _reporting ,
2017-08-23 07:39:33 +12:00
slim _mode : state . ui . slim _mode ,
2017-05-22 04:19:44 +12:00
load _queue : state . ui . load _queue ,
2018-08-31 16:04:55 +12:00
users : state . core . users ,
2017-11-09 09:12:23 +13:00
tracks : state . core . tracks ,
2017-11-09 21:04:45 +13:00
playlist : ( state . core . playlists [ uri ] !== undefined ? state . core . playlists [ uri ] : false ) ,
2017-08-15 16:36:50 +12:00
spotify _library _playlists : state . spotify . library _playlists ,
local _library _playlists : state . mopidy . library _playlists ,
2016-11-11 09:33:35 +13:00
mopidy _connected : state . mopidy . connected ,
2017-08-04 22:33:49 +12:00
spotify _authorized : state . spotify . authorization ,
2018-09-14 11:58:13 +12:00
spotify _userid : ( state . spotify . me && state . spotify . me . id ? state . spotify . me . id : null )
2016-11-09 21:13:40 +13:00
}
2016-10-20 21:38:01 +13:00
}
const mapDispatchToProps = ( dispatch ) => {
return {
2017-08-02 21:11:22 +12:00
coreActions : bindActionCreators ( coreActions , dispatch ) ,
2016-11-11 12:12:50 +13:00
uiActions : bindActionCreators ( uiActions , dispatch ) ,
2016-10-20 21:38:01 +13:00
mopidyActions : bindActionCreators ( mopidyActions , dispatch ) ,
spotifyActions : bindActionCreators ( spotifyActions , dispatch )
}
}
2016-10-21 09:56:07 +13:00
export default connect ( mapStateToProps , mapDispatchToProps ) ( Playlist )