Moving reset into dedicated Reset modal
This commit is contained in:
@ -50,6 +50,7 @@ import ShareConfiguration from './views/modals/ShareConfiguration';
|
||||
import AddToPlaylist from './views/modals/AddToPlaylist';
|
||||
import ImageZoom from './views/modals/ImageZoom';
|
||||
import EditCommand from './views/modals/EditCommand';
|
||||
import Reset from './views/modals/Reset';
|
||||
|
||||
import { scrollTo, isTouchDevice } from './util/helpers';
|
||||
import storage from './util/storage';
|
||||
@ -289,6 +290,7 @@ export class App extends React.Component {
|
||||
<Route path="/add-to-playlist/:uris" component={AddToPlaylist} />
|
||||
<Route path="/image-zoom" component={ImageZoom} />
|
||||
<Route path="/share-configuration" component={ShareConfiguration} />
|
||||
<Route path="/reset" component={Reset} />
|
||||
<Route path="/edit-command/:id?" component={EditCommand} />
|
||||
|
||||
<Route path="/queue/radio" component={EditRadio} />
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import localForage from 'localforage';
|
||||
import { get as getStorage } from '../util/storage';
|
||||
import { isTouchDevice } from '../util/helpers';
|
||||
import * as uiActions from '../services/ui/actions';
|
||||
import { indexToArray } from '../util/arrays';
|
||||
import localForage from 'localforage';
|
||||
|
||||
class DebugInfo extends React.Component {
|
||||
constructor(props) {
|
||||
|
||||
@ -399,9 +399,7 @@ settings:
|
||||
up_to_date: Up to date
|
||||
share_configuration: Share configuration
|
||||
restart: Restart server
|
||||
reset_cache: Reset cache
|
||||
reset_storage: Reset storage
|
||||
reset: Reset all settings
|
||||
reset: Reset settings
|
||||
about:
|
||||
title: About
|
||||
blurb_1: ' is an open-source project by '
|
||||
@ -561,3 +559,22 @@ modal:
|
||||
do_you_want_to_import: Do you want to import this?
|
||||
import_now: Import now
|
||||
successful: Import successful
|
||||
reset:
|
||||
title: Reset Iris
|
||||
subtitle: Clear your local configuration, storage information or cache
|
||||
items: Reset to defaults
|
||||
preferences:
|
||||
label: Preferences
|
||||
description: Interface customisation
|
||||
database:
|
||||
label: Database
|
||||
description: Local browser-based database of libraries and assets
|
||||
service_worker:
|
||||
label: Service worker
|
||||
description: Re-install service worker
|
||||
cache:
|
||||
label: Fetch cache
|
||||
description: External API requests cache (requires Service Worker)
|
||||
test_mode:
|
||||
label: Test mode
|
||||
description: Verbose logging for diagnosing issues
|
||||
|
||||
@ -50,6 +50,12 @@ export function set(data) {
|
||||
};
|
||||
}
|
||||
|
||||
export function resetState() {
|
||||
return {
|
||||
type: 'RESET_STATE',
|
||||
};
|
||||
}
|
||||
|
||||
export function clearCurrentTrack() {
|
||||
return {
|
||||
type: 'CLEAR_CURRENT_TRACK',
|
||||
|
||||
@ -27,9 +27,8 @@ import geniusMiddleware from '../services/genius/middleware';
|
||||
import spotifyMiddleware from '../services/spotify/middleware';
|
||||
import googleMiddleware from '../services/google/middleware';
|
||||
import snapcastMiddleware from '../services/snapcast/middleware';
|
||||
import localstorageMiddleware from '../services/localstorage/middleware';
|
||||
|
||||
let state = {
|
||||
let initialState = {
|
||||
core: {
|
||||
outputs: [],
|
||||
queue: [],
|
||||
@ -135,7 +134,7 @@ state.snapcast = { ...state.snapcast, ...storage.get('snapcast') };
|
||||
*/
|
||||
|
||||
// Run any migrations
|
||||
state = migration(state);
|
||||
initialState = migration(initialState);
|
||||
|
||||
const rootPersistConfig = {
|
||||
key: 'root',
|
||||
@ -230,7 +229,7 @@ const uiPersistConfig = {
|
||||
debug: window.test_mode,
|
||||
};
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
const appReducer = combineReducers({
|
||||
core: persistReducer(corePersistConfig, core),
|
||||
ui: persistReducer(uiPersistConfig, ui),
|
||||
mopidy: persistReducer(mopidyPersistConfig, mopidy),
|
||||
@ -241,12 +240,18 @@ const rootReducer = combineReducers({
|
||||
google,
|
||||
snapcast,
|
||||
});
|
||||
const rootReducer = (state, action) => {
|
||||
if (action.type === 'RESET_STATE') {
|
||||
state = initialState;
|
||||
}
|
||||
return appReducer(state, action);
|
||||
};
|
||||
|
||||
const persistedReducer = persistReducer(rootPersistConfig, rootReducer);
|
||||
|
||||
const store = createStore(
|
||||
persistedReducer,
|
||||
state,
|
||||
initialState,
|
||||
applyMiddleware(
|
||||
thunk,
|
||||
coreMiddleware,
|
||||
|
||||
@ -71,62 +71,6 @@ class Settings extends React.Component {
|
||||
this.setState({ [name]: value });
|
||||
}
|
||||
|
||||
resetAllSettings = () => {
|
||||
localForage.clear().then(() => {
|
||||
console.debug('Cleared settings, reloading...');
|
||||
window.location = '#';
|
||||
window.location.reload(true);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
resetStorage = () => {
|
||||
localForage.keys().then((keys) => {
|
||||
const keysToKeep = ['persist:root', 'persist:ui', 'persist:spotify'];
|
||||
const keysToRemove = keys.filter((key) => keysToKeep.indexOf(key) < 0);
|
||||
|
||||
keysToRemove.forEach((key, index) => {
|
||||
localForage.removeItem(key).then(() => {
|
||||
console.debug(`Removed ${key}`);
|
||||
if (index === keysToRemove.length) {
|
||||
console.debug('Reloading...');
|
||||
window.location = '#';
|
||||
window.location.reload(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
resetServiceWorkerAndCache = () => {
|
||||
const { coreActions: { handleException } } = this.props;
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
|
||||
// Hose out all our caches
|
||||
caches.keys().then(function (cacheNames) {
|
||||
cacheNames.forEach(function (cacheName) {
|
||||
caches.delete(cacheName);
|
||||
});
|
||||
});
|
||||
|
||||
// Unregister all service workers
|
||||
// This forces our SW to bugger off and a new one is registered on refresh
|
||||
navigator.serviceWorker.getRegistrations().then(
|
||||
(registrations) => {
|
||||
for (let registration of registrations) {
|
||||
registration.unregister();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
window.location = '#';
|
||||
window.location.reload(true);
|
||||
} else {
|
||||
handleException(i18n('errors.no_service_worker'));
|
||||
}
|
||||
}
|
||||
|
||||
doRestart = () => {
|
||||
const { pusherActions: { restart } } = this.props;
|
||||
restart();
|
||||
@ -584,23 +528,11 @@ class Settings extends React.Component {
|
||||
<I18n path="settings.advanced.restart" />
|
||||
</Button>
|
||||
<Button
|
||||
to="/reset"
|
||||
type="destructive"
|
||||
onClick={this.resetServiceWorkerAndCache}
|
||||
tracking={{ category: 'System', action: 'ResetCache' }}
|
||||
>
|
||||
<I18n path="settings.advanced.reset_cache" />
|
||||
<I18n path="settings.advanced.reset" />
|
||||
</Button>
|
||||
<Button
|
||||
type="destructive"
|
||||
onClick={this.resetStorage}
|
||||
tracking={{ category: 'System', action: 'ResetStorage' }}
|
||||
>
|
||||
<I18n path="settings.advanced.reset_storage" />
|
||||
</Button>
|
||||
<ConfirmationButton
|
||||
content={i18n('settings.advanced.reset')}
|
||||
onConfirm={this.resetAllSettings}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h4 className="underline">
|
||||
|
||||
213
src/js/views/modals/Reset.js
Normal file
213
src/js/views/modals/Reset.js
Normal file
@ -0,0 +1,213 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import localForage from 'localforage';
|
||||
import Modal from './Modal';
|
||||
import * as uiActions from '../../services/ui/actions';
|
||||
import * as coreActions from '../../services/core/actions';
|
||||
import { i18n, I18n } from '../../locale';
|
||||
import Button from '../../components/Button';
|
||||
|
||||
class Reset extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
test_mode: false,
|
||||
preferences: false,
|
||||
database: true,
|
||||
cache: ('serviceWorker' in navigator),
|
||||
service_worker: ('serviceWorker' in navigator),
|
||||
working: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.uiActions.setWindowTitle(i18n('modal.reset.title'));
|
||||
}
|
||||
|
||||
handleSubmit = (e) => {
|
||||
const {
|
||||
coreActions: {
|
||||
resetState,
|
||||
},
|
||||
} = this.props;
|
||||
const {
|
||||
preferences,
|
||||
database,
|
||||
cache,
|
||||
service_worker,
|
||||
test_mode,
|
||||
working,
|
||||
} = this.state;
|
||||
|
||||
if (working) return null;
|
||||
|
||||
this.setState({ working: true });
|
||||
|
||||
e.preventDefault();
|
||||
const tasks = [];
|
||||
|
||||
if (preferences) {
|
||||
tasks.push(
|
||||
new Promise((resolve) => {
|
||||
const keysToRemove = ['persist:root', 'persist:ui', 'persist:spotify', 'persist:pusher'];
|
||||
keysToRemove.forEach((key, index) => {
|
||||
localForage.removeItem(key).then(() => {
|
||||
console.debug(`Removed ${key}`);
|
||||
if (index === keysToRemove.length - 1) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
if (database) {
|
||||
tasks.push(
|
||||
new Promise((resolve) => {
|
||||
localForage.keys().then((keys) => {
|
||||
const keysToKeep = ['persist:root', 'persist:ui', 'persist:spotify', 'persist:pusher'];
|
||||
const keysToRemove = keys.filter((key) => keysToKeep.indexOf(key) < 0);
|
||||
keysToRemove.forEach((key, index) => {
|
||||
localForage.removeItem(key).then(() => {
|
||||
console.debug(`Removed ${key}`);
|
||||
if (index === keysToRemove.length - 1) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
if (cache) {
|
||||
tasks.push(
|
||||
new Promise((resolve, reject) => {
|
||||
if ('serviceWorker' in navigator) {
|
||||
caches.keys().then((cacheNames) => {
|
||||
cacheNames.forEach((cacheName) => {
|
||||
caches.delete(cacheName);
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
if (service_worker) {
|
||||
tasks.push(
|
||||
new Promise((resolve, reject) => {
|
||||
if ('serviceWorker' in navigator) {
|
||||
// Unregister all service workers
|
||||
// This forces our SW to bugger off and a new one is registered on refresh
|
||||
navigator.serviceWorker.getRegistrations().then(
|
||||
(registrations) => {
|
||||
for (const registration of registrations) {
|
||||
registration.unregister();
|
||||
}
|
||||
resolve();
|
||||
},
|
||||
);
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Promise.all(tasks).then(() => {
|
||||
console.log('Reset complete, refreshing...');
|
||||
resetState();
|
||||
setTimeout(
|
||||
() => window.location = `/iris${test_mode ? '?test_mode=0' : ''}`,
|
||||
1000,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
render = () => {
|
||||
const {
|
||||
preferences,
|
||||
database,
|
||||
service_worker,
|
||||
cache,
|
||||
test_mode,
|
||||
working,
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<Modal className="modal--reset">
|
||||
|
||||
<h1>
|
||||
<I18n path="modal.reset.title" />
|
||||
</h1>
|
||||
<h2>
|
||||
<I18n path="modal.reset.subtitle" />
|
||||
</h2>
|
||||
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<div className="field checkbox checkbox--block">
|
||||
<div className="name">
|
||||
<I18n path="modal.reset.items" />
|
||||
</div>
|
||||
<div className="input">
|
||||
{['preferences', 'database', 'cache', 'service_worker', 'test_mode'].map((name) => {
|
||||
const { [name]: value } = this.state;
|
||||
const disabled = (name === 'cache' || name === 'service_worker')
|
||||
&& !('serviceWorker' in navigator);
|
||||
|
||||
return (
|
||||
<div className="checkbox-group__item" key={name}>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="spotify"
|
||||
checked={value}
|
||||
disabled={disabled}
|
||||
onChange={() => this.setState({ [name]: !value })}
|
||||
/>
|
||||
<div className="label">
|
||||
<div>
|
||||
<div className="title">
|
||||
<I18n path={`modal.reset.${name}.label`} />
|
||||
</div>
|
||||
<div className="description mid_grey-text">
|
||||
<I18n path={`modal.reset.${name}.description`} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="actions centered-text">
|
||||
<Button
|
||||
type="primary"
|
||||
size="large"
|
||||
onClick={this.handleSubmit}
|
||||
working={working}
|
||||
disabled={!database && !cache && !service_worker && !preferences && !test_mode}
|
||||
tracking={{ category: 'Reset', action: 'Submit' }}
|
||||
>
|
||||
<I18n path="actions.reset" />
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = () => ({});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
coreActions: bindActionCreators(coreActions, dispatch),
|
||||
uiActions: bindActionCreators(uiActions, dispatch),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Reset);
|
||||
@ -239,7 +239,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
&--share-configuration {
|
||||
&--share-configuration,
|
||||
&--reset {
|
||||
.checkbox-group {
|
||||
&__item {
|
||||
padding-bottom: 0.75rem;
|
||||
@ -255,7 +256,7 @@
|
||||
}
|
||||
|
||||
.description {
|
||||
padding-top: 4px;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user