mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Add error boundary for page crashes
This commit is contained in:
parent
9186c0af41
commit
0d9a8680fb
5 changed files with 85 additions and 3 deletions
|
@ -7,6 +7,7 @@ import { toAsyncPageRoute } from 'components/router/AsyncRoute';
|
||||||
import { toViewManagerPageRoute } from 'components/router/LegacyRoute';
|
import { toViewManagerPageRoute } from 'components/router/LegacyRoute';
|
||||||
import { LEGACY_ADMIN_ROUTES } from './_legacyRoutes';
|
import { LEGACY_ADMIN_ROUTES } from './_legacyRoutes';
|
||||||
import ServerContentPage from 'components/ServerContentPage';
|
import ServerContentPage from 'components/ServerContentPage';
|
||||||
|
import ErrorBoundary from '../../../components/router/ErrorBoundary';
|
||||||
|
|
||||||
export const DASHBOARD_APP_PATHS = {
|
export const DASHBOARD_APP_PATHS = {
|
||||||
Dashboard: 'dashboard',
|
Dashboard: 'dashboard',
|
||||||
|
@ -26,7 +27,8 @@ export const DASHBOARD_APP_ROUTES: RouteObject[] = [
|
||||||
children: [
|
children: [
|
||||||
...ASYNC_ADMIN_ROUTES.map(toAsyncPageRoute),
|
...ASYNC_ADMIN_ROUTES.map(toAsyncPageRoute),
|
||||||
...LEGACY_ADMIN_ROUTES.map(toViewManagerPageRoute)
|
...LEGACY_ADMIN_ROUTES.map(toViewManagerPageRoute)
|
||||||
]
|
],
|
||||||
|
errorElement: <ErrorBoundary pageClasses={[ 'type-interior' ]} />
|
||||||
},
|
},
|
||||||
|
|
||||||
/* NOTE: The metadata editor might deserve a dedicated app in the future */
|
/* NOTE: The metadata editor might deserve a dedicated app in the future */
|
||||||
|
|
|
@ -6,6 +6,8 @@ import ConnectionRequired from 'components/ConnectionRequired';
|
||||||
import { toAsyncPageRoute } from 'components/router/AsyncRoute';
|
import { toAsyncPageRoute } from 'components/router/AsyncRoute';
|
||||||
import { toViewManagerPageRoute } from 'components/router/LegacyRoute';
|
import { toViewManagerPageRoute } from 'components/router/LegacyRoute';
|
||||||
import { toRedirectRoute } from 'components/router/Redirect';
|
import { toRedirectRoute } from 'components/router/Redirect';
|
||||||
|
import ErrorBoundary from 'components/router/ErrorBoundary';
|
||||||
|
|
||||||
import AppLayout from '../AppLayout';
|
import AppLayout from '../AppLayout';
|
||||||
|
|
||||||
import { ASYNC_USER_ROUTES } from './asyncRoutes';
|
import { ASYNC_USER_ROUTES } from './asyncRoutes';
|
||||||
|
@ -29,7 +31,8 @@ export const EXPERIMENTAL_APP_ROUTES: RouteObject[] = [
|
||||||
path: 'video',
|
path: 'video',
|
||||||
element: <VideoPage />
|
element: <VideoPage />
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
ErrorBoundary
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Public routes */
|
/* Public routes */
|
||||||
|
|
|
@ -5,6 +5,7 @@ import ConnectionRequired from 'components/ConnectionRequired';
|
||||||
import { toAsyncPageRoute } from 'components/router/AsyncRoute';
|
import { toAsyncPageRoute } from 'components/router/AsyncRoute';
|
||||||
import { toViewManagerPageRoute } from 'components/router/LegacyRoute';
|
import { toViewManagerPageRoute } from 'components/router/LegacyRoute';
|
||||||
import { toRedirectRoute } from 'components/router/Redirect';
|
import { toRedirectRoute } from 'components/router/Redirect';
|
||||||
|
import ErrorBoundary from 'components/router/ErrorBoundary';
|
||||||
|
|
||||||
import AppLayout from '../AppLayout';
|
import AppLayout from '../AppLayout';
|
||||||
|
|
||||||
|
@ -23,7 +24,8 @@ export const STABLE_APP_ROUTES: RouteObject[] = [
|
||||||
children: [
|
children: [
|
||||||
...ASYNC_USER_ROUTES.map(toAsyncPageRoute),
|
...ASYNC_USER_ROUTES.map(toAsyncPageRoute),
|
||||||
...LEGACY_USER_ROUTES.map(toViewManagerPageRoute)
|
...LEGACY_USER_ROUTES.map(toViewManagerPageRoute)
|
||||||
]
|
],
|
||||||
|
ErrorBoundary
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Public routes */
|
/* Public routes */
|
||||||
|
|
67
src/components/router/ErrorBoundary.tsx
Normal file
67
src/components/router/ErrorBoundary.tsx
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
import Alert from '@mui/material/Alert/Alert';
|
||||||
|
import AlertTitle from '@mui/material/AlertTitle/AlertTitle';
|
||||||
|
import Box from '@mui/material/Box/Box';
|
||||||
|
import Paper from '@mui/material/Paper/Paper';
|
||||||
|
import Typography from '@mui/material/Typography/Typography';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import React, { type FC, useEffect } from 'react';
|
||||||
|
import { useRouteError } from 'react-router-dom';
|
||||||
|
|
||||||
|
import loading from 'components/loading/loading';
|
||||||
|
import Page from 'components/Page';
|
||||||
|
|
||||||
|
interface ErrorBoundaryParams {
|
||||||
|
pageClasses?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const ErrorBoundary: FC<ErrorBoundaryParams> = ({
|
||||||
|
pageClasses = [ 'libraryPage' ]
|
||||||
|
}) => {
|
||||||
|
const error = useRouteError() as Error;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loading.hide();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Page
|
||||||
|
id='errorBoundary'
|
||||||
|
className={classNames('mainAnimatedPage', pageClasses)}
|
||||||
|
>
|
||||||
|
<Box className='content-primary'>
|
||||||
|
<Alert severity='error'>
|
||||||
|
<AlertTitle>
|
||||||
|
{error.name}
|
||||||
|
</AlertTitle>
|
||||||
|
|
||||||
|
<Typography>
|
||||||
|
{error.message}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
{error.stack && (
|
||||||
|
<Paper
|
||||||
|
variant='outlined'
|
||||||
|
sx={{
|
||||||
|
marginTop: 1,
|
||||||
|
backgroundColor: 'transparent'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
component='pre'
|
||||||
|
sx={{
|
||||||
|
overflow: 'auto',
|
||||||
|
margin: 2,
|
||||||
|
maxHeight: '25rem' // 20 lines
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{error.stack}
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
|
)}
|
||||||
|
</Alert>
|
||||||
|
</Box>
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ErrorBoundary;
|
|
@ -41,6 +41,14 @@ export const DEFAULT_THEME_OPTIONS: ThemeOptions = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
MuiAlert: {
|
||||||
|
styleOverrides: {
|
||||||
|
message: {
|
||||||
|
// NOTE: This seems like a bug. Block content does not fill the container width.
|
||||||
|
flexGrow: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
MuiButton: {
|
MuiButton: {
|
||||||
defaultProps: {
|
defaultProps: {
|
||||||
variant: 'contained'
|
variant: 'contained'
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue