Skip to content

Commit

Permalink
Merge pull request #74 from jaedb/context
Browse files Browse the repository at this point in the history
Context menus
  • Loading branch information
jaedb authored Mar 2, 2017
2 parents 25d572e + fe7c2e0 commit a5773c6
Show file tree
Hide file tree
Showing 23 changed files with 649 additions and 448 deletions.
2 changes: 1 addition & 1 deletion src/js/components/AlbumGrid.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class AlbumGrid extends React.Component{
e: e,
context: 'album',
uris: [item.uri],
item: item
items: [item]
}
this.props.uiActions.showContextMenu(data)
}
Expand Down
2 changes: 1 addition & 1 deletion src/js/components/ArtistGrid.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class ArtistGrid extends React.Component{
e: e,
context: 'artist',
uris: [item.uri],
item: item
items: [item]
}
this.props.uiActions.showContextMenu(data)
}
Expand Down
161 changes: 123 additions & 38 deletions src/js/components/ContextMenu.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

import React, { PropTypes } from 'react'
import { Link } from 'react-router'
import { Link, hashHistory } from 'react-router'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import FontAwesome from 'react-fontawesome'
Expand All @@ -15,12 +15,15 @@ import * as spotifyActions from '../services/spotify/actions'
class ContextMenu extends React.Component{

constructor(props) {
super(props);
super(props)
this.state = {
submenu_expanded: false
}
this.handleScroll = this.handleScroll.bind(this)
this.handleClick = this.handleClick.bind(this)
}

componentDidMount(){
componentDidMount(){
window.addEventListener("scroll", this.handleScroll, false)
window.addEventListener("click", this.handleClick, false)
}
Expand All @@ -30,63 +33,97 @@ class ContextMenu extends React.Component{
window.removeEventListener("click", this.handleClick, false)
}

handleScroll(){
if (this.props.menu) this.props.uiActions.hideContextMenu()
componentWillReceiveProps( nextProps ){
// if we've been given a menu object (ie activated) when we didn't have one prior
if (nextProps.menu && !this.props.menu){
this.setState({ submenu_expanded: false })
$('body').addClass('context-menu-open')

// we DID have one prior, and now we don't
} else if (this.props.menu && !nextProps.menu){
$('body').removeClass('context-menu-open')
}
}

handleScroll(e){
if (this.props.menu){
this.props.uiActions.hideContextMenu()
}
}

handleClick(){
if (this.props.menu) this.props.uiActions.hideContextMenu()
handleClick(e){
// if we click outside of the context menu or context menu trigger, kill it
if ($(e.target).closest('.context-menu').length <= 0 && $(e.target).closest('.context-menu-trigger').length <= 0){
this.props.uiActions.hideContextMenu()
}
}

playQueueItem(){
this.props.uiActions.hideContextMenu()
var tracks = this.props.menu.items;
this.props.mopidyActions.changeTrack( tracks[0].tlid );
this.props.uiActions.hideContextMenu();
this.props.mopidyActions.changeTrack( tracks[0].tlid )
}

removeFromQueue(){
this.props.uiActions.hideContextMenu()
var tracks = this.props.menu.items;
var tracks_tlids = [];
for( var i = 0; i < tracks.length; i++ ){
tracks_tlids.push( tracks[i].tlid );
}
this.props.mopidyActions.removeTracks( tracks_tlids );
this.props.uiActions.hideContextMenu();
}

playURIs(){
this.props.mopidyActions.playURIs(this.props.menu.uris, this.props.menu.tracklist_uri);
this.props.uiActions.hideContextMenu();
this.props.uiActions.hideContextMenu()
this.props.mopidyActions.playURIs(this.props.menu.uris, this.props.menu.tracklist_uri)
}

playURIsNext(){
this.props.mopidyActions.enqueueURIsNext(this.props.menu.uris, this.props.menu.tracklist_uri);
this.props.uiActions.hideContextMenu();
this.props.uiActions.hideContextMenu()
this.props.mopidyActions.enqueueURIsNext(this.props.menu.uris, this.props.menu.tracklist_uri)
}

addToPlaylist(){
this.props.uiActions.openModal('add_to_playlist', { tracks_uris: this.props.menu.uris })
this.props.uiActions.hideContextMenu();
this.setState({ submenu_expanded: !this.state.submenu_expanded })
}

addToQueue(){
this.props.mopidyActions.enqueueURIs(this.props.menu.uris, this.props.menu.tracklist_uri)
this.props.uiActions.hideContextMenu()
this.props.mopidyActions.enqueueURIs(this.props.menu.uris, this.props.menu.tracklist_uri)
}

addTracksToPlaylist(playlist_uri){
this.props.uiActions.hideContextMenu()
this.props.uiActions.addTracksToPlaylist(playlist_uri, this.props.menu.uris)
this.props.uiActions.hideContextMenu();
}

removeFromPlaylist(){
this.props.uiActions.hideContextMenu()
this.props.uiActions.removeTracksFromPlaylist(this.props.menu.tracklist_uri, this.props.menu.indexes)
this.props.uiActions.hideContextMenu();
}

startRadio(){
this.props.uiActions.hideContextMenu()
this.props.pusherActions.startRadio(this.props.menu.uris)
this.props.uiActions.hideContextMenu();
}

goToArtist(){
if (!this.props.menu.items || this.props.menu.items.length <= 0 || !this.props.menu.items[0].artists || this.props.menu.items[0].artists.length <= 0){
return null
} else {
this.props.uiActions.hideContextMenu()
hashHistory.push( global.baseURL +'artist/'+ this.props.menu.items[0].artists[0].uri )
}
}

goToUser(){
if (!this.props.menu.items || this.props.menu.items.length <= 0){
return null
} else {
this.props.uiActions.hideContextMenu()
hashHistory.push( global.baseURL +'user/'+ this.props.menu.items[0].owner.uri )
}
}

copyURIs(e){
Expand All @@ -113,7 +150,16 @@ class ContextMenu extends React.Component{
playlists = helpers.sortItems(playlists, 'name')

return (
<div className="submenu">
<div className={this.state.submenu_expanded ? 'submenu expanded' : 'submenu'}>
<span className="menu-item-wrapper">
<a className="menu-item close-submenu" onClick={e => this.setState({submenu_expanded: false})}>
<span className="label">
<FontAwesome name='caret-left' />
&nbsp;&nbsp;
Back
</span>
</a>
</span>
{
playlists.map( playlist => {
return (
Expand All @@ -137,6 +183,7 @@ class ContextMenu extends React.Component{
{ handleClick: 'playURIs', label: 'Play' },
{ handleClick: 'playURIsNext', label: 'Play next' },
{ handleClick: 'addToQueue', label: 'Add to queue' },
{ handleClick: 'goToArtist', label: 'Go to artist' },
// { handleClick: 'toggleFollow', label: 'Follow/unfollow' }, TODO
{ handleClick: 'copyURIs', label: 'Copy URI' }
]
Expand All @@ -153,6 +200,7 @@ class ContextMenu extends React.Component{
case 'playlist':
var items = [
{ handleClick: 'playURIs', label: 'Play' },
{ handleClick: 'goToUser', label: 'Go to user' },
// { handleClick: 'toggleFollow', label: 'Follow/unfollow' }, TODO
{ handleClick: 'copyURIs', label: 'Copy URI' }
]
Expand Down Expand Up @@ -195,19 +243,52 @@ class ContextMenu extends React.Component{
}

renderTitle(){
var item = this.props.menu.item
var style = null
if (item && item.images){
style = {
backgroundImage: 'url('+helpers.sizedImages(item.images).medium+')'
}
if (!this.props.menu.items || this.props.menu.items.length <= 0){
return null
}

switch (this.props.menu.context){

case 'artist':
case 'album':
case 'playlist':
var item = this.props.menu.items[0]
var style = null
if (item && item.images){
style = {
backgroundImage: 'url('+helpers.sizedImages(item.images).medium+')'
}
}

return (
<Link className="title" to={global.baseURL+helpers.uriType(item.uri)+'/'+item.uri}>
{style ? <div className="background" style={style}></div> : null}
<div className="type">
{helpers.uriSource(item.uri)}
&nbsp;
{this.props.menu.context}
</div>
<div className="text">{item.name}</div>
</Link>
)
break

default:
return (
<span className="title">
<div className="type">
{helpers.uriSource(this.props.menu.items[0].uri)}
&nbsp;
{this.props.menu.context}s
</div>
<div className="text">
{this.props.menu.items.length} items
</div>
</span>
)
break

}
return (
<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(){
Expand All @@ -222,9 +303,9 @@ class ContextMenu extends React.Component{
<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" />
<FontAwesome className="submenu-icon" name='caret-right' />
</a>
{ this.renderPlaylistSubmenu() }
{this.renderPlaylistSubmenu()}
</span>
)
}else{
Expand Down Expand Up @@ -254,7 +335,8 @@ class ContextMenu extends React.Component{
var height = 0
if (items) height = items.length * 34 // this is an approximation of how tall each menu item is

var className = "context-menu"
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'
if (this.props.menu.position_x > (window.innerWidth - 308)) className += ' right-align-submenu'
if (this.props.menu.position_y > (window.innerHeight - height)){
Expand All @@ -264,8 +346,11 @@ class ContextMenu extends React.Component{

return (
<div className={className} style={style}>
{this.props.menu.item ? this.renderTitle() : null}
{this.renderItems()}
<div className="liner">
{this.renderTitle()}
{this.renderItems()}
</div>
<div className="background" onClick={e => this.props.uiActions.hideContextMenu()}></div>
</div>
);
}
Expand Down
24 changes: 24 additions & 0 deletions src/js/components/ContextMenuTrigger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

import React, { PropTypes } from 'react'
import FontAwesome from 'react-fontawesome'

export default class ContextMenuTrigger extends React.Component{

constructor(props) {
super(props);
}

render(){
var className = 'context-menu-trigger'
if (this.props.className){
className += ' '+this.props.className
}
return (
<button
className={className}
onClick={e => this.props.onTrigger(e)}>
<FontAwesome name="ellipsis-v" />
</button>
);
}
}
8 changes: 8 additions & 0 deletions src/js/components/Modal/Modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ class Modal extends React.Component{
super(props)
}

componentWillReceiveProps( nextProps ){
if (nextProps.modal){
$('body').addClass('modal-open')
} else {
$('body').removeClass('modal-open')
}
}

render(){
if( !this.props.modal ) return null;

Expand Down
2 changes: 1 addition & 1 deletion src/js/components/PlaylistGrid.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class PlaylistGrid extends React.Component{
e: e,
context: 'playlist',
uris: [item.uri],
item: item
items: [item]
}
this.props.uiActions.showContextMenu(data)
}
Expand Down
25 changes: 23 additions & 2 deletions src/js/components/SearchForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Link, hashHistory } from 'react-router'
import { createStore, bindActionCreators } from 'redux'
import FontAwesome from 'react-fontawesome'

import * as helpers from '../helpers'
import * as uiActions from '../services/ui/actions'
import * as spotifyActions from '../services/spotify/actions'

Expand All @@ -19,8 +20,28 @@ class SearchForm extends React.Component{
}

handleSubmit(e){
e.preventDefault();
hashHistory.push(global.baseURL+'search/'+this.state.query);
e.preventDefault()

// check for uri type matching
switch (helpers.uriType(this.state.query)){

case 'album':
hashHistory.push(global.baseURL+'album/'+this.state.query)
break

case 'artist':
hashHistory.push(global.baseURL+'artist/'+this.state.query)
break

case 'playlist':
hashHistory.push(global.baseURL+'playlist/'+this.state.query)
break

default:
hashHistory.push(global.baseURL+'search/'+this.state.query)
break
}

return false
}

Expand Down
Loading

0 comments on commit a5773c6

Please sign in to comment.