Add panic dialog
This dialog show a few hints to users who encounter unexpected errors
This commit is contained in:
parent
51812af06f
commit
9f9e043feb
|
@ -35,6 +35,7 @@ import { RenameDialog } from './rename-dialog';
|
|||
import { UploadDialog } from './upload-dialog';
|
||||
import { RecordDialog } from './record-dialog';
|
||||
import { ErrorDialog } from './error-dialog';
|
||||
import { PanicDialog } from './panic-dialog';
|
||||
import { ConvertDialog } from './convert-dialog';
|
||||
import { AboutDialog } from './about-dialog';
|
||||
import { DumpDialog } from './dump-dialog';
|
||||
|
@ -379,6 +380,7 @@ export const Main = (props: {}) => {
|
|||
<RecordDialog />
|
||||
<DumpDialog trackIndexes={selected} />
|
||||
<AboutDialog />
|
||||
<PanicDialog />
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useShallowEqualSelector } from '../utils';
|
||||
import { actions as panicDialogActions } from '../redux/panic-dialog-feature';
|
||||
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
import DialogActions from '@material-ui/core/DialogActions';
|
||||
import DialogContent from '@material-ui/core/DialogContent';
|
||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||
import Slide from '@material-ui/core/Slide';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import { TransitionProps } from '@material-ui/core/transitions';
|
||||
import { Typography } from '@material-ui/core';
|
||||
|
||||
const Transition = React.forwardRef(function Transition(
|
||||
props: TransitionProps & { children?: React.ReactElement<any, any> },
|
||||
ref: React.Ref<unknown>
|
||||
) {
|
||||
return <Slide direction="up" ref={ref} {...props} />;
|
||||
});
|
||||
|
||||
export const PanicDialog = (props: {}) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
let { visible, dismissed } = useShallowEqualSelector(state => state.panicDialog);
|
||||
|
||||
const handleReloadApp = useCallback(() => {
|
||||
window.location.reload();
|
||||
}, []);
|
||||
|
||||
const handleIgnore = useCallback(() => {
|
||||
dispatch(panicDialogActions.dismiss());
|
||||
}, [dispatch]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={visible && !dismissed}
|
||||
maxWidth={'sm'}
|
||||
fullWidth={true}
|
||||
scroll={'paper'}
|
||||
TransitionComponent={Transition as any}
|
||||
aria-labelledby="error-dialog-slide-title"
|
||||
aria-describedby="error-dialog-slide-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-slide-title">Oops… Something unexpected happened.</DialogTitle>
|
||||
<DialogContent>
|
||||
<Typography color="textSecondary" variant="body1" component="div">
|
||||
Try to restart the app. If the error persists, try the followings:
|
||||
<ol>
|
||||
<li>Use your browser in incognito mode.</li>
|
||||
<li>Use a blank MiniDisc.</li>
|
||||
<li>Try to use Web MiniDisc on another computer.</li>
|
||||
</ol>
|
||||
If this does not solve the error, your unit might not be supported yet or you have encountered a bug. The full error is
|
||||
reported in the JS console.
|
||||
</Typography>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleIgnore} size="small">
|
||||
Ignore and Continue
|
||||
</Button>
|
||||
<Button onClick={handleReloadApp} color="primary">
|
||||
Restart the App
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { enableBatching } from 'redux-batched-actions';
|
||||
|
||||
export const initialState = {
|
||||
visible: false,
|
||||
dismissed: false, // This will prevent showing the dialog during the same session
|
||||
};
|
||||
|
||||
const slice = createSlice({
|
||||
name: 'panicDialog',
|
||||
initialState,
|
||||
reducers: {
|
||||
setVisible: (state, action: PayloadAction<boolean>) => {
|
||||
state.visible = action.payload;
|
||||
},
|
||||
dismiss: (state, action: PayloadAction<void>) => {
|
||||
state.visible = false;
|
||||
state.dismissed = true;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { actions, reducer } = slice;
|
||||
export default enableBatching(reducer);
|
|
@ -1,26 +1,51 @@
|
|||
import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit';
|
||||
import { configureStore, getDefaultMiddleware, Middleware, combineReducers } from '@reduxjs/toolkit';
|
||||
import uploadDialog from './upload-dialog-feature';
|
||||
import renameDialog from './rename-dialog-feature';
|
||||
import errorDialog from './error-dialog-feature';
|
||||
import panicDialog, { actions as panicDialogActions } from './panic-dialog-feature';
|
||||
import convertDialog from './convert-dialog-feature';
|
||||
import dumpDialog from './dump-dialog-feature';
|
||||
import recordDialog from './record-dialog-feature';
|
||||
import appState from './app-feature';
|
||||
import appState, { actions as appActions } from './app-feature';
|
||||
import main from './main-feature';
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
renameDialog,
|
||||
uploadDialog,
|
||||
errorDialog,
|
||||
convertDialog,
|
||||
dumpDialog,
|
||||
recordDialog,
|
||||
appState,
|
||||
main,
|
||||
},
|
||||
middleware: [...getDefaultMiddleware()],
|
||||
const errorCatcher: Middleware = store => next => async action => {
|
||||
try {
|
||||
await next(action);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
next(panicDialogActions.setVisible(true));
|
||||
}
|
||||
};
|
||||
|
||||
let reducer = combineReducers({
|
||||
renameDialog,
|
||||
uploadDialog,
|
||||
errorDialog,
|
||||
panicDialog,
|
||||
convertDialog,
|
||||
dumpDialog,
|
||||
recordDialog,
|
||||
appState,
|
||||
main,
|
||||
});
|
||||
|
||||
const resetStateAction = appActions.setState.toString();
|
||||
const resetStatePayoload = 'WELCOME';
|
||||
const resetStateReducer: typeof reducer = function(...args) {
|
||||
const [state, action] = args;
|
||||
if (action.type === resetStateAction && action.payload === resetStatePayoload) {
|
||||
return initialState;
|
||||
}
|
||||
return reducer(...args);
|
||||
};
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: resetStateReducer,
|
||||
middleware: [errorCatcher, ...getDefaultMiddleware()],
|
||||
});
|
||||
|
||||
const initialState = Object.freeze(store.getState());
|
||||
|
||||
export type RootState = ReturnType<typeof store.getState>;
|
||||
export type AppDispatch = typeof store.dispatch;
|
||||
|
|
Loading…
Reference in New Issue