Removing col w-XX format in lists; Collapse lists on responsive; Context menu check asset ability to be in library

This commit is contained in:
James Barnsley
2017-03-12 16:19:06 +13:00
parent 4e1ecbaae7
commit f17055b93a
9 changed files with 344 additions and 206 deletions

View File

@ -17,8 +17,7 @@ class ContextMenu extends React.Component{
constructor(props) {
super(props)
this.state = {
submenu_expanded: false,
in_library: false
submenu_expanded: false
}
this.handleScroll = this.handleScroll.bind(this)
this.handleClick = this.handleClick.bind(this)
@ -41,6 +40,17 @@ class ContextMenu extends React.Component{
this.setState({ submenu_expanded: false })
$('body').addClass('context-menu-open')
// if we're able to be in the library, run a check
if (this.props.spotify_authorized && this.contextType().source == 'spotify'){
switch (nextProps.menu.context){
case 'artist':
case 'album':
case 'playlist':
this.props.spotifyActions.following(nextProps.menu.items[0].uri)
break
}
}
// we DID have one prior, and now we don't
} else if (this.props.menu && !nextProps.menu){
$('body').removeClass('context-menu-open')
@ -60,6 +70,35 @@ class ContextMenu extends React.Component{
}
}
contextType(){
var context = {
name: null,
label: 'Unknown'
}
if (this.props.menu && this.props.menu.context){
context.name = this.props.menu.context
context.label = this.props.menu.context
switch (this.props.menu.context){
case 'playlist':
case 'editable-playlist':
context.label = 'playlist'
break
case 'track':
case 'queue-track':
case 'playlist-track':
case 'editable-playlist-track':
context.label = 'track'
break
}
}
return context
}
inLibrary(){
if (!this.props.menu || !this.props.menu.context){
return false
@ -82,6 +121,14 @@ class ContextMenu extends React.Component{
return false
}
canBeInLibrary(){
if (!this.props.spotify_authorized){
return false
}
return (helpers.uriSource(this.props.menu.items[0].uri) == 'spotify')
}
toggleInLibrary(in_library){
this.props.uiActions.hideContextMenu()
if (in_library){
@ -117,10 +164,6 @@ class ContextMenu extends React.Component{
this.props.mopidyActions.enqueueURIsNext(this.props.menu.uris, this.props.menu.tracklist_uri)
}
addToPlaylist(){
this.setState({ submenu_expanded: !this.state.submenu_expanded })
}
addToQueue(){
this.props.uiActions.hideContextMenu()
this.props.mopidyActions.enqueueURIs(this.props.menu.uris, this.props.menu.tracklist_uri)
@ -208,79 +251,12 @@ class ContextMenu extends React.Component{
)
}
getItems(){
switch (this.props.menu.context) {
case 'album':
var items = [
{ handleClick: 'playURIs', label: 'Play' },
{ handleClick: 'playURIsNext', label: 'Play next' },
{ handleClick: 'addToQueue', label: 'Add to queue' },
{ handleClick: 'toggleInLibrary', label: 'Add to library' },
{ handleClick: 'goToArtist', label: 'Go to artist' },
{ handleClick: 'copyURIs', label: 'Copy URI' }
]
break
case 'artist':
var items = [
{ handleClick: 'startRadio', label: 'Start radio' },
{ handleClick: 'toggleInLibrary', label: 'Add to library' },
{ handleClick: 'copyURIs', label: 'Copy URI' }
]
break
case 'playlist':
var items = [
{ handleClick: 'playURIs', label: 'Play' },
{ handleClick: 'toggleInLibrary', label: 'Add to library' },
{ handleClick: 'goToUser', label: 'Go to user' },
{ handleClick: 'copyURIs', label: 'Copy URI' }
]
break
case 'queue':
var items = [
{ handleClick: 'playQueueItem', label: 'Play' },
{ handleClick: 'addToPlaylist', label: 'Add to playlist' },
{ handleClick: 'copyURIs', label: 'Copy URIs' },
{ handleClick: 'removeFromQueue', label: 'Remove' }
]
break
case 'editable-playlist-track':
var items = [
{ handleClick: 'playURIs', label: 'Play' },
{ handleClick: 'playURIsNext', label: 'Play next' },
{ handleClick: 'addToQueue', label: 'Add to queue' },
{ handleClick: 'addToPlaylist', label: 'Add to playlist' },
{ handleClick: 'startRadio', label: 'Start radio' },
{ handleClick: 'copyURIs', label: 'Copy URIs' },
{ handleClick: 'removeFromPlaylist', label: 'Remove' }
]
break
default:
var items = [
{ handleClick: 'playURIs', label: 'Play' },
{ handleClick: 'playURIsNext', label: 'Play next' },
{ handleClick: 'addToQueue', label: 'Add to queue' },
{ handleClick: 'addToPlaylist', label: 'Add to playlist' },
{ handleClick: 'startRadio', label: 'Start radio' },
{ handleClick: 'copyURIs', label: 'Copy URIs' }
]
break
}
return items
}
renderTitle(){
if (!this.props.menu.items || this.props.menu.items.length <= 0){
return null
}
switch (this.props.menu.context){
switch (this.contextType().name){
case 'artist':
case 'album':
@ -299,7 +275,7 @@ class ContextMenu extends React.Component{
<div className="type">
{helpers.uriSource(item.uri)}
&nbsp;
{this.props.menu.context}
{this.contextType().label}
</div>
<div className="text">{item.name}</div>
</Link>
@ -312,7 +288,7 @@ class ContextMenu extends React.Component{
<div className="type">
{helpers.uriSource(this.props.menu.items[0].uri)}
&nbsp;
{this.props.menu.context}s
{this.contextType().label}s
</div>
<div className="text">
{this.props.menu.items.length} items
@ -325,51 +301,180 @@ class ContextMenu extends React.Component{
}
renderItems(){
var items = this.getItems()
return (
<div>
{
items.map((item, index) => {
switch (item.handleClick){
case 'addToPlaylist':
return (
<span key={item.handleClick} className="menu-item-wrapper has-submenu">
<a className="menu-item" onClick={e => this[item.handleClick](e)}>
<span className="label">{ item.label }</span>
<FontAwesome className="submenu-icon" name='caret-right' />
</a>
{this.renderPlaylistSubmenu()}
</span>
)
break
case 'toggleInLibrary':
return (
<span className="menu-item-wrapper" key={item.handleClick}>
<a className="menu-item" onClick={e => this[item.handleClick](this.inLibrary())}>
<span className="label">
{this.inLibrary() ? 'Remove from library' : 'Add to library'}
</span>
</a>
</span>
)
break
default:
return (
<span className="menu-item-wrapper" key={item.handleClick}>
<a className="menu-item" onClick={e => this[item.handleClick](e)}>
<span className="label">{ item.label }</span>
</a>
</span>
)
}
})
}
</div>
var play_uris = (
<span className="menu-item-wrapper">
<a className="menu-item" onClick={e => this.playURIs(e)}>
<span className="label">Play</span>
</a>
</span>
)
var play_queue_item = (
<span className="menu-item-wrapper">
<a className="menu-item" onClick={e => this.playQueueItem(e)}>
<span className="label">Play</span>
</a>
</span>
)
var play_uris_next = (
<span className="menu-item-wrapper">
<a className="menu-item" onClick={e => this.playURIsNext(e)}>
<span className="label">Play next</span>
</a>
</span>
)
var add_to_queue = (
<span className="menu-item-wrapper">
<a className="menu-item" onClick={e => this.addToQueue(e)}>
<span className="label">Add to queue</span>
</a>
</span>
)
var add_to_playlist = (
<span className="menu-item-wrapper has-submenu">
<a className="menu-item" onClick={e => this.setState({ submenu_expanded: !this.state.submenu_expanded })}>
<span className="label">Add to playlist</span>
<FontAwesome className="submenu-icon" name='caret-right' />
</a>
{this.renderPlaylistSubmenu()}
</span>
)
var toggle_in_library = (
<span className="menu-item-wrapper">
<a className="menu-item" onClick={e => this.toggleInLibrary(this.inLibrary())}>
<span className="label">
{this.inLibrary() ? 'Remove from library' : 'Add to library'}
</span>
</a>
</span>
)
var go_to_artist = (
<span className="menu-item-wrapper">
<a className="menu-item" onClick={e => this.goToArtist(e)}>
<span className="label">Go to artist</span>
</a>
</span>
)
var go_to_user = (
<span className="menu-item-wrapper">
<a className="menu-item" onClick={e => this.goToUser(e)}>
<span className="label">Go to user</span>
</a>
</span>
)
var start_radio = (
<span className="menu-item-wrapper">
<a className="menu-item" onClick={e => this.startRadio(e)}>
<span className="label">Start {this.contextType().label} radio</span>
</a>
</span>
)
var remove_from_queue = (
<span className="menu-item-wrapper">
<a className="menu-item" onClick={e => this.removeFromQueue(e)}>
<span className="label">Remove</span>
</a>
</span>
)
var remove_from_playlist = (
<span className="menu-item-wrapper">
<a className="menu-item" onClick={e => this.removeFromPlaylist(e)}>
<span className="label">Remove</span>
</a>
</span>
)
var copy_uris = (
<span className="menu-item-wrapper">
<a className="menu-item" onClick={e => this.copyURIs(e)}>
<span className="label">Copy URIs</span>
</a>
</span>
)
switch (this.contextType().name){
case 'album':
return (
<div>
{play_uris}
{play_uris_next}
{add_to_queue}
{this.canBeInLibrary() ? toggle_in_library : null}
{go_to_artist}
{copy_uris}
</div>
)
break
case 'artist':
return (
<div>
{start_radio}
{this.canBeInLibrary() ? toggle_in_library : null}
{copy_uris}
</div>
)
break
case 'playlist':
return (
<div>
{play_uris}
{this.canBeInLibrary() ? toggle_in_library : null}
{go_to_user}
{copy_uris}
</div>
)
break
case 'queue':
return (
<div>
{play_queue_item}
{add_to_playlist}
{copy_uris}
{remove_from_queue}
</div>
)
break
case 'editable-playlist-track':
return (
<div>
{play_uris}
{play_uris_next}
{add_to_queue}
{add_to_playlist}
{start_radio}
{copy_uris}
{remove_from_playlist}
</div>
)
break
default:
return (
<div>
{play_uris}
{play_uris_next}
{add_to_queue}
{add_to_playlist}
{start_radio}
{copy_uris}
</div>
)
break
}
}
render(){
@ -379,11 +484,7 @@ class ContextMenu extends React.Component{
left: this.props.menu.position_x,
top: this.props.menu.position_y,
}
var items = this.getItems()
var height = 0
if (items) height = items.length * 34 // this is an approximation of how tall each menu item is
var height = 0 // TODO: use jquery to detect height
var className = "context-menu "+this.props.menu.context
if (this.state.submenu_expanded) className += ' submenu-expanded'
if (this.props.menu.position_x > (window.innerWidth - 154)) className += ' right-align'
@ -413,7 +514,8 @@ const mapStateToProps = (state, ownProps) => {
library_artists: state.ui.library_artists,
library_albums: state.ui.library_albums,
library_playlists: state.ui.library_playlists,
playlists: state.ui.playlists
playlists: state.ui.playlists,
spotify_authorized: state.spotify.authorized
}
}

View File

@ -39,7 +39,8 @@ class List extends React.Component{
<div className="list-item header cf">
{
this.props.columns.map( (col, col_index) => {
return <div className={'col w'+col.width} key={col_index}>{ col.label ? col.label : col.name }</div>
var className = 'col '+col.name.replace('.','_')
return <div className={className} key={col_index}>{ col.label ? col.label : col.name }</div>
})
}
</div>
@ -88,8 +89,9 @@ class List extends React.Component{
key={row_index}>
{
this.props.columns.map( (col, col_index) => {
var className = 'col '+col.name.replace('.','_')
return (
<div className={'col w'+col.width} key={col_index}>
<div className={className} key={col_index}>
{ this.renderValue(row, col.name) }
</div>
)

View File

@ -254,6 +254,7 @@ export default function reducer(ui = {}, action){
return Object.assign({}, ui, {
library_albums: [...library_albums, ...action.uris],
library_albums_started: true,
library_albums_more: action.more,
library_albums_total: action.total
});
@ -347,19 +348,12 @@ export default function reducer(ui = {}, action){
return Object.assign({}, ui, { artists: artists });
case 'LIBRARY_ARTISTS_LOADED':
if (!action.uris){
return Object.assign({}, ui, {
library_artists: null,
library_artists_more: null,
library_artists_total: null
});
}
var library_artists = []
if (ui.library_artists) library_artists = Object.assign([], ui.library_artists)
return Object.assign({}, ui, {
library_artists: [...library_artists, ...action.uris],
library_artists_started: true,
library_artists_more: action.more,
library_artists_total: action.total
});
@ -551,7 +545,8 @@ export default function reducer(ui = {}, action){
library_playlists = helpers.removeDuplicates(library_playlists)
return Object.assign({}, ui, {
library_playlists: library_playlists
library_playlists: library_playlists,
library_playlists_started: true
});
case 'PLAYLIST_LIBRARY_CHECK':

View File

@ -44,8 +44,6 @@ class App extends React.Component{
if (this.props.spotify_authorized){
this.props.spotifyActions.getAllLibraryPlaylists();
this.props.spotifyActions.getLibraryAlbums();
this.props.spotifyActions.getLibraryArtists();
}
// when we navigate to a new route

View File

@ -25,7 +25,9 @@ class LibraryAlbums extends React.Component{
}
componentDidMount(){
if (!this.props.library_albums) this.props.spotifyActions.getLibraryAlbums();
if (!this.props.library_albums_started){
this.props.spotifyActions.getLibraryAlbums();
}
}
handleContextMenu(e,item){
@ -33,7 +35,7 @@ class LibraryAlbums extends React.Component{
e: e,
context: 'album',
uris: [item.uri],
item: item
items: [item]
}
this.props.uiActions.showContextMenu(data)
}
@ -59,33 +61,28 @@ class LibraryAlbums extends React.Component{
if( this.props.view == 'list' ){
var columns = [
{
width: 30,
label: 'Name',
name: 'name'
},
{
width: 30,
label: 'Artists',
name: 'artists'
},
{
width: 15,
label: 'Added',
name: 'added_at'
},
{
width: 15,
label: 'Released',
name: 'release_date'
},
{
width: 10,
label: 'Tracks',
name: 'tracks_total'
}
]
return (
<section className="list-wrapper">
<section className="list-wrapper album-list">
<List
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
rows={albums}
@ -214,7 +211,8 @@ const mapStateToProps = (state, ownProps) => {
sort: state.ui.library_albums_sort,
sort_reverse: state.ui.library_albums_sort_reverse,
library_albums: state.ui.library_albums,
library_albums_more: state.ui.library_albums_more,
library_albums_started: state.ui.library_albums_started,
library_albums_more: state.ui.library_albums_more
}
}

View File

@ -26,13 +26,15 @@ class LibraryArtists extends React.Component{
e: e,
context: 'artist',
uris: [item.uri],
item: item
items: [item]
}
this.props.uiActions.showContextMenu(data)
}
componentDidMount(){
if (!this.props.library_artists) this.props.spotifyActions.getLibraryArtists();
if (!this.props.library_artists_started){
this.props.spotifyActions.getLibraryArtists();
}
}
loadMore(){
@ -55,22 +57,19 @@ class LibraryArtists extends React.Component{
var columns = [
{
label: 'Name',
width: 70,
name: 'name'
},
{
label: 'Followers',
width: 15,
name: 'followers.total'
},
{
label: 'Popularity',
width: 15,
name: 'popularity'
}
]
return (
<section className="list-wrapper">
<section className="list-wrapper artist-list">
<List
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
rows={artists}
@ -161,6 +160,7 @@ const mapStateToProps = (state, ownProps) => {
sort: state.ui.library_artists_sort,
sort_reverse: state.ui.library_artists_sort_reverse,
library_artists: state.ui.library_artists,
library_artists_started: state.ui.library_artists_started,
library_artists_more: state.ui.library_artists_more,
view: state.ui.library_artists_view
}

View File

@ -32,7 +32,7 @@ class LibraryLocalArtists extends React.Component{
e: e,
context: 'artist',
uris: [item.uri],
item: item
items: [item]
}
this.props.uiActions.showContextMenu(data)
}

View File

@ -26,7 +26,7 @@ class LibraryPlaylists extends React.Component{
e: e,
context: 'playlist',
uris: [item.uri],
item: item
items: [item]
}
this.props.uiActions.showContextMenu(data)
}
@ -60,33 +60,28 @@ class LibraryPlaylists extends React.Component{
if( this.props.view == 'list' ){
var columns = [
{
width: 50,
label: 'Name',
name: 'name'
},
{
width: 20,
label: 'Owner',
name: 'owner'
},
{
width: 10,
label: 'Source',
name: 'source'
},
{
width: 10,
label: 'Tracks',
name: 'tracks_total'
},
{
width: 10,
label: 'Editable',
name: 'can_edit'
}
]
return (
<section className="list-wrapper">
<section className="list-wrapper playlist-list">
<List
handleContextMenu={(e,item) => this.handleContextMenu(e,item)}
rows={playlists}

View File

@ -79,57 +79,105 @@
margin-top: -3px;
background: transparent;
}
@include responsive( $bp_medium ){
padding: 7px 10px 7px 20px;
.col {
&.name {
width: 90%;
padding-right: 10%;
}
&.duration {
width: auto;
position: absolute;
right: 10px;
text-align: right;
}
&.artists,
&.album {
width: auto;
color: $mid_grey;
display: inline-block;
}
&.album {
@include bullet();
}
}
&.header {
display: none;
}
.select-state,
.play-state {
top: 11px;
left: 5px;
}
}
}
&.playlist {
@include responsive( $bp_medium ){
padding: 7px 10px 7px 20px;
.col {
&.name {
width: 30%;
width: 90% !important;
padding-right: 10%;
}
&.owner {
width: 20%;
&.duration {
width: auto;
position: absolute;
right: 10px;
text-align: right;
}
&:not(.name){
color: $mid_grey;
display: inline-block;
width: auto !important;
}
&:not(.name):not(:nth-child(2)){
@include bullet();
}
&.followers_total:after {
content: ' followers';
}
&.tracks_total:after {
content: ' tracks';
}
&.popularity:after {
content: '% popularity';
}
&.can_edit,
&.release_date {
display: none;
}
}
&.header {
display: none;
}
.select-state,
.play-state {
top: 11px;
left: 5px;
}
}
}
&.playlist-list .list-item {
.col {
&.name {
width: 50%;
}
&.owner {
width: 20%
}
&.source,
&.can_edit,
&.tracks_total,
&.popularity {
width: 10%;
}
}
}
&.album-list .list-item {
.col {
&.name,
&.artists {
width: 30%;
}
&.added_at,
&.release_date {
width: 15%;
}
&.tracks_total {
width: 10%;
}
}
}
&.artist-list .list-item {
.col {
&.name {
width: 70%;
}
&.followers_total,
&.popularity {
width: 15%;
}
}
}