From 9626643988a9b0a458ba32069b9826a8f55ffcbb Mon Sep 17 00:00:00 2001 From: Maks <41080668+Maks19@users.noreply.github.com> Date: Tue, 19 Dec 2023 13:48:48 +0200 Subject: [PATCH] [WebApp] Connect error page (#1105) --- packages/web-app/package.json | 2 +- packages/web-app/src/App.tsx | 20 ++++- packages/web-app/src/ErrorBoundary.tsx | 78 ------------------- packages/web-app/src/NotFoundPage.tsx | 65 ---------------- packages/web-app/src/Routes.tsx | 4 +- packages/web-app/src/Store.tsx | 3 + packages/web-app/src/index.tsx | 3 +- .../error-boundary/ErrorBoundaryStore.tsx | 35 +++++++++ .../src/modules/error-boundary/index.tsx | 1 + .../src/modules/profile/ProfileStore.tsx | 3 +- packages/web-app/yarn.lock | 10 +-- 11 files changed, 69 insertions(+), 155 deletions(-) delete mode 100644 packages/web-app/src/ErrorBoundary.tsx delete mode 100644 packages/web-app/src/NotFoundPage.tsx create mode 100644 packages/web-app/src/modules/error-boundary/ErrorBoundaryStore.tsx create mode 100644 packages/web-app/src/modules/error-boundary/index.tsx diff --git a/packages/web-app/package.json b/packages/web-app/package.json index f7ef608ca..8744632e2 100644 --- a/packages/web-app/package.json +++ b/packages/web-app/package.json @@ -152,7 +152,7 @@ "react-copy-to-clipboard": "5.1.0", "react-custom-scrollbars-2": "4.5.0", "react-dom": "18.2.0", - "react-error-boundary": "3.1.4", + "react-error-boundary": "4.0.11", "react-final-form": "6.5.9", "react-helmet": "6.1.0", "react-hint": "3.2.1", diff --git a/packages/web-app/src/App.tsx b/packages/web-app/src/App.tsx index 12c83d12c..84fa78310 100644 --- a/packages/web-app/src/App.tsx +++ b/packages/web-app/src/App.tsx @@ -2,7 +2,10 @@ import { SearchProvider } from '@elastic/react-search-ui' import AppSearchAPIConnector from '@elastic/search-ui-app-search-connector' import classNames from 'classnames' import type { History } from 'history' +import { useEffect } from 'react' import Scrollbars from 'react-custom-scrollbars-2' +import type { UseErrorBoundaryApi } from 'react-error-boundary' +import { useErrorBoundary } from 'react-error-boundary' import type { WithStyles } from 'react-jss' import withStyles from 'react-jss' import { ToastContainer } from 'react-toastify' @@ -104,16 +107,30 @@ const searchConfig = { interface AppProps extends WithStyles { isAuthenticated: boolean + setErrorBoundary: (errorBoundary: UseErrorBoundaryApi) => void withInstallReminder: boolean novuSignature: string history: History } -export const _App = ({ classes, history, isAuthenticated, novuSignature, withInstallReminder }: AppProps) => { +export const _App = ({ + classes, + history, + isAuthenticated, + setErrorBoundary, + novuSignature, + withInstallReminder, +}: AppProps) => { const featureManager = useFeatureManager() + const errorBoundary = useErrorBoundary() + const shouldShowNovuBanner = isAuthenticated && novuSignature const isNewChefDownloadFeatureFlagEnabled = featureManager.isEnabled(FeatureFlags.NewChefDownload) + useEffect(() => { + setErrorBoundary(errorBoundary) + }, [setErrorBoundary, errorBoundary]) + return ( <> {shouldShowNovuBanner && } @@ -159,6 +176,7 @@ export const _App = ({ classes, history, isAuthenticated, novuSignature, withIns const mapStoreToProps = (store: RootStore): any => ({ isAuthenticated: store.auth.isAuthenticated, novuSignature: store.profile.novuSignature, + setErrorBoundary: store.errorBoundary.setErrorBoundary, withInstallReminder: store.profile.withInstallReminder, }) diff --git a/packages/web-app/src/ErrorBoundary.tsx b/packages/web-app/src/ErrorBoundary.tsx deleted file mode 100644 index bb0efdabd..000000000 --- a/packages/web-app/src/ErrorBoundary.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import type { FunctionComponent, ReactNode } from 'react' -import type { FallbackProps } from 'react-error-boundary' -import { ErrorBoundary } from 'react-error-boundary' -import type { WithStyles } from 'react-jss' -import withStyles from 'react-jss' -import type { SaladTheme } from './SaladTheme' -import { SmartLink } from './components' - -const styles = (theme: SaladTheme) => ({ - actionBar: { - textAlign: 'center', - }, - crashReportAction: { - backgroundColor: theme.darkBlue, - color: theme.green, - cursor: 'pointer', - }, - modal: { - backgroundColor: theme.green, - color: theme.darkBlue, - padding: '1rem 0.5rem', - userSelect: 'none', - width: '50vmin', - }, - page: { - alignItems: 'center', - backgroundColor: theme.darkBlue, - bottom: 0, - display: 'flex', - justifyContent: 'center', - left: 0, - position: 'fixed', - right: 0, - top: 0, - }, - title: { - fontFamily: 'SharpGroteskLight09', - fontSize: theme.xLarge, - overflow: 'hidden', - textAlign: 'center', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - }, - description: { - fontFamily: 'sharpGroteskBook19', - fontSize: theme.small, - }, -}) - -interface ErrorPageProps extends FallbackProps, WithStyles {} - -const ErrorPage: FunctionComponent = ({ classes }) => ( -
-
-
There's Trouble in the Kitchen!
-
-

- Salad has encountered an unknown error and cannot continue. Please restart the app to try again. If you - encounter further issues, please submit a support ticket - . -

-

- If you'd like to try to get live help from a fellow Chef, feel free to drop by our{' '} - Discord and post your error in one of the community - support channels. -

-
-
-
-) - -const StyledErrorPage = withStyles(styles)(ErrorPage) - -const ErrorBoundaryPage: FunctionComponent<{ children?: ReactNode }> = ({ children }) => ( - {children} -) - -export { ErrorBoundaryPage as ErrorBoundary } diff --git a/packages/web-app/src/NotFoundPage.tsx b/packages/web-app/src/NotFoundPage.tsx deleted file mode 100644 index fef12138e..000000000 --- a/packages/web-app/src/NotFoundPage.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import type { ReactNode } from 'react' -import { Component } from 'react' -import type { WithStyles } from 'react-jss' -import withStyles from 'react-jss' -import type { SaladTheme } from './SaladTheme' - -const styles = (theme: SaladTheme) => ({ - container: { - width: '100%', - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - fontFamily: 'Mallory', - }, - card: { - backgroundColor: theme.darkGreen, - width: '100%', - maxWidth: 680, - padding: 64, - background: 'linear-gradient(234.33deg, rgba(31, 79, 34, 0.05) -117.15%, #1F4F22 61.94%)', - border: '1px solid #1F4F22', - boxSizing: 'border-box', - boxShadow: '0px 5px 30px rgba(31, 79, 34, 0.8), inset 0px 0px 50px #205123', - backdropFilter: 'blur(10px)', - }, - heading: { - fontSize: '32px', - lineHeight: '34px', - textAlign: 'center', - color: theme.lightGreen, - maxWidth: '850px', - margin: '0 auto 15px', - }, - contentContainer: { - margin: '0 auto', - maxWidth: 325, - }, - subheading: { - fontSize: '16px', - lineHeight: '20px', - textAlign: 'center', - color: theme.green, - }, -}) - -interface Props extends WithStyles {} - -/** - * 404 Page - */ -class _NotFoundPage extends Component { - public override render(): ReactNode { - const { classes } = this.props - return ( -
-
-
Uh-oh, Chef.
-
That page doesn't appear to exist.
-
-
- ) - } -} - -export const NotFoundPage = withStyles(styles)(_NotFoundPage) diff --git a/packages/web-app/src/Routes.tsx b/packages/web-app/src/Routes.tsx index 82fe754aa..eb71236c7 100644 --- a/packages/web-app/src/Routes.tsx +++ b/packages/web-app/src/Routes.tsx @@ -1,7 +1,7 @@ import type { Location } from 'history' import type { RouteComponentProps } from 'react-router' import { Redirect, Route, Switch, withRouter } from 'react-router' -import { NotFoundPage } from './NotFoundPage' +import { NoPageFound } from './components' import { ReferralOnboardingContainer, ReferralWelcomeContainer } from './modules/account-views/referral-views' import { ReplaceBonusModalContainer } from './modules/bonus-views' import { EarnInfoPage, EarningSummaryContainer } from './modules/earn-views' @@ -56,7 +56,7 @@ const _Routes = ({ location }: RouteComponentProps) => { - + ) } diff --git a/packages/web-app/src/Store.tsx b/packages/web-app/src/Store.tsx index ec450cfc1..e54bf0a85 100644 --- a/packages/web-app/src/Store.tsx +++ b/packages/web-app/src/Store.tsx @@ -10,6 +10,7 @@ import { BalanceStore } from './modules/balance' import { BonusStore } from './modules/bonus' import { RefreshService } from './modules/data-refresh' import { EngagementStore } from './modules/engagement' +import { ErrorBoundaryStore } from './modules/error-boundary' import { HelpScoutStore } from './modules/helpscout/HelpScoutStore' import { HomeStore } from './modules/home/HomeStore' import { NotificationStore } from './modules/notifications' @@ -71,6 +72,7 @@ export class RootStore { public readonly onboarding: OnboardingStore public readonly startButtonUI: StartButtonUIStore public readonly saladCard: SaladCardStore + public readonly errorBoundary: ErrorBoundaryStore constructor(axios: AxiosInstance, private readonly featureManager: FeatureManager) { this.routing = new RouterStore() @@ -97,6 +99,7 @@ export class RootStore { this.seasons = new SeasonsStore(axios) this.onboarding = new OnboardingStore(this) this.startButtonUI = new StartButtonUIStore(this) + this.errorBoundary = new ErrorBoundaryStore() // Pass AnalyticsStore to FeatureManager featureManager.setAnalyticsStore(this.analytics) diff --git a/packages/web-app/src/index.tsx b/packages/web-app/src/index.tsx index ceb83f1dc..04722b1f8 100644 --- a/packages/web-app/src/index.tsx +++ b/packages/web-app/src/index.tsx @@ -25,10 +25,9 @@ import { SkeletonTheme } from 'react-loading-skeleton' import { Router } from 'react-router-dom' import { App } from './App' import { createClient } from './axiosFactory' -import { Head } from './components' +import { Head, ErrorBoundary } from './components' import { NovuProviderWrapper } from './components/NovuProviderWrapper' import { config } from './config' -import { ErrorBoundary } from './ErrorBoundary' import { FeatureManagerProvider, UnleashFeatureManager } from './FeatureManager' import { DefaultTheme as JSSTheme } from './SaladTheme' import { createStore } from './Store' diff --git a/packages/web-app/src/modules/error-boundary/ErrorBoundaryStore.tsx b/packages/web-app/src/modules/error-boundary/ErrorBoundaryStore.tsx new file mode 100644 index 000000000..a70bd0893 --- /dev/null +++ b/packages/web-app/src/modules/error-boundary/ErrorBoundaryStore.tsx @@ -0,0 +1,35 @@ +import { action, observable } from 'mobx' +import type { UseErrorBoundaryApi } from 'react-error-boundary' + +export class ErrorBoundaryStore { + @observable + private pendingShowErrorBoundaryActions: (() => void)[] = [] + + @observable + public errorBoundary?: UseErrorBoundaryApi = undefined + + @action + public setErrorBoundary = (errorBoundary: UseErrorBoundaryApi) => { + this.errorBoundary = errorBoundary + // Trigger any queued showErrorBoundary actions + this.pendingShowErrorBoundaryActions.forEach((queuedAction) => { + queuedAction() + }) + this.pendingShowErrorBoundaryActions = [] + } + + @action + public showErrorBoundary = (errorCaughtMessage: Error) => { + if (this.errorBoundary) { + this.errorBoundary.showBoundary(errorCaughtMessage) + } else { + // Queue the showErrorBoundary action if errorBoundary is not defined + this.pendingShowErrorBoundaryActions.push(() => this.showErrorBoundary(errorCaughtMessage)) + } + } + + @action + public resetErrorBoundary = () => { + this.errorBoundary?.resetBoundary() + } +} diff --git a/packages/web-app/src/modules/error-boundary/index.tsx b/packages/web-app/src/modules/error-boundary/index.tsx new file mode 100644 index 000000000..2eefdb97a --- /dev/null +++ b/packages/web-app/src/modules/error-boundary/index.tsx @@ -0,0 +1 @@ +export * from './ErrorBoundaryStore' diff --git a/packages/web-app/src/modules/profile/ProfileStore.tsx b/packages/web-app/src/modules/profile/ProfileStore.tsx index 37e02cf10..e8c6471c3 100644 --- a/packages/web-app/src/modules/profile/ProfileStore.tsx +++ b/packages/web-app/src/modules/profile/ProfileStore.tsx @@ -112,9 +112,10 @@ export class ProfileStore { try { let profile = yield this.axios.get('/api/v1/profile') this.currentProfile = profile.data + this.store.errorBoundary.resetErrorBoundary() } catch (err) { this.currentProfile = undefined - this.store.routing.replace('/profile-error') + this.store.errorBoundary.showErrorBoundary(new Error(`An error occurred: ${err}`)) } return this.currentProfile }) diff --git a/packages/web-app/yarn.lock b/packages/web-app/yarn.lock index 46e42d915..80a3ff2bc 100644 --- a/packages/web-app/yarn.lock +++ b/packages/web-app/yarn.lock @@ -3455,7 +3455,7 @@ __metadata: react-copy-to-clipboard: "npm:5.1.0" react-custom-scrollbars-2: "npm:4.5.0" react-dom: "npm:18.2.0" - react-error-boundary: "npm:3.1.4" + react-error-boundary: "npm:4.0.11" react-final-form: "npm:6.5.9" react-helmet: "npm:6.1.0" react-hint: "npm:3.2.1" @@ -18590,14 +18590,14 @@ __metadata: languageName: node linkType: hard -"react-error-boundary@npm:3.1.4": - version: 3.1.4 - resolution: "react-error-boundary@npm:3.1.4" +"react-error-boundary@npm:4.0.11": + version: 4.0.11 + resolution: "react-error-boundary@npm:4.0.11" dependencies: "@babel/runtime": "npm:^7.12.5" peerDependencies: react: ">=16.13.1" - checksum: f977ca61823e43de2381d53dd7aa8b4d79ff6a984c9afdc88dc44f9973b99de7fd382d2f0f91f2688e24bb987c0185bf45d0b004f22afaaab0f990a830253bfb + checksum: 33dad3df7687971e65c7182d97f44bd618cb5d77d1c338e0a7c17c5cf7706a07b9055fffb771ff19bad750d40dd3cfd18d661a60b0518e73197e294dc185f18c languageName: node linkType: hard