Cleaning close modal into actions; Pushing shared config to server
This commit is contained in:
@ -30,6 +30,7 @@ import Search from './views/Search';
|
||||
import Settings from './views/Settings';
|
||||
import Discover from './views/Discover/Discover';
|
||||
import Library from './views/Library/Library';
|
||||
import ModalStateListener from './components/ModalStateListener';
|
||||
import Modals from './views/Modals/Modals';
|
||||
|
||||
import { scrollTo, isTouchDevice } from './util/helpers';
|
||||
@ -251,6 +252,7 @@ const App = () => {
|
||||
options={{ enableMouseEvents: true }}
|
||||
>
|
||||
<div className="body">
|
||||
<ModalStateListener />
|
||||
<Switch>
|
||||
<Route
|
||||
path="/modal"
|
||||
|
||||
16
src/js/components/ModalStateListener.js
Normal file
16
src/js/components/ModalStateListener.js
Normal file
@ -0,0 +1,16 @@
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
|
||||
const ModalStateListener = () => {
|
||||
const { name } = useSelector((state) => state.ui.modal || {});
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
|
||||
if (name && location.pathname.indexOf(`modal/${name}`) < 0) {
|
||||
history.push(`modal/${name}`);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export default ModalStateListener;
|
||||
@ -106,6 +106,13 @@ export function getConfig() {
|
||||
};
|
||||
}
|
||||
|
||||
export function setSharedConfig(shared_config) {
|
||||
return {
|
||||
type: 'PUSHER_SET_SHARED_CONFIG',
|
||||
shared_config,
|
||||
};
|
||||
}
|
||||
|
||||
export function getVersion() {
|
||||
return {
|
||||
type: 'PUSHER_GET_VERSION',
|
||||
|
||||
@ -471,6 +471,25 @@ const PusherMiddleware = (function () {
|
||||
);
|
||||
break;
|
||||
|
||||
case 'PUSHER_SET_SHARED_CONFIG': {
|
||||
const { shared_config } = action;
|
||||
request(store, 'set_shared_config', { shared_config })
|
||||
.then(
|
||||
() => {
|
||||
// No action required, the change will be broadcast
|
||||
},
|
||||
(error) => {
|
||||
store.dispatch(coreActions.handleException(
|
||||
'Could not share to server',
|
||||
error,
|
||||
));
|
||||
},
|
||||
);
|
||||
|
||||
next(action);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'PUSHER_GET_CONNECTIONS':
|
||||
request(store, 'get_connections')
|
||||
.then(
|
||||
|
||||
@ -269,7 +269,17 @@ export function openModal(name, data) {
|
||||
return {
|
||||
type: 'OPEN_MODAL',
|
||||
name,
|
||||
data,
|
||||
data: {
|
||||
name,
|
||||
...data,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function closeModal(url) {
|
||||
return {
|
||||
type: 'CLOSE_MODAL',
|
||||
url,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -36,11 +36,12 @@ const UIMiddleware = (function () {
|
||||
next(action);
|
||||
break;
|
||||
|
||||
case 'OPEN_MODAL':
|
||||
const { name } = action;
|
||||
window.location = `/iris/modal/${name}`;
|
||||
console.debug(action);
|
||||
next(action); // Save data to state
|
||||
case 'CLOSE_MODAL':
|
||||
if (action.url) {
|
||||
window.location = action.url;
|
||||
} else {
|
||||
window.history.back();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'HIDE_CONTEXT_MENU':
|
||||
|
||||
@ -78,6 +78,9 @@ export default function reducer(ui = {}, action) {
|
||||
case 'OPEN_MODAL':
|
||||
return { ...ui, modal: action.data };
|
||||
|
||||
case 'CLOSE_MODAL':
|
||||
return { ...ui, modal: null };
|
||||
|
||||
/**
|
||||
* Notifications
|
||||
* */
|
||||
|
||||
@ -52,10 +52,13 @@ class AddToPlaylist extends React.Component {
|
||||
coreActions: {
|
||||
addTracksToPlaylist,
|
||||
},
|
||||
uiActions: {
|
||||
closeModal,
|
||||
},
|
||||
uris,
|
||||
} = this.props;
|
||||
addTracksToPlaylist(playlist_uri, uris);
|
||||
window.history.back();
|
||||
closeModal();
|
||||
}
|
||||
|
||||
renderList = () => {
|
||||
|
||||
@ -76,6 +76,9 @@ class AddToQueue extends React.Component {
|
||||
mopidyActions: {
|
||||
enqueueURIs,
|
||||
},
|
||||
uiActions: {
|
||||
closeModal,
|
||||
},
|
||||
view: {
|
||||
uris = [],
|
||||
},
|
||||
@ -83,8 +86,7 @@ class AddToQueue extends React.Component {
|
||||
|
||||
e.preventDefault();
|
||||
enqueueURIs({ uris, play_next });
|
||||
this.reset();
|
||||
window.history.back();
|
||||
closeModal();
|
||||
}
|
||||
|
||||
onChange = (text) => {
|
||||
|
||||
@ -38,7 +38,7 @@ const CreatePlaylist = () => {
|
||||
dispatch(
|
||||
coreActions.createPlaylist({ ...playlist, tracks_uris }),
|
||||
);
|
||||
window.history.back();
|
||||
dispatch(uiActions.closeModal());
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@ class EditCommand extends React.Component {
|
||||
|
||||
this.props.pusherActions.setCommand(this.state);
|
||||
|
||||
window.history.back();
|
||||
this.props.uiActions.closeModal();
|
||||
|
||||
// A bit hacky, but wait for a moment to allow the back navigation
|
||||
// and then scroll down to our commands list
|
||||
|
||||
@ -111,6 +111,9 @@ class EditPlaylist extends React.Component {
|
||||
coreActions: {
|
||||
savePlaylist,
|
||||
},
|
||||
uiActions: {
|
||||
closeModal,
|
||||
},
|
||||
} = this.props;
|
||||
|
||||
e.preventDefault();
|
||||
@ -127,7 +130,7 @@ class EditPlaylist extends React.Component {
|
||||
collaborative,
|
||||
image,
|
||||
);
|
||||
window.history.back();
|
||||
closeModal();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -96,7 +96,7 @@ class EditRadio extends React.Component {
|
||||
|
||||
if (valid_seeds) {
|
||||
this.props.pusherActions.startRadio(this.state.seeds);
|
||||
window.history.back();
|
||||
this.props.uiActions.closeModal();
|
||||
} else {
|
||||
this.setState({ error_message: i18n('modal.edit_radio.invalid_seed_uri') });
|
||||
}
|
||||
@ -116,7 +116,7 @@ class EditRadio extends React.Component {
|
||||
|
||||
if (valid_seeds) {
|
||||
this.props.pusherActions.updateRadio(this.state.seeds);
|
||||
window.history.back();
|
||||
this.props.uiActions.closeModal();
|
||||
} else {
|
||||
this.setState({ error_message: i18n('modal.edit_radio.invalid_seed_uri') });
|
||||
}
|
||||
|
||||
@ -2,7 +2,12 @@ import React, { useState, useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { pick } from 'lodash';
|
||||
import Modal from './Modal';
|
||||
import { setWindowTitle, set as setUi, createNotification } from '../../services/ui/actions';
|
||||
import {
|
||||
setWindowTitle,
|
||||
set as setUi,
|
||||
createNotification,
|
||||
closeModal,
|
||||
} from '../../services/ui/actions';
|
||||
import * as spotifyActions from '../../services/spotify/actions';
|
||||
import * as snapcastActions from '../../services/snapcast/actions';
|
||||
import * as lastfmActions from '../../services/lastfm/actions';
|
||||
@ -61,7 +66,7 @@ const ImportConfiguration = () => {
|
||||
content: i18n('modal.share_configuration.import.successful'),
|
||||
}));
|
||||
|
||||
window.history.back();
|
||||
dispatch(closeModal());
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@ -69,6 +69,7 @@ class InitialSetup extends React.Component {
|
||||
|
||||
// Wait a jiffy to allow changes to apply to store
|
||||
setTimeout(() => {
|
||||
self.props.uiActions.closeModal();
|
||||
// We've changed a connection setting, so need to reload
|
||||
if (self.state.host !== self.props.host || self.state.port !== self.props.port) {
|
||||
window.location = '/';
|
||||
@ -150,7 +151,7 @@ class InitialSetup extends React.Component {
|
||||
<I18n path="settings.interface.reporting.description" />
|
||||
<a href="https://github.com/jaedb/Iris/wiki/Terms-of-use#privacy-policy" target="_blank">
|
||||
<I18n path="settings.interface.reporting.privacy_policy" />
|
||||
</a>
|
||||
</a>
|
||||
.
|
||||
{!this.state.allow_reporting && (
|
||||
<span className="red-text">
|
||||
|
||||
@ -1,48 +1,44 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { closeModal } from '../../services/ui/actions';
|
||||
import Icon from '../../components/Icon';
|
||||
|
||||
class Modal extends React.Component {
|
||||
componentDidMount() {
|
||||
const Modal = ({
|
||||
extraControls,
|
||||
noclose,
|
||||
children,
|
||||
className = '',
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
$('body').addClass('modal-open');
|
||||
return () => {
|
||||
$('body').removeClass('modal-open');
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onClose = () => {
|
||||
dispatch(closeModal());
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
$('body').removeClass('modal-open');
|
||||
}
|
||||
return (
|
||||
<div className={`modal ${className}`}>
|
||||
|
||||
render() {
|
||||
const {
|
||||
extraControls = null,
|
||||
noclose = false,
|
||||
children,
|
||||
className = '',
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<div className={`modal ${className}`}>
|
||||
|
||||
<div className="controls">
|
||||
{extraControls}
|
||||
{!noclose && (
|
||||
<div className="control close" onClick={(e) => window.history.back()}>
|
||||
<Icon name="close" className="white" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="content">
|
||||
{children}
|
||||
</div>
|
||||
<div className="controls">
|
||||
{extraControls}
|
||||
{!noclose && (
|
||||
<div className="control close" onClick={onClose}>
|
||||
<Icon name="close" className="white" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
<div className="content">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => ({
|
||||
shortkeys_enabled: state.ui.shortkeys_enabled,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Modal);
|
||||
export default Modal;
|
||||
|
||||
@ -31,6 +31,9 @@ class Reset extends React.Component {
|
||||
coreActions: {
|
||||
resetState,
|
||||
},
|
||||
uiActions: {
|
||||
closeModal,
|
||||
},
|
||||
} = this.props;
|
||||
const {
|
||||
preferences,
|
||||
@ -137,7 +140,7 @@ class Reset extends React.Component {
|
||||
console.log('Reset complete, refreshing...');
|
||||
resetState(stateKeysToReset);
|
||||
setTimeout(
|
||||
() => window.location = `/iris/settings${test_mode ? '?test_mode=0' : ''}`,
|
||||
() => closeModal(window.location = `/iris/settings${test_mode ? '?test_mode=0' : ''}`),
|
||||
1000,
|
||||
);
|
||||
});
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { pick } from 'lodash';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import Modal from './Modal';
|
||||
import { setWindowTitle } from '../../services/ui/actions';
|
||||
import { deliverMessage } from '../../services/pusher/actions';
|
||||
import { setWindowTitle, closeModal } from '../../services/ui/actions';
|
||||
import { deliverMessage, setSharedConfig } from '../../services/pusher/actions';
|
||||
import { i18n, I18n } from '../../locale';
|
||||
import Button from '../../components/Button';
|
||||
import { indexToArray } from '../../util/arrays';
|
||||
@ -26,8 +27,8 @@ const RecipientsList = ({
|
||||
<input
|
||||
type="checkbox"
|
||||
name="connection_server"
|
||||
checked={selected.includes('server')}
|
||||
onChange={() => onChange('server')}
|
||||
checked={selected.includes('SERVER')}
|
||||
onChange={() => onChange('SERVER')}
|
||||
/>
|
||||
<div className="label">
|
||||
<div>
|
||||
@ -68,7 +69,7 @@ const RecipientsList = ({
|
||||
const ShareConfiguration = () => {
|
||||
const dispatch = useDispatch();
|
||||
const [recipients, setRecipients] = useState([]);
|
||||
const [configuration, setConfiguration] = useState({});
|
||||
const [selectedConfigs, setSelectedConfigs] = useState([]);
|
||||
const spotify = useSelector((state) => state.spotify);
|
||||
const genius = useSelector((state) => state.genius);
|
||||
const lastfm = useSelector((state) => state.lastfm);
|
||||
@ -92,64 +93,79 @@ const ShareConfiguration = () => {
|
||||
});
|
||||
}
|
||||
|
||||
const onConfigurationChanged = (id) => {
|
||||
setConfiguration((prev) => {
|
||||
const next = { ...prev };
|
||||
|
||||
if (next.id) {
|
||||
delete next[id];
|
||||
const onSelectedConfigChanged = (id) => {
|
||||
setSelectedConfigs((prev) => {
|
||||
const next = [...prev];
|
||||
if (next.includes(id)) {
|
||||
const index = next.indexOf(id);
|
||||
next.splice(index, 1);
|
||||
} else {
|
||||
switch (id) {
|
||||
case 'spotify':
|
||||
next.spotify = {
|
||||
authorization: spotify.authorization,
|
||||
me: spotify.me,
|
||||
};
|
||||
break;
|
||||
case 'genius':
|
||||
next.genius = {
|
||||
authorization: genius.authorization,
|
||||
me: genius.me,
|
||||
};
|
||||
break;
|
||||
case 'lastfm':
|
||||
next.lastfm = {
|
||||
authorization: lastfm.authorization,
|
||||
me: lastfm.me,
|
||||
};
|
||||
break;
|
||||
case 'ui':
|
||||
next.ui = ui;
|
||||
break;
|
||||
case 'snapcast':
|
||||
next.snapcast = {
|
||||
enabled: snapcast.enabled,
|
||||
host: snapcast.host,
|
||||
port: snapcast.port,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
next.push(id);
|
||||
}
|
||||
return next;
|
||||
});
|
||||
}
|
||||
|
||||
const collateConfig = () => {
|
||||
const obj = {};
|
||||
selectedConfigs.forEach((name) => {
|
||||
switch (name) {
|
||||
case 'spotify':
|
||||
obj.spotify = pick(spotify, ['authorization', 'me']);
|
||||
break;
|
||||
case 'genius':
|
||||
obj.genius = pick(genius, ['authorization', 'me']);
|
||||
break;
|
||||
case 'lastfm':
|
||||
obj.lastfm = pick(lastfm, ['authorization', 'me']);
|
||||
break;
|
||||
case 'snapcast':
|
||||
obj.snapcast = pick(snapcast, ['enabled', 'host', 'port', 'ssl']);
|
||||
break;
|
||||
case 'ui':
|
||||
obj.ui = pick(
|
||||
ui,
|
||||
[
|
||||
'language',
|
||||
'theme',
|
||||
'smooth_scrolling_enabled',
|
||||
'hotkeys_enabled',
|
||||
'allow_reporting',
|
||||
'wide_scrollbars_enabled',
|
||||
'hide_scrollbars',
|
||||
'grid_glow_enabled',
|
||||
'sort',
|
||||
'initial_setup_complete',
|
||||
'uri_schemes_search_enabled',
|
||||
],
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
};
|
||||
|
||||
const onSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
const configuration = collateConfig();
|
||||
|
||||
for (const recipient of recipients) {
|
||||
dispatch(
|
||||
deliverMessage(
|
||||
recipient,
|
||||
'share_configuration_received',
|
||||
configuration,
|
||||
),
|
||||
);
|
||||
if (recipient === 'SERVER') {
|
||||
dispatch(setSharedConfig(configuration));
|
||||
} else {
|
||||
dispatch(
|
||||
deliverMessage(
|
||||
recipient,
|
||||
'share_configuration_received',
|
||||
configuration,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
window.history.back();
|
||||
dispatch(closeModal());
|
||||
}
|
||||
|
||||
return (
|
||||
@ -185,8 +201,8 @@ const ShareConfiguration = () => {
|
||||
<input
|
||||
type="checkbox"
|
||||
name="spotify"
|
||||
checked={configuration.spotify}
|
||||
onChange={() => onConfigurationChanged('spotify')}
|
||||
checked={selectedConfigs.indexOf('spotify') > -1}
|
||||
onChange={() => onSelectedConfigChanged('spotify')}
|
||||
/>
|
||||
<div className="label">
|
||||
<div>
|
||||
@ -214,8 +230,8 @@ const ShareConfiguration = () => {
|
||||
<input
|
||||
type="checkbox"
|
||||
name="lastfm_authorization"
|
||||
checked={configuration.lastfm}
|
||||
onChange={() => onConfigurationChanged('lastfm')}
|
||||
checked={selectedConfigs.indexOf('lastfm') > -1}
|
||||
onChange={() => onSelectedConfigChanged('lastfm')}
|
||||
/>
|
||||
<div className="label">
|
||||
<div>
|
||||
@ -243,8 +259,8 @@ const ShareConfiguration = () => {
|
||||
<input
|
||||
type="checkbox"
|
||||
name="genius_authorization"
|
||||
checked={configuration.genius}
|
||||
onChange={() => onConfigurationChanged('genius')}
|
||||
checked={selectedConfigs.indexOf('genius') > -1}
|
||||
onChange={() => onSelectedConfigChanged('genius')}
|
||||
/>
|
||||
<div className="label">
|
||||
<div>
|
||||
@ -271,8 +287,8 @@ const ShareConfiguration = () => {
|
||||
<input
|
||||
type="checkbox"
|
||||
name="snapcast"
|
||||
checked={configuration.snapcast}
|
||||
onChange={() => onConfigurationChanged('snapcast')}
|
||||
checked={selectedConfigs.indexOf('snapcast') > -1}
|
||||
onChange={() => onSelectedConfigChanged('snapcast')}
|
||||
/>
|
||||
<div className="label">
|
||||
<div>
|
||||
@ -292,8 +308,8 @@ const ShareConfiguration = () => {
|
||||
<input
|
||||
type="checkbox"
|
||||
name="interface"
|
||||
checked={configuration.ui}
|
||||
onChange={() => onConfigurationChanged('ui')}
|
||||
checked={selectedConfigs.indexOf('ui') > -1}
|
||||
onChange={() => onSelectedConfigChanged('ui')}
|
||||
/>
|
||||
<div className="label">
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user