import React, { PropTypes } from 'react'; import ReactDOM from 'react-dom'; import { createStore, bindActionCreators } from 'redux' import { hashHistory, Link } from 'react-router' import { connect } from 'react-redux' import Sidebar from '../components/Sidebar' import MiniPlayer from '../components/MiniPlayer' import SidebarToggleButton from '../components/SidebarToggleButton' import ContextMenu from '../components/ContextMenu' import Dragger from '../components/Dragger' import Modal from '../components/Modal/Modal' import Notifications from '../components/Notifications' import DebugInfo from '../components/DebugInfo' 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' /** * Root level application **/ class App extends React.Component{ constructor(props){ super(props); this.handleKeyUp = this.handleKeyUp.bind(this); this.handleKeyDown = this.handleKeyDown.bind(this); } componentWillMount(){ window.addEventListener("keyup", this.handleKeyUp, false); window.addEventListener("keydown", this.handleKeyDown, false); } componentDidMount(){ this.props.pusherActions.connect(); this.props.mopidyActions.connect(); this.props.spotifyActions.connect(); if (this.props.spotify_authorized){ // TODO: remove this so we don't tap out our API limits before we even get started // Perhaps fire this on demand? Context menu, playlists loading or AddToPlaylistModal this.props.spotifyActions.getAllLibraryPlaylists(); } // when we navigate to a new route hashHistory.listen( location => { // hide our sidebar this.props.uiActions.toggleSidebar( false ) }); } componentWillReceiveProps(nextProps){ // mopidy comes online if( !this.props.mopidy_connected && nextProps.mopidy_connected ){ this.props.mopidyActions.getLibraryPlaylists(); } // spotify authorized if( !this.props.spotify_authorized && nextProps.spotify_authorized ){ this.props.spotifyActions.getAllLibraryPlaylists(); } // spotify un-authorized if( this.props.spotify_authorized && !nextProps.spotify_authorized ){ // TODO: flush out playlists and then re-fetch mopidy } } componentWillUnmount(){ window.removeEventListener("keyup", this.handleKeyUp, false); window.removeEventListener("keydown", this.handleKeyDown, false); } shouldTriggerShortcut(e){ var ignoreNodes = ['INPUT', 'TEXTAREA']; var keyCodes = [32,27,191]; if( ignoreNodes.indexOf(e.target.nodeName) > -1 ){ return false; } if( keyCodes.indexOf(e.keyCode) > -1 ){ e.preventDefault(); return true; } } handleKeyDown(e){ this.shouldTriggerShortcut(e); } handleKeyUp(e){ if( !this.shouldTriggerShortcut(e) ) return; switch(e.keyCode){ case 32: // spacebar if (this.props.play_state == 'playing'){ this.props.mopidyActions.pause() this.props.uiActions.createNotification(null, 'pause', true) }else{ this.props.mopidyActions.play() this.props.uiActions.createNotification(null, 'play', true) } break; case 27: // esc if (this.props.dragger && this.props.dragger.dragging){ this.props.uiActions.dragCancel() } if (this.props.modal){ this.props.uiActions.closeModal() } break; case 191: // "/" if (e.ctrlKey){ if (this.props.modal){ this.props.uiActions.closeModal() } else { this.props.uiActions.openModal('kiosk_mode') } } break; } } render(){ var className = ''; if (this.props.dragger && this.props.dragger.active) className += ' dragging' if (this.props.sidebar_open) className += ' sidebar-open' if (this.props.modal) className += ' modal-open' if (helpers.isTouchDevice() || this.props.emulate_touch){ className += ' touch' } else { className += ' notouch' } return (