Refactoring mopidy actions - everything must have an action method

This commit is contained in:
James Barnsley
2018-02-26 10:25:56 +13:00
parent dce75cf529
commit 75d6e8c85e
4 changed files with 454 additions and 213 deletions

View File

@ -35,25 +35,25 @@ class PlaybackControls extends React.Component{
}
renderConsumeButton(){
var button = <a className="control has-tooltip" onClick={() => this.props.mopidyActions.instruct('tracklist.setConsume', [true])}><FontAwesome name="fire" /><span className="tooltip">Consume</span></a>
var button = <a className="control has-tooltip" onClick={() => this.props.mopidyActions.setConsume(true)}><FontAwesome name="fire" /><span className="tooltip">Consume</span></a>
if (this.props.consume){
button = <a className="control active has-tooltip" onClick={() => this.props.mopidyActions.instruct('tracklist.setConsume', [false])}><FontAwesome name="fire" /><span className="tooltip">Consume</span></a>
button = <a className="control active has-tooltip" onClick={() => this.props.mopidyActions.setConsume(false)}><FontAwesome name="fire" /><span className="tooltip">Consume</span></a>
}
return button;
}
renderRandomButton(){
var button = <a className="control has-tooltip" onClick={() => this.props.mopidyActions.instruct('tracklist.setRandom', [true])}><FontAwesome name="random" /><span className="tooltip">Shuffle</span></a>
var button = <a className="control has-tooltip" onClick={() => this.props.mopidyActions.setRandom(true)}><FontAwesome name="random" /><span className="tooltip">Shuffle</span></a>
if (this.props.random){
button = <a className="control active has-tooltip" onClick={() => this.props.mopidyActions.instruct('tracklist.setRandom', [false])}><FontAwesome name="random" /><span className="tooltip">Shuffle</span></a>
button = <a className="control active has-tooltip" onClick={() => this.props.mopidyActions.setRandom(false)}><FontAwesome name="random" /><span className="tooltip">Shuffle</span></a>
}
return button;
}
renderRepeatButton(){
var button = <a className="control has-tooltip" onClick={() => this.props.mopidyActions.instruct('tracklist.setRepeat', [true])}><FontAwesome name="repeat" /><span className="tooltip">Repeat</span></a>
var button = <a className="control has-tooltip" onClick={() => this.props.mopidyActions.setRepeat(true)}><FontAwesome name="repeat" /><span className="tooltip">Repeat</span></a>
if (this.props.repeat){
button = <a className="control active has-tooltip" onClick={() => this.props.mopidyActions.instruct('tracklist.setRepeat', [false])}><FontAwesome name="repeat" /><span className="tooltip">Repeat</span></a>
button = <a className="control active has-tooltip" onClick={() => this.props.mopidyActions.setRepeat(false)}><FontAwesome name="repeat" /><span className="tooltip">Repeat</span></a>
}
return button;
}

View File

