Add panic dialog

This dialog show a few hints  to users who encounter unexpected errors
This commit is contained in:
Stefano Brilli 2020-09-05 16:00:36 +02:00
parent 51812af06f
commit 9f9e043feb
4 changed files with 133 additions and 14 deletions

View File

@ -35,6 +35,7 @@ import { RenameDialog } from './rename-dialog';
import { UploadDialog } from './upload-dialog'; import { UploadDialog } from './upload-dialog';
import { RecordDialog } from './record-dialog'; import { RecordDialog } from './record-dialog';
import { ErrorDialog } from './error-dialog'; import { ErrorDialog } from './error-dialog';
import { PanicDialog } from './panic-dialog';
import { ConvertDialog } from './convert-dialog'; import { ConvertDialog } from './convert-dialog';
import { AboutDialog } from './about-dialog'; import { AboutDialog } from './about-dialog';
import { DumpDialog } from './dump-dialog'; import { DumpDialog } from './dump-dialog';
@ -379,6 +380,7 @@ export const Main = (props: {}) => {
<RecordDialog /> <RecordDialog />
<DumpDialog trackIndexes={selected} /> <DumpDialog trackIndexes={selected} />
<AboutDialog /> <AboutDialog />
<PanicDialog />
</React.Fragment> </React.Fragment>
); );
}; };

View File

@ -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>
);
};

View File

@ -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);

View File

@ -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 uploadDialog from './upload-dialog-feature';
import renameDialog from './rename-dialog-feature'; import renameDialog from './rename-dialog-feature';
import errorDialog from './error-dialog-feature'; import errorDialog from './error-dialog-feature';
import panicDialog, { actions as panicDialogActions } from './panic-dialog-feature';
import convertDialog from './convert-dialog-feature'; import convertDialog from './convert-dialog-feature';
import dumpDialog from './dump-dialog-feature'; import dumpDialog from './dump-dialog-feature';
import recordDialog from './record-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'; import main from './main-feature';
export const store = configureStore({ const errorCatcher: Middleware = store => next => async action => {
reducer: { try {
await next(action);
} catch (e) {
console.error(e);
next(panicDialogActions.setVisible(true));
}
};
let reducer = combineReducers({
renameDialog, renameDialog,
uploadDialog, uploadDialog,
errorDialog, errorDialog,
panicDialog,
convertDialog, convertDialog,
dumpDialog, dumpDialog,
recordDialog, recordDialog,
appState, appState,
main, main,
},
middleware: [...getDefaultMiddleware()],
}); });
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 RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch; export type AppDispatch = typeof store.dispatch;