Context menus on all key grid and list views
This commit is contained in:
@ -22,9 +22,12 @@ class AlbumGrid extends React.Component{
|
||||
}
|
||||
}
|
||||
|
||||
handleContextMenu(e,uri){
|
||||
handleContextMenu(e,item){
|
||||
e.preventDefault()
|
||||
var data = { uris: [uri] }
|
||||
var data = {
|
||||
uris: [item.uri],
|
||||
item: item
|
||||
}
|
||||
this.props.uiActions.showContextMenu( e, data, 'album', 'click' )
|
||||
}
|
||||
|
||||
@ -41,7 +44,7 @@ class AlbumGrid extends React.Component{
|
||||
<div className="grid-item"
|
||||
key={index}
|
||||
onClick={ (e) => this.handleClick(e,global.baseURL+'album/'+album.uri) }
|
||||
onContextMenu={e => this.handleContextMenu(e,album.uri)}>
|
||||
onContextMenu={e => this.handleContextMenu(e,album)}>
|
||||
<Thumbnail size="medium" images={album.images} />
|
||||
<div className="name">{ album.name }</div>
|
||||
<div className="secondary">
|
||||
|
||||
@ -14,9 +14,12 @@ class ArtistGrid extends React.Component{
|
||||
super(props);
|
||||
}
|
||||
|
||||
handleContextMenu(e,uri){
|
||||
handleContextMenu(e,item){
|
||||
e.preventDefault()
|
||||
var data = { uris: [uri] }
|
||||
var data = {
|
||||
uris: [item.uri],
|
||||
item: item
|
||||
}
|
||||
this.props.uiActions.showContextMenu( e, data, 'artist', 'click' )
|
||||
}
|
||||
|
||||
@ -34,7 +37,7 @@ class ArtistGrid extends React.Component{
|
||||
className="grid-item"
|
||||
to={global.baseURL+'artist/'+artist.uri}
|
||||
key={index}
|
||||
onContextMenu={e => this.handleContextMenu(e,artist.uri)}>
|
||||
onContextMenu={e => this.handleContextMenu(e,artist)}>
|
||||
<Thumbnail size="medium" images={artist.images} />
|
||||
<div className="name">{ artist.name }</div>
|
||||
<div className="secondary">
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import { Link } from 'react-router'
|
||||
import { connect } from 'react-redux'
|
||||
import { bindActionCreators } from 'redux'
|
||||
import FontAwesome from 'react-fontawesome'
|
||||
@ -7,6 +8,7 @@ import FontAwesome from 'react-fontawesome'
|
||||
import TrackList from './TrackList'
|
||||
import * as helpers from '../helpers'
|
||||
import * as uiActions from '../services/ui/actions'
|
||||
import * as pusherActions from '../services/pusher/actions'
|
||||
import * as mopidyActions from '../services/mopidy/actions'
|
||||
import * as spotifyActions from '../services/spotify/actions'
|
||||
|
||||
@ -86,6 +88,11 @@ class ContextMenu extends React.Component{
|
||||
this.props.uiActions.hideContextMenu();
|
||||
}
|
||||
|
||||
startRadio(){
|
||||
this.props.pusherActions.startRadio(this.props.context_menu.data.uris)
|
||||
this.props.uiActions.hideContextMenu();
|
||||
}
|
||||
|
||||
copyURIs(e){
|
||||
var temp = $("<input>");
|
||||
$("body").append(temp);
|
||||
@ -126,7 +133,7 @@ class ContextMenu extends React.Component{
|
||||
)
|
||||
}
|
||||
|
||||
getItems(trigger){
|
||||
getItems(){
|
||||
switch (this.props.context_menu.context) {
|
||||
|
||||
case 'queue':
|
||||
@ -138,22 +145,12 @@ class ContextMenu extends React.Component{
|
||||
]
|
||||
break
|
||||
|
||||
case 'editable-playlist':
|
||||
var items = [
|
||||
{ handleClick: 'playURIs', label: 'Play', icon: 'play' },
|
||||
{ handleClick: 'playURIsNext', label: 'Play next', icon: 'play' },
|
||||
{ handleClick: 'addToQueue', label: 'Add to queue', icon: 'plus' },
|
||||
{ handleClick: 'addToPlaylist', label: 'Add to playlist', icon: 'plus', playlists: true },
|
||||
{ handleClick: 'copyURIs', label: 'Copy URIs', icon: 'copy' },
|
||||
{ handleClick: 'removeFromPlaylist', label: 'Remove', icon: 'trash' }
|
||||
]
|
||||
break
|
||||
|
||||
case 'album':
|
||||
var items = [
|
||||
{ handleClick: 'playURIs', label: 'Play', icon: 'play' },
|
||||
{ handleClick: 'playURIsNext', label: 'Play next', icon: 'play' },
|
||||
{ handleClick: 'addToQueue', label: 'Add to queue', icon: 'plus' },
|
||||
{ handleClick: 'startRadio', label: 'Start radio', icon: 'spotify' },
|
||||
{ handleClick: 'copyURIs', label: 'Copy URI', icon: 'copy' }
|
||||
]
|
||||
break
|
||||
@ -174,12 +171,25 @@ class ContextMenu extends React.Component{
|
||||
]
|
||||
break
|
||||
|
||||
case 'editable-playlist-track':
|
||||
var items = [
|
||||
{ handleClick: 'playURIs', label: 'Play', icon: 'play' },
|
||||
{ handleClick: 'playURIsNext', label: 'Play next', icon: 'play' },
|
||||
{ handleClick: 'addToQueue', label: 'Add to queue', icon: 'plus' },
|
||||
{ handleClick: 'addToPlaylist', label: 'Add to playlist', icon: 'plus', playlists: true },
|
||||
{ handleClick: 'startRadio', label: 'Start radio', icon: 'spotify' },
|
||||
{ handleClick: 'copyURIs', label: 'Copy URIs', icon: 'copy' },
|
||||
{ handleClick: 'removeFromPlaylist', label: 'Remove', icon: 'trash' }
|
||||
]
|
||||
break
|
||||
|
||||
default:
|
||||
var items = [
|
||||
{ handleClick: 'playURIs', label: 'Play', icon: 'play' },
|
||||
{ handleClick: 'playURIsNext', label: 'Play next', icon: 'play' },
|
||||
{ handleClick: 'addToQueue', label: 'Add to queue', icon: 'plus' },
|
||||
{ handleClick: 'addToPlaylist', label: 'Add to playlist', icon: 'plus', playlists: true },
|
||||
{ handleClick: 'startRadio', label: 'Start radio', icon: 'spotify' },
|
||||
{ handleClick: 'copyURIs', label: 'Copy URIs', icon: 'copy' }
|
||||
]
|
||||
break
|
||||
@ -189,31 +199,23 @@ class ContextMenu extends React.Component{
|
||||
}
|
||||
|
||||
renderTitle(){
|
||||
var style = {}
|
||||
var item = this.props.context_menu.data.item
|
||||
var image = null
|
||||
var style = null
|
||||
if (item && item.images){
|
||||
image = helpers.sizedImages(item.images).medium
|
||||
style = {
|
||||
backgroundImage: 'url('+helpers.sizedImages(item.images).medium+')'
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className="title" style={style}>
|
||||
{image ? <img src={image} /> : null}
|
||||
{this.props.context_menu.data.item.name}
|
||||
</div>
|
||||
<Link className="title" to={global.baseURL+helpers.uriType(item.uri)+'/'+item.uri}>
|
||||
{style ? <div className="background" style={style}></div> : null}
|
||||
<div className="text">{item.name}</div>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
renderItems(trigger){
|
||||
var items = this.getItems(trigger)
|
||||
|
||||
var closeItem = (
|
||||
<span className="menu-item-wrapper cancel">
|
||||
<a className="menu-item" onClick={ (e) => this.closeAndDeselectTracks(e) }>
|
||||
<FontAwesome className="icon" fixedWidth name='close' />
|
||||
<span className="label">Cancel</span>
|
||||
</a>
|
||||
</span>
|
||||
)
|
||||
renderItems(){
|
||||
var items = this.getItems()
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -222,7 +224,7 @@ class ContextMenu extends React.Component{
|
||||
if( item.playlists ){
|
||||
return (
|
||||
<span key={item.handleClick} className="menu-item-wrapper has-submenu">
|
||||
<a className="menu-item" onClick={ (e) => this[item.handleClick](e) }>
|
||||
<a className="menu-item" onClick={e => this[item.handleClick](e)}>
|
||||
<FontAwesome className="icon" fixedWidth name={item.icon} />
|
||||
<span className="label">{ item.label }</span>
|
||||
<FontAwesome className="submenu-icon" name="caret-right" />
|
||||
@ -233,7 +235,7 @@ class ContextMenu extends React.Component{
|
||||
}else{
|
||||
return (
|
||||
<span className="menu-item-wrapper" key={item.handleClick}>
|
||||
<a className="menu-item" onClick={ (e) => this[item.handleClick](e) }>
|
||||
<a className="menu-item" onClick={e => this[item.handleClick](e)}>
|
||||
<FontAwesome className="icon" fixedWidth name={item.icon} />
|
||||
<span className="label">{ item.label }</span>
|
||||
</a>
|
||||
@ -242,7 +244,6 @@ class ContextMenu extends React.Component{
|
||||
}
|
||||
})
|
||||
}
|
||||
{ trigger == 'touch' ? closeItem : null }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -277,7 +278,7 @@ class ContextMenu extends React.Component{
|
||||
|
||||
return (
|
||||
<div className={className} style={style}>
|
||||
{this.renderTitle()}
|
||||
{this.props.context_menu.data.item ? this.renderTitle() : null}
|
||||
{this.renderItems(trigger)}
|
||||
</div>
|
||||
);
|
||||
@ -298,6 +299,7 @@ const mapStateToProps = (state, ownProps) => {
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
uiActions: bindActionCreators(uiActions, dispatch),
|
||||
pusherActions: bindActionCreators(pusherActions, dispatch),
|
||||
spotifyActions: bindActionCreators(spotifyActions, dispatch),
|
||||
mopidyActions: bindActionCreators(mopidyActions, dispatch)
|
||||
}
|
||||
|
||||
@ -1,9 +1,16 @@
|
||||
|
||||
import React, { PropTypes } from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
import { createStore, bindActionCreators } from 'redux'
|
||||
import { Link } from 'react-router'
|
||||
import FontAwesome from 'react-fontawesome'
|
||||
import GridItem from './GridItem'
|
||||
|
||||
export default class GridSlider extends React.Component{
|
||||
import ArtistSentence from './ArtistSentence'
|
||||
import Thumbnail from './Thumbnail'
|
||||
|
||||
import * as uiActions from '../services/ui/actions'
|
||||
|
||||
class GridSlider extends React.Component{
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
@ -15,6 +22,21 @@ export default class GridSlider extends React.Component{
|
||||
}
|
||||
}
|
||||
|
||||
handleClick(e,link){
|
||||
if( e.target.tagName.toLowerCase() !== 'a' ){
|
||||
hashHistory.push(link)
|
||||
}
|
||||
}
|
||||
|
||||
handleContextMenu(e,item){
|
||||
e.preventDefault()
|
||||
var data = {
|
||||
uris: [item.uri],
|
||||
item: item
|
||||
}
|
||||
this.props.uiActions.showContextMenu( e, data, 'album', 'click' )
|
||||
}
|
||||
|
||||
next(){
|
||||
if (this.state.page >= this._pagelimit) return false
|
||||
this.setState({ page: this.state.page + 1 })
|
||||
@ -47,8 +69,19 @@ export default class GridSlider extends React.Component{
|
||||
{
|
||||
this.props.tracks.map(
|
||||
(track, index) => {
|
||||
var item = Object.assign({}, track.album, { artists: track.artists })
|
||||
return <GridItem item={item} key={index} link={global.baseURL+'album/'+track.album.uri} />
|
||||
var album = Object.assign({}, track.album, { artists: track.artists })
|
||||
return (
|
||||
<div className="grid-item"
|
||||
key={index}
|
||||
onClick={ (e) => this.handleClick(e,global.baseURL+'album/'+album.uri) }
|
||||
onContextMenu={e => this.handleContextMenu(e,album)}>
|
||||
<Thumbnail size="medium" images={album.images} />
|
||||
<div className="name">{ album.name }</div>
|
||||
<div className="secondary">
|
||||
{ album.artists ? <ArtistSentence artists={album.artists} /> : <span>-</span> }
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -61,3 +94,15 @@ export default class GridSlider extends React.Component{
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
return {}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
uiActions: bindActionCreators(uiActions, dispatch)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(GridSlider)
|
||||
|
||||
|
||||
@ -25,10 +25,13 @@ class List extends React.Component{
|
||||
}
|
||||
}
|
||||
|
||||
handleContextMenu(e,uri){
|
||||
handleContextMenu(e,item){
|
||||
e.preventDefault()
|
||||
var data = { uris: [uri] }
|
||||
this.props.uiActions.showContextMenu(e, data, helpers.uriType(uri), 'click')
|
||||
var data = {
|
||||
uris: [item.uri],
|
||||
item: item
|
||||
}
|
||||
this.props.uiActions.showContextMenu(e, data, helpers.uriType(item.uri), 'click')
|
||||
}
|
||||
|
||||
renderHeader(){
|
||||
@ -82,7 +85,7 @@ class List extends React.Component{
|
||||
return (
|
||||
<div
|
||||
onClick={e => this.handleClick(e, row.uri)}
|
||||
onContextMenu={e => this.handleContextMenu(e,row.uri)}
|
||||
onContextMenu={e => this.handleContextMenu(e,row)}
|
||||
className={className}
|
||||
key={row_index}>
|
||||
{
|
||||
|
||||
@ -39,7 +39,7 @@ class Sidebar extends React.Component{
|
||||
|
||||
<section>
|
||||
<title>Discover</title>
|
||||
<Link activeClassName="active" to={global.baseURL+"discover"}>
|
||||
<Link activeClassName="active" disabled={!this.props.spotify_authorized} to={this.props.spotify_authorized ? global.baseURL+"discover" : null}>
|
||||
<Icon name="compass" className="white" />
|
||||
Discover
|
||||
</Link>
|
||||
|
||||
@ -214,7 +214,7 @@ const PusherMiddleware = (function(){
|
||||
})
|
||||
.then(
|
||||
response => {
|
||||
uiActions.createNotification('Starting radio...')
|
||||
store.dispatch(uiActions.createNotification('Starting radio...'))
|
||||
}
|
||||
)
|
||||
|
||||
@ -252,11 +252,11 @@ const PusherMiddleware = (function(){
|
||||
})
|
||||
.then(
|
||||
response => {
|
||||
uiActions.createNotification('Stopping radio')
|
||||
store.dispatch(uiActions.createNotification('Stopping radio'))
|
||||
}
|
||||
)
|
||||
|
||||
store.dispatch( uiActions.createNotification('Stopping radio') )
|
||||
store.dispatch(uiActions.createNotification('Stopping radio'))
|
||||
var data = {
|
||||
action: 'stop_radio',
|
||||
seed_artists: [],
|
||||
|
||||
@ -532,10 +532,9 @@ export function resolveRadioSeeds( radio ){
|
||||
|
||||
// add to our list of async requests
|
||||
requests.push(
|
||||
sendRequest( dispatch, getState, 'tracks/'+ track_ids )
|
||||
sendRequest( dispatch, getState, 'tracks?ids='+ track_ids )
|
||||
.then( response => {
|
||||
if (!(response instanceof Array)) response = [response]
|
||||
Object.assign(resolved_seeds.seed_tracks, response);
|
||||
Object.assign(resolved_seeds.seed_tracks, response.tracks);
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
@ -94,7 +94,7 @@ const SpotifyMiddleware = (function(){
|
||||
|
||||
// only resolve if radio is enabled
|
||||
if( action.data.radio.enabled ){
|
||||
store.dispatch( spotifyActions.resolveRadioSeeds( action.data.radio ) )
|
||||
store.dispatch(spotifyActions.resolveRadioSeeds(action.data.radio))
|
||||
}
|
||||
break
|
||||
|
||||
|
||||
@ -153,7 +153,7 @@ class Playlist extends React.Component{
|
||||
</div>
|
||||
|
||||
<section className="list-wrapper">
|
||||
{ this.props.playlist.tracks ? <TrackList context={ this.props.playlist.can_edit ? 'editable-playlist' : 'playlist'} tracks={ this.props.playlist.tracks } removeTracks={ tracks_indexes => this.removeTracks(tracks_indexes) } reorderTracks={ (indexes, index) => this.reorderTracks(indexes, index) } /> : null }
|
||||
{ this.props.playlist.tracks ? <TrackList context={ this.props.playlist.can_edit ? 'editable-playlist-track' : 'playlist-track'} tracks={ this.props.playlist.tracks } removeTracks={ tracks_indexes => this.removeTracks(tracks_indexes) } reorderTracks={ (indexes, index) => this.reorderTracks(indexes, index) } /> : null }
|
||||
<LazyLoadListener enabled={this.props.playlist.tracks_more} loadMore={ () => this.loadMore() }/>
|
||||
</section>
|
||||
|
||||
|
||||
@ -67,7 +67,7 @@ class Queue extends React.Component{
|
||||
}
|
||||
}
|
||||
if (this.props.radio.seed_genres.length > 0) seed_sentence =this.props.radio.seed_artists.length+' genres'
|
||||
if (this.props.radio.seed_tracks.length > 0) seed_sentence =this.props.radio.seed_artists.length+' tracks'
|
||||
if (this.props.radio.seed_tracks.length > 0) seed_sentence =this.props.radio.seed_tracks.length+' tracks'
|
||||
|
||||
return (
|
||||
<div className="radio">
|
||||
|
||||
@ -25,21 +25,38 @@
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
padding: 8px 12px;
|
||||
width: 100%;
|
||||
width: 140px;
|
||||
font-weight: bold;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
background: $darkest_grey;
|
||||
|
||||
img {
|
||||
.background {
|
||||
@include blur(5px);
|
||||
background-size: cover;
|
||||
background-position: 50% 0%;
|
||||
opacity: 0.2;
|
||||
background-position: 50% 20%;
|
||||
opacity: 0.5;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: -10px;
|
||||
left: -10px;
|
||||
bottom: -10px;
|
||||
right: -10px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.text {
|
||||
@include one_line_text();
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
&:hover{
|
||||
.background {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,7 +104,7 @@
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&:not(:first-child) .menu-item {
|
||||
.menu-item {
|
||||
border-top: 1px solid lighten($dark_grey, 8%);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user