Files
Iris/src/js/App.js

370 lines
11 KiB
JavaScript
Raw Normal View History

2016-10-03 14:44:27 +13:00
import React, { PropTypes } from 'react';
import ReactDOM from 'react-dom';
2018-07-08 21:12:18 +12:00
import { createStore, bindActionCreators } from 'redux';
import { hashHistory, Link } from 'react-router';
import { connect } from 'react-redux';
import ReactGA from 'react-ga';
import Sidebar from './components/Sidebar';;
import PlaybackControls from './components/PlaybackControls';
import ContextMenu from './components/ContextMenu';
import Dragger from './components/Dragger';
import Notifications from './components/Notifications';
import DebugInfo from './components/DebugInfo';
import * as helpers from './helpers';
import * as coreActions from './services/core/actions';
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';
2016-10-03 14:44:27 +13:00
2016-10-14 18:15:34 +13:00
/**
* Root level application
**/
2016-10-03 14:44:27 +13:00
class App extends React.Component{
constructor(props){
super(props);
2016-10-27 13:02:10 +13:00
this.handleKeyUp = this.handleKeyUp.bind(this);
2016-10-27 13:56:47 +13:00
this.handleKeyDown = this.handleKeyDown.bind(this);
this.handleWindowResize = this.handleWindowResize.bind(this);
this.handleInstallPrompt = this.handleInstallPrompt.bind(this);
2016-10-03 14:44:27 +13:00
}
2016-10-27 13:02:10 +13:00
componentWillMount(){
window.addEventListener("keyup", this.handleKeyUp, false);
window.addEventListener("keydown", this.handleKeyDown, false);
window.addEventListener("resize", this.handleWindowResize, false);
window.addEventListener("beforeinstallprompt", this.handleInstallPrompt, false);
}
componentWillUnmount(){
window.removeEventListener("keyup", this.handleKeyUp, false);
window.removeEventListener("keydown", this.handleKeyDown, false);
window.removeEventListener("resize", this.handleWindowResize, false);
window.removeEventListener("beforeinstallprompt", this.handleInstallPrompt, false);
}
componentDidMount(){
if (this.props.allow_reporting){
ReactGA.initialize('UA-64701652-3');
2018-07-21 21:22:47 +12:00
/*
2018-07-21 21:22:47 +12:00
if (Raven !== undefined){
Raven.config('https://ca99fb6662fe40ae8ec4c18a466e4b4b@sentry.io/219026').install();
}
*/
}
2017-09-19 16:57:59 +12:00
// Fire up our services
this.props.mopidyActions.connect();
this.props.pusherActions.connect();
this.props.coreActions.getBroadcasts();
// when we navigate to a new route
2017-10-10 13:05:28 +13:00
hashHistory.listen(location => {
// Log our pageview
if (this.props.allow_reporting){
ReactGA.set({ page: window.location.hash });
ReactGA.pageview(window.location.hash);
}
// Hide our sidebar
2017-10-10 13:05:28 +13:00
this.props.uiActions.toggleSidebar(false )
// Unselect any tracks
this.props.uiActions.setSelectedTracks([]);
// Close context menu
if (this.props.context_menu){
this.props.uiActions.hideContextMenu();
}
});
// Check our slim_mode
this.handleWindowResize(null);
2017-11-28 21:21:30 +13:00
// Check for url-parsed configuration values
var url_vars = this.props.location.query;
2017-12-04 07:58:48 +13:00
if (url_vars !== undefined && url_vars.host !== undefined && url_vars.port !== undefined){
this.props.mopidyActions.set({
2017-11-29 08:34:42 +13:00
host: url_vars.host,
port: url_vars.port
});
2017-11-28 21:21:30 +13:00
hashHistory.push(global.baseURL);
}
2018-02-01 08:06:40 +13:00
// show initial setup if required
if (!this.props.initial_setup_complete){
2018-07-10 16:57:06 +12:00
hashHistory.push(global.baseURL+'initial-setup');
}
}
2016-10-27 16:58:11 +13:00
componentWillReceiveProps(nextProps){
// We've navigated to a new location
if (this.props.location.pathname !== nextProps.location.pathname){
// Scroll to bottom, only if we've PUSHed to a new route
// We also prevent scroll reset for any sub_view routes (like tabs, services, etc)
if (nextProps.location.action == 'PUSH' && nextProps.params.sub_view === undefined){
window.scrollTo(0, 0);
}
}
}
2016-10-27 13:56:47 +13:00
shouldTriggerShortcut(e){
if (!this.props.shortkeys_enabled){
return false;
}
// When we're focussed on certian elements, don't fire any shortcuts
// Typically form inputs
var ignoreNodes = ['INPUT', 'TEXTAREA'];
if (ignoreNodes.indexOf(e.target.nodeName) > -1){
return false;
2016-10-27 13:56:47 +13:00
}
// Listen for standalone key codes
let keyCodes = [27,32,191];
if (keyCodes.indexOf(e.keyCode) > -1){
e.preventDefault();
return true;
}
// Listen for key codes that require ctrl to be held
let keyCodesWithCtrl = [37,38,39,40];
if ((e.ctrlKey || e.metaKey) && keyCodesWithCtrl.indexOf(e.keyCode) > -1){
e.preventDefault();
return true;
2016-10-27 13:56:47 +13:00
}
// Listen for key codes that require ctrl to be held
let keyCodesWithCtrlShift = [70];
if ((e.ctrlKey || e.metaKey) && e.shiftKey && keyCodesWithCtrlShift.indexOf(e.keyCode) > -1){
e.preventDefault();
return true;
}
2016-10-27 13:56:47 +13:00
}
handleInstallPrompt(e){
e.preventDefault();
this.props.uiActions.installPrompt(e);
}
handleWindowResize(e){
var width = Math.max(document.documentElement.clientWidth, window.innerWidth || 0)
var height = Math.max(document.documentElement.clientHeight, window.innerHeight || 0)
if (width <= 800){
if (!this.props.slim_mode){
this.props.uiActions.setSlimMode(true)
}
} else {
if (this.props.slim_mode){
this.props.uiActions.setSlimMode(false)
}
}
}
2016-10-27 13:56:47 +13:00
handleKeyDown(e){
this.shouldTriggerShortcut(e)
2016-10-27 13:02:10 +13:00
}
handleKeyUp(e){
2017-04-20 08:28:42 +12:00
if (!this.shouldTriggerShortcut(e)){
return
}
2016-10-27 13:56:47 +13:00
switch(e.keyCode){
2016-10-27 13:02:10 +13:00
case 32: // spacebar
if (e.ctrlKey || e.metaKey){
this.props.mopidyActions.stop();
this.props.uiActions.createNotification({content: 'stop', type: 'shortcut', key: 'shortcut', duration: 1});
} else if (this.props.play_state == 'playing'){
this.props.mopidyActions.pause();
this.props.uiActions.createNotification({content: 'pause', type: 'shortcut', key: 'shortcut', duration: 1});
} else {
this.props.mopidyActions.play();
this.props.uiActions.createNotification({content: 'play', type: 'shortcut', key: 'shortcut', duration: 1});
2016-10-27 13:02:10 +13:00
}
2017-10-31 08:31:43 +13:00
break;
case 27: // esc
if (this.props.dragger && this.props.dragger.dragging){
this.props.uiActions.dragEnd();
}
2017-10-31 08:31:43 +13:00
break;
2017-04-20 08:28:42 +12:00
case 40: // down
if ((e.ctrlKey || e.metaKey) && e.shiftKey){
this.props.mopidyActions.setMute(true);
this.props.uiActions.createNotification({content: 'volume-off', type: 'shortcut', key: 'shortcut', duration: 1});
} else if (e.ctrlKey){
var volume = this.props.volume;
2017-04-20 08:28:42 +12:00
if (volume !== 'false'){
volume -= 5;
if (volume < 0){
volume = 0;
}
this.props.mopidyActions.setVolume(volume);
if (this.props.mute){
this.props.mopidyActions.setMute(false);
}
this.props.uiActions.createNotification({content: 'volume-down', type: 'shortcut', key: 'shortcut', duration: 1});
2017-04-20 08:28:42 +12:00
}
}
2017-10-31 08:31:43 +13:00
break;
2017-04-20 08:28:42 +12:00
case 38: // up
if ((e.ctrlKey || e.metaKey) && e.shiftKey){
this.props.mopidyActions.setVolume(100)
if (this.props.mute){
this.props.mopidyActions.setMute(false);
}
this.props.uiActions.createNotification({content: 'volume-up', type: 'shortcut', key: 'shortcut', duration: 1});
} else if (e.ctrlKey || e.metaKey){
2017-04-20 08:28:42 +12:00
var volume = this.props.volume
if (volume !== 'false'){
volume += 5;
if (volume > 100){
volume = 100
}
this.props.mopidyActions.setVolume(volume);
if (this.props.mute){
this.props.mopidyActions.setMute(false);
}
this.props.uiActions.createNotification({content: 'volume-up', type: 'shortcut', key: 'shortcut', duration: 1});
2017-04-20 08:28:42 +12:00
}
}
2017-10-31 08:31:43 +13:00
break;
2017-04-20 08:28:42 +12:00
case 37: // left
if ((e.ctrlKey || e.metaKey) && e.shiftKey){
var new_position = this.props.play_time_position - 30000;
if (new_position < 0){
new_position = 0;;
}
this.props.mopidyActions.seek(new_position);
this.props.uiActions.createNotification({content: 'fast-backward', type: 'shortcut', key: 'shortcut', duration: 1});
} else if (e.ctrlKey || e.metaKey){
this.props.mopidyActions.previous();
this.props.uiActions.createNotification({content: 'step-backward', type: 'shortcut', key: 'shortcut', duration: 1});
2017-04-20 08:28:42 +12:00
}
2017-10-31 08:31:43 +13:00
break;
2017-04-20 08:28:42 +12:00
case 39: // right
if ((e.ctrlKey || e.metaKey) && e.shiftKey){
this.props.mopidyActions.seek(this.props.play_time_position + 30000);
this.props.uiActions.createNotification({content: 'fast-forward', type: 'shortcut', key: 'shortcut', duration: 1});
} else if (e.ctrlKey || e.metaKey){
this.props.mopidyActions.next();
this.props.uiActions.createNotification({content: 'step-forward', type: 'shortcut', key: 'shortcut', duration: 1});
2017-04-20 08:28:42 +12:00
}
2017-10-31 08:31:43 +13:00
break;
case 70: // F
if ((e.ctrlKey || e.metaKey) && e.shiftKey){
2018-07-08 21:12:18 +12:00
hashHistory.push(global.baseURL+'modal/kiosk-mode');
}
2017-10-31 08:31:43 +13:00
break;
2016-10-27 13:02:10 +13:00
}
}
2016-10-03 14:44:27 +13:00
render(){
2016-11-10 16:17:10 +13:00
var className = '';
if (this.props.dragger && this.props.dragger.active){
className += ' dragging';
}
if (this.props.sidebar_open){
className += ' sidebar-open';
}
if (this.props.touch_dragging){
className += ' touch-dragging';
}
if (this.props.slim_mode){
className += ' slim-mode';
}
if (helpers.isTouchDevice()){
className += ' touch';
2017-02-27 08:44:02 +13:00
} else {
className += ' notouch';
2017-02-27 08:44:02 +13:00
}
2016-11-10 16:17:10 +13:00
2016-10-03 14:44:27 +13:00
return (
2016-11-10 16:17:10 +13:00
<div className={className}>
<div className="body">
<Sidebar />
<PlaybackControls />
<main>
{this.props.children}
</main>
</div>
<ContextMenu />
2016-11-10 16:17:10 +13:00
<Dragger />
<Notifications
uiActions={this.props.uiActions}
2018-07-13 16:48:41 +12:00
spotifyActions={this.props.spotifyActions}
notifications={this.props.notifications}
2017-05-29 20:04:01 +12:00
processes={this.props.processes}
broadcasts={this.props.broadcasts}
/>
2017-01-29 12:54:06 +13:00
{this.props.debug_info ? <DebugInfo /> : null}
2016-10-03 14:44:27 +13:00
</div>
);
}
}
/**
* Export our component
*
* We also integrate our global store, using connect()
**/
const mapStateToProps = (state, ownProps) => {
return {
shortkeys_enabled: state.ui.shortkeys_enabled,
allow_reporting: state.ui.allow_reporting,
touch_dragging: state.ui.touch_dragging,
initial_setup_complete: state.ui.initial_setup_complete,
slim_mode: state.ui.slim_mode,
2017-05-29 20:04:01 +12:00
broadcasts: (state.ui.broadcasts ? state.ui.broadcasts : []),
2017-04-20 08:28:42 +12:00
volume: (state.mopidy.volume ? state.mopidy.volume : false),
notifications: (state.ui.notifications ? state.ui.notifications : []),
2017-04-06 09:01:53 +12:00
processes: (state.ui.processes ? state.ui.processes : {}),
2017-03-27 14:23:38 +13:00
load_queue: (state.ui.load_queue ? state.ui.load_queue : {}),
mopidy_connected: state.mopidy.connected,
spotify_authorized: state.spotify.authorization,
play_state: state.mopidy.play_state,
play_time_position: parseInt(state.mopidy.time_position),
mute: state.mopidy.mute,
2016-11-21 21:46:53 +13:00
sidebar_open: state.ui.sidebar_open,
2016-11-10 16:17:10 +13:00
dragger: state.ui.dragger,
2017-01-29 12:54:06 +13:00
context_menu: state.ui.context_menu,
debug_info: state.ui.debug_info
}
2016-10-03 14:44:27 +13:00
}
const mapDispatchToProps = (dispatch) => {
return {
coreActions: bindActionCreators(coreActions, dispatch),
2016-10-27 10:02:12 +13:00
uiActions: bindActionCreators(uiActions, dispatch),
2016-10-27 16:58:11 +13:00
pusherActions: bindActionCreators(pusherActions, dispatch),
mopidyActions: bindActionCreators(mopidyActions, dispatch),
spotifyActions: bindActionCreators(spotifyActions, dispatch)
2016-10-03 14:44:27 +13:00
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)