@ -36,11 +36,155 @@ export function debug(call, value){
}
}
/**
* Core play actions
**/
export function getPlayState(){
return {
type: 'MOPIDY_GET_PLAY_STATE'
}
}
export function play(){
return {
type: 'MOPIDY_PLAY'
}
}
export function pause(){
return {
type: 'MOPIDY_PAUSE'
}
}
export function stop(){
return {
type: 'MOPIDY_PAUSE'
}
}
export function next(){
return {
type: 'MOPIDY_NEXT'
}
}
export function previous(){
return {
type: 'MOPIDY_PREVIOUS'
}
}
export function getMute(){
return {
type: 'MOPIDY_GET_MUTE'
}
}
export function setMute(mute){
return {
type: 'MOPIDY_SET_MUTE',
mute: mute
}
}
export function getVolume(){
return {
type: 'MOPIDY_GET_VOLUME'
}
}
export function setVolume(volume){
return {
type: 'MOPIDY_SET_VOLUME',
volume: volume
}
}
export function getConsume(){
return {
type: 'MOPIDY_GET_CONSUME'
}
}
export function setConsume(consume){
return {
type: 'MOPIDY_SET_CONSUME',
consume: consume
}
}
export function getRepeat(){
return {
type: 'MOPIDY_GET_REPEAT'
}
}
export function setRepeat(repeat){
return {
type: 'MOPIDY_SET_REPEAT',
repeat: repeat
}
}
export function getRandom(){
return {
type: 'MOPIDY_GET_RANDOM'
}
}
export function setRandom(random){
return {
type: 'MOPIDY_SET_RANDOM',
random: random
}
}
export function seek(time_position){
return {
type: 'MOPIDY_SEEK',
time_position: parseInt(time_position)
}
}
export function getTimePosition(){
return {
type: 'MOPIDY_GET_TIME_POSITION'
}
}
export function setTimePosition(time_position){
return {
type: 'MOPIDY_SET_TIME_POSITION',
time_position: time_position
}
}
export function getUriSchemes(){
return {
type: 'MOPIDY_GET_URI_SCHEMES'
}
}
/**
* Playback-oriented actions
* Advanced playback actions
**/
export function getCurrentTrack(){
return {
type: 'MOPIDY_GET_CURRENT_TRACK'
}
}
export function getQueue(){
return {
type: 'MOPIDY_GET_QUEUE'
}
}
export function changeTrack(tlid){
return {
type: 'MOPIDY_CHANGE_TRACK',
@ -109,51 +253,6 @@ export function clearTracklist(){
return instruct('tracklist.clear')
}
export function play(){
return {
type: 'MOPIDY_TRIGGER_PLAY'
}
}
export function pause(){
return instruct('playback.pause')
}
export function stop(){
return instruct('playback.stop')
}
export function next(){
return instruct('playback.next')
}
export function previous(){
return instruct('playback.previous')
}
export function setMute(mute){
return instruct('mixer.setMute', {mute: mute})
}
export function setVolume(volume){
return instruct('playback.setVolume', {volume: volume})
}
export function seek(time_position){
return instruct('playback.seek', {time_position: parseInt(time_position)})
}
export function getTimePosition(){
return instruct('playback.getTimePosition')
}
export function setTimePosition(time_position){
return {
type: 'MOPIDY_TIMEPOSITION',
data: time_position
}
}
/**
@ -296,6 +395,9 @@ export function getSearchResults(context, query, limit = 100){
**/
export function getQueueHistory(){
return {
type: 'MOPIDY_GET_HISTORY'
}
return instruct('history.getHistory')
}

View File

@ -32,16 +32,17 @@ const MopidyMiddleware = (function(){
case 'state:online':
store.dispatch({ type: 'MOPIDY_CONNECTED' });
instruct(ws, store, 'playback.getState' );
instruct(ws, store, 'playback.getVolume' );
instruct(ws, store, 'mixer.getMute' );
instruct(ws, store, 'tracklist.getConsume' );
instruct(ws, store, 'tracklist.getRandom' );
instruct(ws, store, 'tracklist.getRepeat' );
instruct(ws, store, 'tracklist.getTlTracks' );
instruct(ws, store, 'playback.getCurrentTlTrack' );
instruct(ws, store, 'playback.getTimePosition' );
instruct(ws, store, 'getUriSchemes' );
store.dispatch(mopidyActions.getPlayState());
store.dispatch(mopidyActions.getVolume());
store.dispatch(mopidyActions.getMute());
store.dispatch(mopidyActions.getConsume());
store.dispatch(mopidyActions.getRandom());
store.dispatch(mopidyActions.getRepeat());
store.dispatch(mopidyActions.getQueue());
store.dispatch(mopidyActions.getCurrentTrack());
store.dispatch(mopidyActions.getTimePosition());
store.dispatch(mopidyActions.getUriSchemes());
// every 1000s update our play position (when playing)
progress_interval = setInterval(() => {
@ -71,38 +72,38 @@ const MopidyMiddleware = (function(){
break;
case 'event:tracklistChanged':
instruct(ws, store, 'tracklist.getTlTracks' );
store.dispatch(mopidyActions.getQueueTracks())
break;
case 'event:playbackStateChanged':
instruct(ws, store, 'playback.getState' );
instruct(ws, store, 'playback.getTimePosition' );
store.dispatch(mopidyActions.getPlayState());
store.dispatch(mopidyActions.getTimePosition());
break;
case 'event:seeked':
store.dispatch({ type: 'MOPIDY_TIMEPOSITION', data: data.time_position });
store.dispatch({ type: 'MOPIDY_TIMEPOSITION', data: data.time_position});
break;
case 'event:trackPlaybackEnded':
instruct(ws, store, 'playback.getTimePosition' );
store.dispatch(mopidyActions.getTimePosition());
break;
case 'event:trackPlaybackStarted':
instruct(ws, store, 'playback.getCurrentTlTrack' );
store.dispatch(mopidyActions.getCurrentTrack());
break;
case 'event:volumeChanged':
store.dispatch({ type: 'MOPIDY_VOLUME', data: data.volume });
store.dispatch({type: 'MOPIDY_VOLUME', data: data.volume});
break;
case 'event:muteChanged':
store.dispatch({ type: 'MOPIDY_MUTE', data: data.mute });
store.dispatch({type: 'MOPIDY_MUTE', data: data.mute});
break;
case 'event:optionsChanged':
instruct(ws, store, 'tracklist.getConsume' );
instruct(ws, store, 'tracklist.getRandom' );
instruct(ws, store, 'tracklist.getRepeat' );
store.dispatch(mopidyActions.getConsume());
store.dispatch(mopidyActions.getRandom());
store.dispatch(mopidyActions.getRepeat());
break;
default:
@ -131,16 +132,16 @@ const MopidyMiddleware = (function(){
var method = callParts[1];
return new Promise((resolve, reject) => {
if (model in ws){
if (method in ws[model]){
var mopidyObject = ws[model][method]
var property = method;
} else {
var mopidyObject = ws[model]
var property = model;
}
if (method in ws[model]){
var mopidyObject = ws[model][method];
var property = method;
} else {
var mopidyObject = ws[model];
var property = model;
}
} else {
// Detect invalid model.method calls, which result in an empty mopidyObject
if (!mopidyObject || typeof(mopidyObject) !== 'function'){
var error = {
message: 'Call to an invalid object. Check you are calling a valid Mopidy object.',
call: call,
@ -152,12 +153,9 @@ const MopidyMiddleware = (function(){
error
));
reject(error)
reject(error);
}
property = property.replace('get','');
property = property.replace('set','');
var loader_key = helpers.generateGuid()
store.dispatch(uiActions.startLoading(loader_key, 'mopidy_'+property))
@ -175,7 +173,6 @@ const MopidyMiddleware = (function(){
response => {
clearTimeout(timeout);
store.dispatch(uiActions.stopLoading(loader_key));
store.dispatch({ type: 'MOPIDY_'+property.toUpperCase(), call: call, data: response });
resolve(response);
},
error => {
@ -200,143 +197,275 @@ const MopidyMiddleware = (function(){
case 'MOPIDY_CONNECT':
if (socket != null) socket.close()
store.dispatch({ type: 'MOPIDY_CONNECTING' })
var state = store.getState()
if (socket != null) socket.close();
store.dispatch({ type: 'MOPIDY_CONNECTING'});
var state = store.getState();
socket = new Mopidy({
webSocketUrl: 'ws'+(state.mopidy.ssl ? 's' : '')+'://'+state.mopidy.host+':'+state.mopidy.port+'/mopidy/ws/',
callingConvention: 'by-position-or-by-name'
})
});
socket.on((type, data) => handleMessage(socket, store, type, data ) )
break
socket.on((type, data) => handleMessage(socket, store, type, data));
break;
case 'MOPIDY_CONNECTED':
ReactGA.event({ category: 'Mopidy', action: 'Connected', label: window.location.hostname })
next(action)
break
ReactGA.event({ category: 'Mopidy', action: 'Connected', label: window.location.hostname });
next(action);
break;
case 'MOPIDY_DISCONNECT':
if (socket != null) socket.close()
socket = null
store.dispatch({ type: 'MOPIDY_DISCONNECTED' })
break
// send an instruction to the websocket
case 'MOPIDY_INSTRUCT':
instruct(socket, store, action.call, action.value )
break
break;
case 'MOPIDY_DEBUG':
instruct(socket, store, action.call, action.value )
.then(response => {
store.dispatch({ type: 'DEBUG', response: response })
store.dispatch({type: 'DEBUG', response: response});
})
break
case 'MOPIDY_URISCHEMES':
var uri_schemes = action.data
var remove = ['http','https','mms','rtmp','rtmps','rtsp','sc','yt']
// remove all our ignored types
for(var i = 0; i < remove.length; i++){
var index = uri_schemes.indexOf(remove[i])
if (index > -1 ) uri_schemes.splice(index, 1);
}
// append with ':' to make them a mopidy URI
for(var i = 0; i < uri_schemes.length; i++){
uri_schemes[i] = uri_schemes[i] +':'
}
// Enable Iris providers when the backend is available
if (uri_schemes.includes('spotify:')){
store.dispatch({
type: 'SPOTIFY_SET',
data: {
enabled: true
}
})
store.dispatch(spotifyActions.connect());
}
// If we haven't customised our search schemes, add all to search
if (store.getState().ui.search_uri_schemes === undefined){
store.dispatch(uiActions.set({search_uri_schemes: uri_schemes}));
}
store.dispatch({ type: 'MOPIDY_URISCHEMES_FILTERED', data: uri_schemes });
break
break;
/**
* General playback
**/
case 'MOPIDY_TRIGGER_PLAY':
case 'MOPIDY_GET_PLAY_STATE':
instruct(socket, store, 'playback.getState')
.then(
response => {
store.dispatch({
type: 'MOPIDY_PLAY_STATE',
play_state: response
})
}
)
break
case 'MOPIDY_PLAY':
instruct(socket, store, 'playback.play');
var data = {
type: 'notification',
notification_type: 'info',
content: store.getState().pusher.username +(store.getState().mopidy.play_state == 'paused' ? ' resumed' : ' started')+' playback',
icon: (store.getState().core.current_track ? helpers.getTrackIcon(store.getState().core.current_track, store.getState().core) : false)
}
store.dispatch(pusherActions.deliverBroadcast(data));
store.dispatch(pusherActions.deliverBroadcast(
{
type: 'notification',
notification_type: 'info',
content: store.getState().pusher.username +(store.getState().mopidy.play_state == 'paused' ? ' resumed' : ' started')+' playback',
icon: (store.getState().core.current_track ? helpers.getTrackIcon(store.getState().core.current_track, store.getState().core) : false)
}
));
break
case 'MOPIDY_PAUSE':
var data = {
type: 'notification',
notification_type: 'info',
content: store.getState().pusher.username +' paused playback',
icon: (store.getState().core.current_track ? helpers.getTrackIcon(store.getState().core.current_track, store.getState().core) : false)
}
store.dispatch(pusherActions.deliverBroadcast(data));
instruct(socket, store, 'playback.pause');
store.dispatch(pusherActions.deliverBroadcast(
{
type: 'notification',
notification_type: 'info',
content: store.getState().pusher.username +' paused playback',
icon: (store.getState().core.current_track ? helpers.getTrackIcon(store.getState().core.current_track, store.getState().core) : false)
}
));
break
case 'MOPIDY_NEXT':
var data = {
type: 'notification',
notification_type: 'info',
content: store.getState().pusher.username +' skipped <em>'+store.getState().core.current_track.name+'</em>',
icon: (store.getState().core.current_track ? helpers.getTrackIcon(store.getState().core.current_track, store.getState().core) : false)
}
store.dispatch(pusherActions.deliverBroadcast(data));
instruct(socket, store, 'playback.next');
store.dispatch(pusherActions.deliverBroadcast(
{
type: 'notification',
notification_type: 'info',
content: store.getState().pusher.username +' skipped <em>'+store.getState().core.current_track.name+'</em>',
icon: (store.getState().core.current_track ? helpers.getTrackIcon(store.getState().core.current_track, store.getState().core) : false)
}
));
break
case 'MOPIDY_STOP':
var data = {
type: 'notification',
notification_type: 'info',
content: store.getState().pusher.username +' stopped playback',
icon: (store.getState().core.current_track ? helpers.getTrackIcon(store.getState().core.current_track, store.getState().core) : false)
}
store.dispatch(pusherActions.deliverBroadcast(data));
instruct(socket, store, 'playback.stop');
store.dispatch(pusherActions.deliverBroadcast(
{
type: 'notification',
notification_type: 'info',
content: store.getState().pusher.username +' stopped playback',
icon: (store.getState().core.current_track ? helpers.getTrackIcon(store.getState().core.current_track, store.getState().core) : false)
}
));
break
case 'MOPIDY_CHANGE_TRACK':
instruct(socket, store, 'playback.play', {tlid: action.tlid});
var broadcast_data = {
type: 'notification',
notification_type: 'info',
content: store.getState().pusher.username +' changed track'
}
store.dispatch(pusherActions.deliverBroadcast(broadcast_data));
store.dispatch(pusherActions.deliverBroadcast(
{
type: 'notification',
notification_type: 'info',
content: store.getState().pusher.username +' changed track'
}
));
break;
case 'MOPIDY_REMOVE_TRACKS':
instruct(socket, store, 'tracklist.remove', {tlid: action.tlids});
var broadcast_data = {
type: 'notification',
notification_type: 'info',
content: store.getState().pusher.username +' removed '+action.tlids.length+' tracks'
}
store.dispatch(pusherActions.deliverBroadcast(broadcast_data));
store.dispatch(pusherActions.deliverBroadcast(
{
type: 'notification',
notification_type: 'info',
content: store.getState().pusher.username +' removed '+action.tlids.length+' tracks'
}
));
break;
case 'MOPIDY_GET_REPEAT':
instruct(socket, store, 'tracklist.getRepeat')
.then(
response => {
store.dispatch({
type: 'MOPIDY_REPEAT',
repeat: response
});
}
);
break;
case 'MOPIDY_SET_REPEAT':
instruct(socket, store, 'tracklist.setRepeat', [action.repeat]);
break;
case 'MOPIDY_GET_RANDOM':
instruct(socket, store, 'tracklist.getRandom')
.then(
response => {
store.dispatch({
type: 'MOPIDY_RANDOM',
random: response
});
}
);
break;
case 'MOPIDY_SET_RANDOM':
instruct(socket, store, 'tracklist.setRandom', [action.random]);
break;
case 'MOPIDY_GET_CONSUME':
instruct(socket, store, 'tracklist.getConsume')
.then(
response => {
store.dispatch({
type: 'MOPIDY_CONSUME',
consume: response
});
}
);
break;
case 'MOPIDY_SET_CONSUME':
instruct(socket, store, 'tracklist.setConsume', [action.consume]);
break;
case 'MOPIDY_GET_MUTE':
instruct(socket, store, 'mixer.getMute')
.then(
response => {
store.dispatch({
type: 'MOPIDY_MUTE',
mute: response
});
}
);
break;
case 'MOPIDY_SET_MUTE':
instruct(socket, store, 'mixer.setMute', [action.mute]);
store.dispatch(pusherActions.deliverBroadcast(
{
type: 'notification',
notification_type: 'info',
content: store.getState().pusher.username +(action.mute ? ' muted' : ' unmuted')+' playback'
}
));
break;
case 'MOPIDY_GET_VOLUME':
instruct(socket, store, 'playback.getVolume')
.then(
response => {
store.dispatch({
type: 'MOPIDY_VOLUME',
volume: response
});
}
);
break;
case 'MOPIDY_SET_VOLUME':
instruct(socket, store, 'playback.setVolume', {volume: action.volume});
break;
case 'MOPIDY_SEEK':
instruct(socket, store, 'playback.seek', {time_position: action.time_position});
break;
case 'MOPIDY_GET_TIME_POSITION':
instruct(socket, store, 'playback.getTimePosition')
.then(
response => {
store.dispatch({
type: 'MOPIDY_TIME_POSITION',
time_position: response
});
}
);
break;
case 'MOPIDY_GET_URI_SCHEMES':
instruct(socket, store, 'getUriSchemes')
.then(
response => {
var uri_schemes = response;
var remove = ['http','https','mms','rtmp','rtmps','rtsp','sc','yt'];
// remove all our ignored types
for(var i = 0; i < remove.length; i++){
var index = uri_schemes.indexOf(remove[i]);
if (index > -1 ) uri_schemes.splice(index, 1);
}
// append with ':' to make them a mopidy URI
for(var i = 0; i < uri_schemes.length; i++){
uri_schemes[i] = uri_schemes[i] +':';
}
// Enable Iris providers when the backend is available
if (uri_schemes.includes('spotify:')){
store.dispatch({
type: 'SPOTIFY_SET',
data: {
enabled: true
}
})
store.dispatch(spotifyActions.connect());
}
// If we haven't customised our search schemes, add all to search
if (store.getState().ui.search_uri_schemes === undefined){
store.dispatch(uiActions.set({search_uri_schemes: uri_schemes}));
}
store.dispatch({type: 'MOPIDY_URI_SCHEMES', uri_schemes: uri_schemes});
}
);
break;
/**
* Advanced playback events
**/
case 'MOPIDY_PLAY_PLAYLIST':
// Clear tracklist (if set)
@ -1775,33 +1904,43 @@ const MopidyMiddleware = (function(){
* ======================================================================================
**/
case 'MOPIDY_TLTRACKS':
store.dispatch({
type: 'QUEUE_LOADED',
tracks: helpers.formatTracks(action.data)
})
case 'MOPIDY_GET_QUEUE':
instruct(socket, store, 'tracklist.getTlTracks')
.then(
response => {
store.dispatch({
type: 'QUEUE_LOADED',
tracks: helpers.formatTracks(response)
});
}
);
break;
case 'MOPIDY_CURRENTTLTRACK':
if (action.data && action.data.track){
var track = helpers.formatTracks(action.data);
case 'MOPIDY_GET_CURRENT_TRACK':
instruct(socket, store, 'playback.getCurrentTlTrack')
.then(
response => {
if (response && response.track){
var track = helpers.formatTracks(response);
// We've got Spotify running, and it's a spotify track - go straight to the source!
if (store.getState().spotify.enabled && helpers.uriSource(track.uri) == 'spotify'){
store.dispatch(spotifyActions.getTrack(track.uri))
// We've got Spotify running, and it's a spotify track - go straight to the source!
if (store.getState().spotify.enabled && helpers.uriSource(track.uri) == 'spotify'){
store.dispatch(spotifyActions.getTrack(track.uri))
// Some other source, rely on Mopidy backends to do their work
} else {
store.dispatch(mopidyActions.getImages('tracks',[track.uri]))
}
// Some other source, rely on Mopidy backends to do their work
} else {
store.dispatch(mopidyActions.getImages('tracks',[track.uri]))
}
// Set our window title to the track title
helpers.setWindowTitle(track, store.getState().mopidy.play_state);
store.dispatch({
type: 'CURRENT_TRACK_LOADED',
current_track: track
});
}
// Set our window title to the track title
helpers.setWindowTitle(track, store.getState().mopidy.play_state);
store.dispatch({
type: 'CURRENT_TRACK_LOADED',
current_track: track
});
}
}
);
break;
case 'MOPIDY_GET_TRACK':

View File

@ -26,48 +26,48 @@ export default function reducer(mopidy = {}, action){
tlid: action.tlid
});
case 'MOPIDY_URISCHEMES_FILTERED':
case 'MOPIDY_URI_SCHEMES':
return Object.assign({}, mopidy, {
uri_schemes: action.data
uri_schemes: action.uri_schemes
});
/**
* State-oriented actions
**/
case 'MOPIDY_STATE':
case 'MOPIDY_PLAY_STATE':
return Object.assign({}, mopidy, {
play_state: action.data
play_state: action.play_state
});
case 'MOPIDY_CONSUME':
return Object.assign({}, mopidy, {
consume: action.data
consume: action.consume
});
case 'MOPIDY_RANDOM':
return Object.assign({}, mopidy, {
random: action.data
random: action.random
});
case 'MOPIDY_REPEAT':
return Object.assign({}, mopidy, {
repeat: action.data
repeat: action.repeat
});
case 'MOPIDY_VOLUME':
return Object.assign({}, mopidy, {
volume: action.data
volume: action.volume
});
case 'MOPIDY_MUTE':
return Object.assign({}, mopidy, {
mute: action.data
mute: action.mute
});
case 'MOPIDY_TIMEPOSITION':
case 'MOPIDY_TIME_POSITION':
return Object.assign({}, mopidy, {
time_position: action.data
time_position: action.time_position
});
case 'MOPIDY_HISTORY':