Skip to content

Commit eab880b

Browse files
committed
fix: introduce proper error boundary
- add methods to quit or relaunch the app - create error boundary that displays error and offers options to relaunch or quit closes #2564 Signed-off-by: Maxim Stykow <maxim.stykow@tngtech.com>
1 parent 7c556e9 commit eab880b

File tree

14 files changed

+119
-183
lines changed

14 files changed

+119
-183
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"re-resizable": "^6.9.11",
3030
"react": "^18.2.0",
3131
"react-dom": "^18.2.0",
32+
"react-error-boundary": "^4.0.12",
3233
"react-hot-toast": "^2.4.1",
3334
"react-hotkeys-hook": "^4.5.0",
3435
"react-redux": "^9.1.0",

renovate.json5

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
'prettier',
4949
'prop-types',
5050
'proxy-memoize',
51+
'react-error-boundary',
5152
'react-hot-toast',
5253
'react-hotkeys-hook',
5354
'react-virtuoso',

src/ElectronBackend/main/main.ts

+6
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ export async function main(): Promise<void> {
5858
},
5959
);
6060

61+
ipcMain.handle(IpcChannel.Quit, () => {
62+
mainWindow.close();
63+
});
64+
ipcMain.handle(IpcChannel.Relaunch, () => {
65+
mainWindow.reload();
66+
});
6167
ipcMain.handle(
6268
IpcChannel.ConvertInputFile,
6369
getConvertInputFileToDotOpossumAndOpenListener(mainWindow),

src/ElectronBackend/preload.ts

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { IpcChannel } from '../shared/ipc-channels';
99
import { ElectronAPI } from '../shared/shared-types';
1010

1111
const electronAPI: ElectronAPI = {
12+
quit: () => ipcRenderer.invoke(IpcChannel.Quit),
13+
relaunch: () => ipcRenderer.invoke(IpcChannel.Relaunch),
1214
openLink: (link) => ipcRenderer.invoke(IpcChannel.OpenLink, { link }),
1315
openFile: () => ipcRenderer.invoke(IpcChannel.OpenFile),
1416
deleteFile: () => ipcRenderer.invoke(IpcChannel.DeleteFile),

src/Frontend/Components/App/App.tsx

+12-11
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// SPDX-License-Identifier: Apache-2.0
55
import '@fontsource-variable/karla';
66
import { StyledEngineProvider, ThemeProvider } from '@mui/material/styles';
7+
import { ErrorBoundary } from 'react-error-boundary';
78

89
import { View } from '../../enums/enums';
910
import { useAppSelector } from '../../state/hooks';
@@ -12,7 +13,7 @@ import { getSelectedView } from '../../state/selectors/view-selector';
1213
import { usePanelSizes } from '../../state/variables/use-panel-sizes';
1314
import { useSignalsWorker } from '../../web-workers/use-signals-worker';
1415
import { AuditView } from '../AuditView/AuditView';
15-
import { ErrorBoundary } from '../ErrorBoundary/ErrorBoundary';
16+
import { ErrorFallback } from '../ErrorFallback/ErrorFallback';
1617
import { GlobalPopup } from '../GlobalPopup/GlobalPopup';
1718
import { ProcessPopup } from '../ProcessPopup/ProcessPopup';
1819
import { ReportView } from '../ReportView/ReportView';
@@ -32,18 +33,18 @@ export function App() {
3233
usePanelSizes(); // pre-hydrate size of panels
3334

3435
return (
35-
<ErrorBoundary>
36-
<StyledEngineProvider injectFirst>
37-
<ThemeProvider theme={theme}>
38-
<GlobalPopup />
39-
<ProcessPopup />
40-
<ViewContainer>
36+
<StyledEngineProvider injectFirst>
37+
<ThemeProvider theme={theme}>
38+
<ViewContainer>
39+
<ErrorBoundary FallbackComponent={ErrorFallback}>
40+
<GlobalPopup />
41+
<ProcessPopup />
4142
<TopBar />
4243
{renderView()}
43-
</ViewContainer>
44-
</ThemeProvider>
45-
</StyledEngineProvider>
46-
</ErrorBoundary>
44+
</ErrorBoundary>
45+
</ViewContainer>
46+
</ThemeProvider>
47+
</StyledEngineProvider>
4748
);
4849

4950
function renderView() {

src/Frontend/Components/ErrorBoundary/ErrorBoundary.tsx

-110
This file was deleted.

src/Frontend/Components/ErrorBoundary/__tests__/ErrorBoundary.test.tsx

-62
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// SPDX-FileCopyrightText: Meta Platforms, Inc. and its affiliates
2+
// SPDX-FileCopyrightText: TNG Technology Consulting GmbH <https://www.tngtech.com>
3+
//
4+
// SPDX-License-Identifier: Apache-2.0
5+
import { styled } from '@mui/material';
6+
7+
export const Container = styled('div')({
8+
display: 'flex',
9+
flexDirection: 'row',
10+
height: '100%',
11+
width: '100%',
12+
alignItems: 'center',
13+
justifyContent: 'center',
14+
});
15+
16+
export const TextContainer = styled('div')({
17+
display: 'flex',
18+
flexDirection: 'column',
19+
gap: '20px',
20+
width: 'fit-content',
21+
maxWidth: '600px',
22+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// SPDX-FileCopyrightText: Meta Platforms, Inc. and its affiliates
2+
// SPDX-FileCopyrightText: TNG Technology Consulting GmbH <https://www.tngtech.com>
3+
//
4+
// SPDX-License-Identifier: Apache-2.0
5+
import MuiButton from '@mui/material/Button';
6+
import MuiButtonGroup from '@mui/material/ButtonGroup';
7+
import MuiTypography from '@mui/material/Typography';
8+
import { FallbackProps } from 'react-error-boundary';
9+
10+
import { text } from '../../../shared/text';
11+
import { OpossumColors } from '../../shared-styles';
12+
import { Container, TextContainer } from './ErrorFallback.style';
13+
14+
export const ErrorFallback: React.FC<FallbackProps> = ({
15+
error,
16+
resetErrorBoundary,
17+
}) => {
18+
return (
19+
<Container>
20+
<TextContainer role="alert">
21+
<MuiTypography textAlign={'center'} variant={'h6'}>
22+
{text.errorBoundary.unexpectedError}
23+
</MuiTypography>
24+
<MuiTypography
25+
sx={{ fontFamily: 'monospace' }}
26+
color={OpossumColors.red}
27+
>
28+
{error.message}
29+
</MuiTypography>
30+
<MuiButtonGroup fullWidth variant={'contained'}>
31+
<MuiButton
32+
color={'secondary'}
33+
onClick={() => {
34+
resetErrorBoundary();
35+
window.electronAPI.relaunch();
36+
}}
37+
>
38+
{text.errorBoundary.relaunch}
39+
</MuiButton>
40+
<MuiButton
41+
color={'secondary'}
42+
onClick={() => {
43+
window.electronAPI.quit();
44+
}}
45+
>
46+
{text.errorBoundary.quit}
47+
</MuiButton>
48+
</MuiButtonGroup>
49+
</TextContainer>
50+
</Container>
51+
);
52+
};

src/shared/ipc-channels.ts

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export enum IpcChannel {
1717
OpenDotOpossumFile = 'open-dot-opossum-file',
1818
GetUserSettings = 'get-user-settings',
1919
SetUserSettings = 'set-user-settings',
20+
Quit = 'quit',
21+
Relaunch = 'relaunch',
2022
}
2123

2224
export enum AllowedFrontendChannels {

src/shared/shared-types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,8 @@ export interface FileSupportPopupArgs {
235235
export type Listener = (event: IpcRendererEvent, ...args: Array<any>) => void;
236236

237237
export interface ElectronAPI {
238+
quit: () => void;
239+
relaunch: () => void;
238240
openLink: (link: string) => Promise<unknown>;
239241
openFile: () => Promise<unknown>;
240242
deleteFile: () => Promise<unknown>;

src/shared/text.ts

+5
Original file line numberDiff line numberDiff line change
@@ -229,4 +229,9 @@ export const text = {
229229
applyChanges: 'Apply changes',
230230
revertAll: 'Revert all',
231231
},
232+
errorBoundary: {
233+
unexpectedError: "We're sorry, an unexpected error occurred!",
234+
relaunch: 'Relaunch App',
235+
quit: 'Quit App',
236+
},
232237
} as const;

src/testing/setup-tests.ts

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ class ResizeObserver {
3232
}
3333

3434
global.window.electronAPI = {
35+
quit: jest.fn(),
36+
relaunch: jest.fn(),
3537
openLink: jest.fn().mockReturnValue(Promise.resolve()),
3638
openFile: jest.fn(),
3739
deleteFile: jest.fn(),

yarn.lock

+12
Original file line numberDiff line numberDiff line change
@@ -9858,6 +9858,7 @@ __metadata:
98589858
re-resizable: "npm:^6.9.11"
98599859
react: "npm:^18.2.0"
98609860
react-dom: "npm:^18.2.0"
9861+
react-error-boundary: "npm:^4.0.12"
98619862
react-hot-toast: "npm:^2.4.1"
98629863
react-hotkeys-hook: "npm:^4.5.0"
98639864
react-redux: "npm:^9.1.0"
@@ -10397,6 +10398,17 @@ __metadata:
1039710398
languageName: node
1039810399
linkType: hard
1039910400

10401+
"react-error-boundary@npm:^4.0.12":
10402+
version: 4.0.12
10403+
resolution: "react-error-boundary@npm:4.0.12"
10404+
dependencies:
10405+
"@babel/runtime": "npm:^7.12.5"
10406+
peerDependencies:
10407+
react: ">=16.13.1"
10408+
checksum: 10/ba59f885eae3c3786548086c6c2088a9f511080c4052e778017959e9e0b6461892efdcf58fcfc2b3a6bc3e79e17cf842fc8ccdc6d82698c51c2ccab12c8c0b85
10409+
languageName: node
10410+
linkType: hard
10411+
1040010412
"react-hot-toast@npm:^2.4.1":
1040110413
version: 2.4.1
1040210414
resolution: "react-hot-toast@npm:2.4.1"

0 commit comments

Comments
 (0)