shellphone.app/app/core/layouts/layout/index.tsx

113 lines
2.7 KiB
TypeScript
Raw Normal View History

import type { ErrorInfo, FunctionComponent } from "react";
import { Component } from "react";
2021-08-01 10:36:32 +00:00
import {
Head,
withRouter,
AuthenticationError,
AuthorizationError,
CSRFTokenMismatchError,
NotFoundError,
RedirectError,
} from "blitz";
import type { WithRouterProps } from "next/dist/client/with-router";
2021-07-18 15:32:45 +00:00
import appLogger from "../../../../integrations/logger";
2021-07-18 15:32:45 +00:00
import Footer from "./footer";
2021-07-18 15:32:45 +00:00
type Props = {
title: string;
pageTitle?: string;
hideFooter?: true;
};
2021-07-18 15:32:45 +00:00
const logger = appLogger.child({ module: "Layout" });
2021-07-18 15:32:45 +00:00
2021-08-01 14:03:49 +00:00
const Layout: FunctionComponent<Props> = ({ children, title, pageTitle = title, hideFooter = false }) => {
2021-07-18 15:32:45 +00:00
return (
<>
{pageTitle ? (
<Head>
2021-09-29 22:10:42 +00:00
<title>{pageTitle} | Shellphone</title>
2021-07-18 15:32:45 +00:00
</Head>
) : null}
2021-09-29 22:10:42 +00:00
<div className="h-full w-full overflow-hidden fixed bg-gray-100">
2021-07-18 15:32:45 +00:00
<div className="flex flex-col w-full h-full">
<div className="flex flex-col flex-1 w-full overflow-y-auto">
2021-09-24 23:07:40 +00:00
<main className="flex flex-col flex-1 my-0 h-full">
2021-07-18 15:32:45 +00:00
<ErrorBoundary>{children}</ErrorBoundary>
</main>
</div>
2021-07-31 14:33:18 +00:00
{!hideFooter ? <Footer /> : null}
2021-07-18 15:32:45 +00:00
</div>
</div>
</>
);
};
2021-07-18 15:32:45 +00:00
type ErrorBoundaryState =
| {
isError: false;
2021-07-18 15:32:45 +00:00
}
| {
isError: true;
errorMessage: string;
};
2021-07-18 15:32:45 +00:00
2021-08-01 14:03:49 +00:00
const blitzErrors = [RedirectError, AuthenticationError, AuthorizationError, CSRFTokenMismatchError, NotFoundError];
2021-08-01 10:36:32 +00:00
2021-07-18 15:32:45 +00:00
const ErrorBoundary = withRouter(
class ErrorBoundary extends Component<WithRouterProps, ErrorBoundaryState> {
public readonly state = {
isError: false,
} as const;
2021-07-18 15:32:45 +00:00
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
return {
isError: true,
errorMessage: error.message,
};
2021-07-18 15:32:45 +00:00
}
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
2021-08-04 05:49:45 +00:00
console.trace("ddd");
logger.error(error, errorInfo.componentStack);
2021-08-01 11:21:49 +00:00
if (blitzErrors.some((blitzError) => error instanceof blitzError)) {
2021-08-01 10:36:32 +00:00
// let Blitz ErrorBoundary handle this one
throw error;
}
2021-08-05 17:07:15 +00:00
// if network error and connection lost, display the auto-reload page with countdown
2021-07-18 15:32:45 +00:00
}
public render() {
if (this.state.isError) {
return (
<>
<h2 className="mt-6 text-center text-3xl leading-9 font-extrabold text-gray-900">
Oops, something went wrong.
</h2>
<p className="mt-2 text-center text-lg leading-5 text-gray-600">
Would you like to{" "}
<button
className="inline-flex space-x-2 items-center text-left"
onClick={this.props.router.reload}
>
<span className="transition-colors duration-150 border-b border-primary-200 hover:border-primary-500">
reload the page
</span>
</button>{" "}
?
</p>
</>
);
2021-07-18 15:32:45 +00:00
}
return this.props.children;
2021-07-18 15:32:45 +00:00
}
2021-08-01 12:04:04 +00:00
},
);
2021-07-18 15:32:45 +00:00
export default Layout;