fix(core): add error boundary for workspace layout (#5014)

https://github.com/toeverything/AFFiNE/assets/3468483/d478bf4f-2be3-4d7d-8d94-aa95c1f74c8e
This commit is contained in:
LongYinan
2023-11-22 01:52:45 +00:00
committed by LongYinan
parent 3839a9bd15
commit 5f1a124b53
4 changed files with 31 additions and 13 deletions

View File

@@ -15,7 +15,7 @@ export const errorDetailStyle = style({
});
export const errorTitle = style({
fontSize: '36px',
fontSize: '32px',
lineHeight: '44px',
fontWeight: 700,
});

View File

@@ -20,7 +20,7 @@ import { useLocation, useParams } from 'react-router-dom';
import {
RecoverableError,
SessionFetchErrorRightAfterLoginOrSignUp,
type SessionFetchErrorRightAfterLoginOrSignUp,
} from '../../unexpected-application-state/errors';
import {
errorDescription,
@@ -33,7 +33,9 @@ import {
} from './affine-error-boundary.css';
import errorBackground from './error-status.assets.svg';
export type AffineErrorBoundaryProps = React.PropsWithChildren;
export type AffineErrorBoundaryProps = React.PropsWithChildren & {
height?: number | string;
};
type AffineError =
| QueryParamError
@@ -81,7 +83,7 @@ export class AffineErrorBoundary extends Component<
if (this.state.error.canRetry()) {
this.state.error.retry();
this.setState({
error: this.state.error,
error: null,
canRetryRecoveredError: this.state.error.canRetry(),
});
} else {
@@ -90,6 +92,10 @@ export class AffineErrorBoundary extends Component<
}
};
private readonly handleRefresh = () => {
this.setState({ error: null });
};
static getDerivedStateFromError(
error: AffineError
): AffineErrorBoundaryState {
@@ -121,14 +127,14 @@ export class AffineErrorBoundary extends Component<
</>
</>
);
} else if (error instanceof SessionFetchErrorRightAfterLoginOrSignUp) {
} else if (error instanceof RecoverableError) {
const retryButtonDesc = this.state.canRetryRecoveredError
? 'Refetch'
: 'Reload';
errorDetail = (
<>
<h1 className={errorTitle}>Sorry.. there was an error</h1>
<span className={errorDescription}> Fetching session failed </span>
<span className={errorDescription}> {error.message} </span>
<span className={errorDescription}>
If you are still experiencing this issue, please{' '}
<a
@@ -151,13 +157,22 @@ export class AffineErrorBoundary extends Component<
} else {
errorDetail = (
<>
<h1>Sorry.. there was an error</h1>
{error.message ?? error.toString()}
<h1 className={errorTitle}>Sorry.. there was an error</h1>
<code className={errorDescription}>
{error.message ?? error.toString()}
</code>
<Button
onClick={this.handleRefresh}
className={errorRetryButton}
type="primary"
>
Refresh
</Button>
</>
);
}
return (
<div className={errorLayout}>
<div className={errorLayout} style={{ height: this.props.height }}>
<div className={errorDetailStyle}>{errorDetail}</div>
<span className={errorDivider} />
<div

View File

@@ -60,7 +60,7 @@ export function useCurrentUser(): CheckedUser {
// login succeed but the session request failed then.
// also need a error boundary to handle this error.
throw new SessionFetchErrorRightAfterLoginOrSignUp(
'First session should not be null',
'Fetching session failed',
() => {
getSession()
.then(session => {

View File

@@ -17,6 +17,7 @@ import {
useParams,
} from 'react-router-dom';
import { AffineErrorBoundary } from '../../components/affine/affine-error-boundary';
import { WorkspaceLayout } from '../../layouts/workspace-layout';
import { performanceLogger, performanceRenderLogger } from '../../shared';
@@ -82,8 +83,10 @@ export const Component = (): ReactElement => {
const incompatible = useLoaderData();
return (
<WorkspaceLayout incompatible={!!incompatible}>
<Outlet />
</WorkspaceLayout>
<AffineErrorBoundary height="100vh">
<WorkspaceLayout incompatible={!!incompatible}>
<Outlet />
</WorkspaceLayout>
</AffineErrorBoundary>
);
};