Files
rowy/src/components/ErrorFallback.tsx
2022-11-14 16:09:01 +11:00

142 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState, useEffect } from "react";
import { FallbackProps } from "react-error-boundary";
import { useLocation } from "react-router-dom";
import useOffline from "@src/hooks/useOffline";
import { Typography, Button } from "@mui/material";
import ReloadIcon from "@mui/icons-material/Refresh";
import InlineOpenInNewIcon from "@src/components/InlineOpenInNewIcon";
import OfflineIcon from "@mui/icons-material/CloudOff";
import EmptyState, { IEmptyStateProps } from "@src/components/EmptyState";
import AccessDenied from "@src/components/AccessDenied";
import { EXTERNAL_LINKS } from "@src/constants/externalLinks";
export interface IErrorFallbackProps extends FallbackProps, IEmptyStateProps {}
export function ErrorFallbackContents({
error,
resetErrorBoundary,
...props
}: IErrorFallbackProps) {
const isOffline = useOffline();
if ((error as any).code === "permission-denied")
return (
<AccessDenied error={error} resetErrorBoundary={resetErrorBoundary} />
);
let renderProps: Partial<IEmptyStateProps> = {
message: "Something went wrong",
description: (
<>
<Typography variant="inherit" style={{ whiteSpace: "pre-line" }}>
{(error as any).code && <b>{(error as any).code}: </b>}
{(error as any).status && <b>{(error as any).status}: </b>}
{error.message}
</Typography>
<Button
size={props.basic ? "small" : "medium"}
href={
EXTERNAL_LINKS.gitHub +
"/discussions/new?" +
new URLSearchParams({
labels: "bug",
category: "support-q-a",
title: [
"Error",
(error as any).code,
(error as any).status,
error.message,
]
.filter(Boolean)
.join(": ")
.replace(/\n/g, " "),
body: "👉 **Please describe the steps that you took that led to this bug.**",
}).toString()
}
target="_blank"
rel="noopener noreferrer"
>
Report issue
<InlineOpenInNewIcon />
</Button>
</>
),
};
if (error.message.startsWith("Loading chunk")) {
if (isOffline) {
renderProps = { Icon: OfflineIcon, message: "Youre offline" };
} else {
renderProps = {
Icon: ReloadIcon,
message: "Update available",
description: (
<Button
size={props.basic ? "small" : "medium"}
variant="outlined"
color="secondary"
startIcon={<ReloadIcon />}
onClick={() => window.location.reload()}
sx={{ mt: 1 }}
>
Reload
</Button>
),
};
}
}
if (error.message.includes("Failed to fetch")) {
renderProps = {
Icon: OfflineIcon,
message: "Youre offline",
description: isOffline ? null : (
<Button
size={props.basic ? "small" : "medium"}
variant="outlined"
color="secondary"
startIcon={<ReloadIcon />}
onClick={() => window.location.reload()}
sx={{ mt: 1 }}
>
Reload
</Button>
),
};
}
return <EmptyState role="alert" fullScreen {...renderProps} {...props} />;
}
export default function ErrorFallback(props: IErrorFallbackProps) {
const { resetErrorBoundary } = props;
// Reset error boundary when navigating away from the page
const location = useLocation();
const [errorPathname] = useState(location.pathname);
useEffect(() => {
if (errorPathname !== location.pathname) resetErrorBoundary();
}, [errorPathname, location.pathname, resetErrorBoundary]);
return <ErrorFallbackContents {...props} />;
}
export function InlineErrorFallback(props: IErrorFallbackProps) {
return (
<ErrorFallbackContents
{...props}
fullScreen={false}
basic
wrap="nowrap"
sx={{ typography: "body2" }}
/>
);
}
export function NonFullScreenErrorFallback(props: IErrorFallbackProps) {
return <ErrorFallbackContents {...props} fullScreen={false} />;
}