Process action naming convention

This commit is contained in:
James Barnsley
2020-02-13 10:41:20 +13:00
parent c4486e4007
commit 571c857814
16 changed files with 559 additions and 342 deletions

View File

@ -31,6 +31,7 @@ export default memo((props) => {
const {
body,
loading,
finished = false,
mini,
lazy,
white,
@ -38,7 +39,7 @@ export default memo((props) => {
progress = null,
} = props;
if (!loading) {
if (!loading && !finished) {
return null;
}
@ -70,6 +71,17 @@ export default memo((props) => {
);
}
if (finished) {
classNameString += ' loader--finished';
return (
<div className={classNameString}>
<div className="loader__spinner">
<Icon name="check" />
</div>
</div>
);
}
if (progress) {
classNameString += ' loader--progress';
return (

View File

@ -112,7 +112,9 @@ class Notifications extends React.Component {
renderProcess(process) {
const {
data: { total, remaining },
message,
level = 'info',
content,
description = null,
status,
closing,
key,
@ -126,28 +128,55 @@ class Notifications extends React.Component {
switch (status) {
case 'running':
return (
<div className={`notification notification--process${closing ? ' closing' : ''}`} key={key}>
<div
className={
`notification notification--${level} notification--process${closing ? ' closing' : ''}`
}
key={key}
>
<Loader
progress={progress}
loading
mini
white
/>
{message}
{content && content !== '' && <div className="notification__content">{content}</div>}
{description && description !== '' && <div className="notification__description">{description}</div>}
<Icon name="close" className="notification__close-button" onClick={() => { uiActions.cancelProcess(key); }} />
</div>
);
case 'finished':
return (
<div
className={
`notification notification--${level} notification--process${closing ? ' closing' : ''}`
}
key={key}
>
<Icon className="notification__icon" name={level === 'error' ? 'close' : 'check'} />
{content && content !== '' && <div className="notification__content">{content}</div>}
{description && description !== '' && <div className="notification__description">{description}</div>}
<Icon name="close" className="notification__close-button" onClick={() => { uiActions.closeProcess(key); }} />
</div>
);
case 'cancelling':
return (
<div className={`notification notification--process cancelling${closing ? ' closing' : ''}`} key={key}>
<div
className={
`notification notification--${level} notification--process cancelling${closing ? ' closing' : ''}`
}
key={key}
>
<Loader />
Cancelling
{content && content !== '' && <div className="notification__content">{content}</div>}
{description && description !== '' && <div className="notification__description">{description}</div>}
</div>
);
case 'cancelled':
case 'finished':
case 'completed':
default:
return null;
}

View File

@ -6,7 +6,7 @@ const coreActions = require('../core/actions');
const mopidyActions = require('../mopidy/actions');
const uiActions = require('../ui/actions');
const GoogleMiddleware = (function () {
const GoogleMiddleware = (function () {
// A Google request is an alias of the Mopidy request
const request = (store, method, params = null, response_callback = null, error_callback = null) => {
store.dispatch(
@ -16,7 +16,7 @@ const GoogleMiddleware = (function () {
response_callback,
error_callback,
),
);
);
};
return (store) => (next) => (action) => {
@ -55,10 +55,10 @@ const GoogleMiddleware = (function () {
},
));
},
);
} else if (last_run.status == 'cancelled') {
);
} else if (last_run.status === 'cancelled') {
store.dispatch(uiActions.resumeProcess('GOOGLE_LIBRARY_ALBUMS_PROCESSOR'));
} else if (last_run.status == 'finished') {
} else if (last_run.status === 'finished') {
// TODO: do we want to force a refresh?
}
@ -68,7 +68,7 @@ const GoogleMiddleware = (function () {
if (store.getState().ui.processes.GOOGLE_LIBRARY_ALBUMS_PROCESSOR !== undefined) {
const processor = store.getState().ui.processes.GOOGLE_LIBRARY_ALBUMS_PROCESSOR;
if (processor.status == 'cancelling') {
if (processor.status === 'cancelling') {
store.dispatch(uiActions.processCancelled('GOOGLE_LIBRARY_ALBUMS_PROCESSOR'));
return false;
}
@ -88,7 +88,7 @@ const GoogleMiddleware = (function () {
));
store.dispatch(mopidyActions.getAlbums(uris_to_load, { name: 'GOOGLE_LIBRARY_ALBUMS_PROCESSOR', data: { uris } }));
} else {
store.dispatch(uiActions.processFinishing('GOOGLE_LIBRARY_ALBUMS_PROCESSOR'));
store.dispatch(uiActions.processFinished('GOOGLE_LIBRARY_ALBUMS_PROCESSOR'));
}
break;
@ -106,7 +106,7 @@ const GoogleMiddleware = (function () {
}
const uris = [];
for (let i = 0; i < response.length; i++) {
for (let i = 0; i < response.length; i++) {
// Convert local URI to actual artist URI
// See https://github.com/mopidy/mopidy-local-sqlite/issues/39
response[i].uri = response[i].uri.replace('local:directory?albumartist=', '');
@ -134,7 +134,7 @@ const GoogleMiddleware = (function () {
default:
return next(action);
}
};
};
}());
export default GoogleMiddleware;

View File

@ -784,7 +784,7 @@ const MopidyMiddleware = (function () {
// no batches means we're done here
} else {
store.dispatch(uiActions.processFinishing('MOPIDY_ENQUEUE_URIS_PROCESSOR'));
store.dispatch(uiActions.processFinished('MOPIDY_ENQUEUE_URIS_PROCESSOR'));
break;
}
@ -1013,7 +1013,7 @@ const MopidyMiddleware = (function () {
// No more schemes, so we're done!
} if (!action.data.uri_scheme) {
store.dispatch(uiActions.processFinishing('MOPIDY_GET_SEARCH_RESULTS_PROCESSOR'));
store.dispatch(uiActions.processFinished('MOPIDY_GET_SEARCH_RESULTS_PROCESSOR'));
return;
}
@ -1204,7 +1204,7 @@ const MopidyMiddleware = (function () {
));
var continue_process = () => {
store.dispatch(uiActions.processFinishing('MOPIDY_GET_SEARCH_RESULTS_PROCESSOR'));
store.dispatch(uiActions.processFinished('MOPIDY_GET_SEARCH_RESULTS_PROCESSOR'));
};
request(socket, store, 'playlists.asList')
@ -1877,7 +1877,7 @@ const MopidyMiddleware = (function () {
));
store.dispatch(mopidyActions.getAlbums(uris_to_load, { name: 'MOPIDY_LIBRARY_ALBUMS_PROCESSOR', data: { uris } }));
} else {
store.dispatch(uiActions.processFinishing('MOPIDY_LIBRARY_ALBUMS_PROCESSOR'));
store.dispatch(uiActions.processFinished('MOPIDY_LIBRARY_ALBUMS_PROCESSOR'));
}
break;
@ -2066,7 +2066,7 @@ const MopidyMiddleware = (function () {
store.dispatch(uiActions.updateProcess('MOPIDY_LIBRARY_ARTISTS_PROCESSOR', 'Loading '+uris.length+' local artists', {uris: uris}));
store.dispatch(mopidyActions.getArtists(uris_to_load, {name: 'MOPIDY_LIBRARY_ARTISTS_PROCESSOR', data: {uris: uris}}));
} else {
store.dispatch(uiActions.processFinishing('MOPIDY_LIBRARY_ARTISTS_PROCESSOR'));
store.dispatch(uiActions.processFinished('MOPIDY_LIBRARY_ARTISTS_PROCESSOR'));
}
break;

View File

@ -60,119 +60,145 @@ const PusherMiddleware = (function () {
));
}
// General broadcast received
// Broadcast of an error
} else if (message.error !== undefined) {
store.dispatch(coreActions.handleException(
`Pusher: ${message.error.message}`,
message,
(message.error.data !== undefined && message.error.data.description !== undefined
? message.error.data.description
: null
),
));
// General broadcast received
} else {
// Broadcast of an error
if (message.error !== undefined) {
store.dispatch(coreActions.handleException(
`Pusher: ${message.error.message}`,
message,
(message.error.data !== undefined && message.error.data.description !== undefined ? message.error.data.description : null),
));
} else {
switch (message.method) {
case 'connection_added':
store.dispatch(pusherActions.connectionAdded(message.params.connection));
break;
case 'connection_changed':
store.dispatch(pusherActions.connectionChanged(message.params.connection));
break;
case 'connection_removed':
store.dispatch(pusherActions.connectionRemoved(message.params.connection));
break;
case 'queue_metadata_changed':
store.dispatch(pusherActions.queueMetadataChanged(message.params.queue_metadata));
break;
case 'spotify_token_changed':
store.dispatch(spotifyActions.tokenChanged(message.params.spotify_token));
break;
case 'share_configuration_received':
store.dispatch(uiActions.createNotification({
type: 'share-configuration-received',
configuration: message.params,
sticky: true,
}));
break;
case 'notification':
store.dispatch(uiActions.createNotification(message.params.notification));
break;
case 'radio_started':
store.dispatch(pusherActions.radioStarted(message.params.radio));
break;
case 'radio_changed':
store.dispatch(pusherActions.radioChanged(message.params.radio));
break;
case 'radio_stopped':
store.dispatch(pusherActions.radioStopped());
break;
case 'commands_changed':
store.dispatch(pusherActions.commandsUpdated(message.params.commands));
break;
case 'reload':
window.location.reload(true);
break;
const params = message.params ? message.params : {};
switch (message.method) {
case 'connection_added':
store.dispatch(pusherActions.connectionAdded(params.connection));
break;
case 'connection_changed':
store.dispatch(pusherActions.connectionChanged(params.connection));
break;
case 'connection_removed':
store.dispatch(pusherActions.connectionRemoved(params.connection));
break;
case 'queue_metadata_changed':
store.dispatch(pusherActions.queueMetadataChanged(params.queue_metadata));
break;
case 'spotify_token_changed':
store.dispatch(spotifyActions.tokenChanged(params.spotify_token));
break;
case 'share_configuration_received':
store.dispatch(uiActions.createNotification({
type: 'share-configuration-received',
configuration: params,
sticky: true,
}));
break;
case 'notification':
store.dispatch(uiActions.createNotification(params.notification));
break;
case 'radio_started':
store.dispatch(pusherActions.radioStarted(params.radio));
break;
case 'radio_changed':
store.dispatch(pusherActions.radioChanged(params.radio));
break;
case 'radio_stopped':
store.dispatch(pusherActions.radioStopped());
break;
case 'commands_changed':
store.dispatch(pusherActions.commandsUpdated(params.commands));
break;
case 'reload':
window.location.reload(true);
break;
// Local scan
case 'local_scan_started':
store.dispatch(uiActions.updateProcess('local_scan', 'Scanning local library'));
break;
case 'local_scan_updated':
store.dispatch(uiActions.updateProcess('local_scan', 'Scanning local library', {}, message.params.output));
break;
case 'local_scan_finished':
store.dispatch(uiActions.processFinished('local_scan'));
store.dispatch(uiActions.createNotification({
key: 'local_scan', type: 'info', content: 'Local scan finished', description: message.params.output,
}));
break;
case 'local_scan_error':
store.dispatch(uiActions.processFinished('local_scan'));
store.dispatch(coreActions.handleException('Local scan failed', message, message.params.error));
break;
// Local scan
case 'local_scan_started':
store.dispatch(uiActions.updateProcess('local_scan', 'Scanning local library'));
break;
case 'local_scan_updated':
store.dispatch(uiActions.updateProcess('local_scan', 'Scanning local library', {}, params.output));
break;
case 'local_scan_finished':
store.dispatch(uiActions.processFinished(
'local_scan',
{
content: 'Local scan finished', description: params.output, sticky: true,
},
));
break;
case 'local_scan_error':
store.dispatch(uiActions.processFinished(
'local_scan',
{
level: 'error', content: 'Local scan failed', description: params.error, sticky: true,
},
));
break;
// Upgrade
case 'upgrade_started':
store.dispatch(uiActions.updateProcess('upgrade', 'Upgrading'));
break;
case 'upgrade_updated':
store.dispatch(uiActions.updateProcess('upgrade', 'Upgrading', {}, message.params.output));
break;
case 'upgrade_finished':
store.dispatch(uiActions.updateProcess('upgrade', 'Restarting to complete upgrade'));
break;
case 'upgrade_error':
store.dispatch(uiActions.processFinished('upgrade'));
store.dispatch(coreActions.handleException('Upgrade failed', message, message.params.error));
break;
// Upgrade
case 'upgrade_started':
store.dispatch(uiActions.updateProcess('upgrade', 'Upgrading'));
break;
case 'upgrade_updated':
store.dispatch(uiActions.updateProcess('upgrade', 'Upgrading', {}, params.output));
break;
case 'upgrade_finished':
store.dispatch(uiActions.updateProcess('upgrade', 'Restarting to complete upgrade'));
break;
case 'upgrade_error':
store.dispatch(uiActions.processFinished(
'upgrade',
{
level: 'error', content: 'Upgrade failed', description: params.error, sticky: true,
},
));
break;
// Restart
case 'restart_started':
store.dispatch(uiActions.processFinished('upgrade', 'Restarting'));
break;
case 'restart_updated':
store.dispatch(uiActions.updateProcess('upgrade', 'Restarting', {}, message.params.output));
break;
case 'restart_error':
store.dispatch(uiActions.processFinished('upgrade'));
store.dispatch(coreActions.handleException('Restart failed', message, message.params.error));
break;
// Restart
case 'restart_started':
store.dispatch(uiActions.removeProcess('upgrade', 'Restarting'));
break;
case 'restart_updated':
store.dispatch(uiActions.updateProcess('upgrade', 'Restarting', {}, params.output));
break;
case 'restart_error':
store.dispatch(uiActions.processFinished(
'upgrade',
{
level: 'error', content: 'Restart failed', description: params.error, sticky: true,
},
));
break;
// Test
case 'test_started':
store.dispatch(uiActions.updateProcess('test', 'Running test', {}, message.params.output));
break;
case 'test_updated':
store.dispatch(uiActions.updateProcess('test', 'Running test'));
break;
case 'test_finished':
store.dispatch(uiActions.processFinished('test'));
store.dispatch(uiActions.createNotification({ type: 'info', content: 'Test finished', description: message.params.output }));
break;
case 'test_error':
store.dispatch(uiActions.processFinished('test'));
store.dispatch(uiActions.createNotification({ type: 'bad', content: message.params.message, description: message.params.error }));
break;
}
// Test
case 'test_started':
store.dispatch(uiActions.updateProcess('test', 'Running test', {}, params.output));
break;
case 'test_updated':
store.dispatch(uiActions.updateProcess('test', 'Running test'));
break;
case 'test_finished':
store.dispatch(uiActions.processFinished(
'test',
{
content: 'Test finished', description: params.output, sticky: true,
},
));
break;
case 'test_error':
store.dispatch(uiActions.removeProcess(
'test',
{
level: 'error', content: params.message, description: params.error,
},
));
break;
default:
break;
}
}
};
@ -522,13 +548,13 @@ const PusherMiddleware = (function () {
request(store, 'run_command', { id: action.id })
.then((response) => {
console.log('Command response', response);
store.dispatch(uiActions.processFinished(notification_key));
store.dispatch(uiActions.removeProcess(notification_key));
if (action.notify) {
store.dispatch(uiActions.createNotification({ key: notification_key, type: 'info', content: 'Command sent' }));
}
},
(error) => {
store.dispatch(uiActions.processFinished(notification_key));
store.dispatch(uiActions.removeProcess(notification_key));
store.dispatch(coreActions.handleException(
'Could not run command',
error,
@ -613,14 +639,14 @@ const PusherMiddleware = (function () {
request(store, 'change_radio', data)
.then(
(response) => {
store.dispatch(uiActions.processFinishing('PUSHER_RADIO_PROCESS'));
store.dispatch(uiActions.processFinished('PUSHER_RADIO_PROCESS'));
if (response.status == 0) {
store.dispatch(uiActions.createNotification({ content: response.message, type: 'bad' }));
}
store.dispatch(pusherActions.radioChanged(response.radio));
},
(error) => {
store.dispatch(uiActions.processFinishing('PUSHER_RADIO_PROCESS'));
store.dispatch(uiActions.processFinished('PUSHER_RADIO_PROCESS'));
store.dispatch(coreActions.handleException(
'Could not change radio',
error,

View File

@ -619,7 +619,7 @@ export function getSearchResults(type, query, limit = 50, offset = 0) {
});
}
dispatch(uiActions.processFinishing('SPOTIFY_GET_SEARCH_RESULTS_PROCESSOR'));
dispatch(uiActions.processFinished('SPOTIFY_GET_SEARCH_RESULTS_PROCESSOR'));
},
(error) => {
dispatch(coreActions.handleException(
@ -1573,7 +1573,7 @@ export function getLibraryTracksAndPlayProcessor(data) {
));
} else {
dispatch(mopidyActions.playURIs(uris, data.uri));
dispatch(uiActions.processFinishing('SPOTIFY_GET_LIBRARY_TRACKS_AND_PLAY_PROCESSOR'));
dispatch(uiActions.processFinished('SPOTIFY_GET_LIBRARY_TRACKS_AND_PLAY_PROCESSOR'));
}
},
(error) => {
@ -1669,7 +1669,7 @@ export function getAllPlaylistTracksProcessor(data) {
// We don't bother "finishing", we just want it "finished" immediately
// This bypasses the fade transition for a more smooth transition between two
// processes that flow together
dispatch(uiActions.processFinished('SPOTIFY_GET_ALL_PLAYLIST_TRACKS_PROCESSOR'));
dispatch(uiActions.removeProcess('SPOTIFY_GET_ALL_PLAYLIST_TRACKS_PROCESSOR'));
if (data.callback_action == 'enqueue') {
dispatch(mopidyActions.enqueueURIs(uris, data.uri, data.play_next, data.at_position, data.offset));
@ -1827,7 +1827,7 @@ export function getLibraryPlaylistsProcessor(data) {
));
dispatch(uiActions.runProcess('SPOTIFY_GET_LIBRARY_PLAYLISTS_PROCESSOR', { next: response.next }));
} else {
dispatch(uiActions.processFinishing('SPOTIFY_GET_LIBRARY_PLAYLISTS_PROCESSOR'));
dispatch(uiActions.processFinished('SPOTIFY_GET_LIBRARY_PLAYLISTS_PROCESSOR'));
dispatch({ type: 'SPOTIFY_LIBRARY_PLAYLISTS_LOADED_ALL' });
}
},
@ -1898,7 +1898,7 @@ export function getLibraryArtistsProcessor(data) {
));
dispatch(uiActions.runProcess('SPOTIFY_GET_LIBRARY_ARTISTS_PROCESSOR', { next: response.artists.next }));
} else {
dispatch(uiActions.processFinishing('SPOTIFY_GET_LIBRARY_ARTISTS_PROCESSOR'));
dispatch(uiActions.processFinished('SPOTIFY_GET_LIBRARY_ARTISTS_PROCESSOR'));
}
},
(error) => {
@ -1968,7 +1968,7 @@ export function getLibraryAlbumsProcessor(data) {
));
dispatch(uiActions.runProcess('SPOTIFY_GET_LIBRARY_ALBUMS_PROCESSOR', { next: response.next }));
} else {
dispatch(uiActions.processFinishing('SPOTIFY_GET_LIBRARY_ALBUMS_PROCESSOR'));
dispatch(uiActions.processFinished('SPOTIFY_GET_LIBRARY_ALBUMS_PROCESSOR'));
}
},
(error) => {

View File

@ -228,30 +228,31 @@ export function stopLoading(key) {
};
}
export function startProcess(key, message, data = {}, description = null) {
export function startProcess(key, content, data = {}, description = null) {
return {
type: 'START_PROCESS',
key,
message,
data,
content,
description,
};
}
export function resumeProcess(key, message, data = {}) {
export function resumeProcess(key) {
return {
type: 'RESUME_PROCESS',
key,
};
}
export function updateProcess(key, message, data = {}, description = null) {
export function updateProcess(key, content, data = {}, description = null, level = 'info') {
return {
type: 'UPDATE_PROCESS',
key,
message,
content,
data,
description,
level,
};
}
@ -276,16 +277,24 @@ export function processCancelled(key) {
};
}
export function processFinishing(key) {
export function processFinished(key, completionMessage = null) {
return {
type: 'PROCESS_FINISHING',
type: 'PROCESS_FINISHED',
key,
completionMessage,
};
}
export function closeProcess(key) {
return {
type: 'CLOSE_PROCESS',
key,
};
}
export function processFinished(key) {
export function removeProcess(key) {
return {
type: 'PROCESS_FINISHED',
type: 'REMOVE_PROCESS',
key,
};
}

View File

@ -5,12 +5,12 @@ import * as helpers from '../../helpers';
const uiActions = require('./actions.js');
const mopidyActions = require('../mopidy/actions.js');
const UIMiddleware = (function () {
const UIMiddleware = (function () {
/**
* The actual middleware inteceptor
* */
return (store) => (next) => (action) => {
switch (action.type) {
switch (action.type) {
case 'MOPIDY_STATE':
// Get the current track from our index
@ -117,7 +117,7 @@ const UIMiddleware = (function () {
// start a timeout to close this notification
if (!action.notification.sticky) {
var timeout = setTimeout(
setTimeout(
() => {
store.dispatch(uiActions.closeNotification(action.notification.key));
},
@ -133,7 +133,7 @@ const UIMiddleware = (function () {
// start a timeout to remove this notification
// This gives us time to animate out the notification before we remove the data
var timeout = setTimeout(
setTimeout(
() => {
store.dispatch(uiActions.removeNotification(action.key));
},
@ -214,32 +214,51 @@ const UIMiddleware = (function () {
next(action);
break;
case 'PROCESS_FINISHING':
// start a timeout to remove this notification
// This gives us time to animate out the notification before we remove the data
var timeout = setTimeout(
() => {
store.dispatch(uiActions.processFinished(action.key));
},
200,
);
next(action);
break;
case 'PROCESS_FINISHED':
store.dispatch({
type: `${action.key}_FINISHED`,
});
// start a timeout to remove this notification
// This gives us time to animate out the notification before we remove the data
if (action.completionMessage) {
store.dispatch(uiActions.updateProcess(
action.key,
action.completionMessage.content,
{},
action.completionMessage.description,
action.completionMessage.level,
));
if (!action.completionMessage.sticky) {
setTimeout(
() => {
store.dispatch(uiActions.closeProcess(action.key));
},
5000,
);
}
} else {
store.dispatch(uiActions.closeProcess(action.key));
}
next(action);
break;
// This action is irrelevant to us, pass it on to the next middleware
case 'CLOSE_PROCESS':
setTimeout(
() => {
store.dispatch(uiActions.removeProcess(action.key));
},
200,
);
next(action);
break;
// This action is irrelevant to us, pass it on to the next middleware
default:
return next(action);
}
};
};
}());
export default UIMiddleware;
export default UIMiddleware;

View File

@ -162,9 +162,10 @@ export default function reducer(ui = {}, action) {
}
processes[action.key] = {
key: action.key,
message: action.message,
content: action.content,
description: action.description,
status: 'running',
level: action.level,
data,
};
return { ...ui, processes };
@ -190,17 +191,24 @@ export default function reducer(ui = {}, action) {
}
return { ...ui, processes };
case 'PROCESS_FINISHING':
case 'PROCESS_FINISHED':
var processes = { ...(ui.processes ? ui.processes : {}) };
if (processes[action.key]) {
processes[action.key] = { ...processes[action.key], status: 'finished' };
}
return { ...ui, processes };
case 'CLOSE_PROCESS':
var processes = { ...(ui.processes ? ui.processes : {}) };
if (processes[action.key]) {
processes[action.key] = { ...processes[action.key], closing: true };
}
return { ...ui, processes };
case 'PROCESS_FINISHED':
case 'REMOVE_PROCESS':
var processes = { ...(ui.processes ? ui.processes : {}) };
if (processes[action.key]) {
processes[action.key] = { ...processes[action.key], status: 'finished', closing: false };
processes[action.key] = { ...processes[action.key], status: 'completed', closing: false };
}
return { ...ui, processes };