Improve dynamic import error message (#3500)

* Improve dynamic import error message
---------
Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
This commit is contained in:
DingDongSoLong4
2023-03-03 03:18:46 +02:00
committed by GitHub
parent 7b07810c12
commit 381486904b
10 changed files with 111 additions and 54 deletions

View File

@@ -1,4 +1,4 @@
import React, { lazy, Suspense, useEffect, useState } from "react"; import React, { Suspense, useEffect, useState } from "react";
import { Route, Switch, useRouteMatch } from "react-router-dom"; import { Route, Switch, useRouteMatch } from "react-router-dom";
import { IntlProvider, CustomFormats } from "react-intl"; import { IntlProvider, CustomFormats } from "react-intl";
import { Helmet } from "react-helmet"; import { Helmet } from "react-helmet";
@@ -31,25 +31,32 @@ import { ReleaseNotesDialog } from "./components/Dialogs/ReleaseNotesDialog";
import { IUIConfig } from "./core/config"; import { IUIConfig } from "./core/config";
import { releaseNotes } from "./docs/en/ReleaseNotes"; import { releaseNotes } from "./docs/en/ReleaseNotes";
import { getPlatformURL, getBaseURL } from "./core/createClient"; import { getPlatformURL, getBaseURL } from "./core/createClient";
import { lazyComponent } from "./utils/lazyComponent";
const Performers = lazy(() => import("./components/Performers/Performers")); const Performers = lazyComponent(
const FrontPage = lazy(() => import("./components/FrontPage/FrontPage")); () => import("./components/Performers/Performers")
const Scenes = lazy(() => import("./components/Scenes/Scenes")); );
const Settings = lazy(() => import("./components/Settings/Settings")); const FrontPage = lazyComponent(
const Stats = lazy(() => import("./components/Stats")); () => import("./components/FrontPage/FrontPage")
const Studios = lazy(() => import("./components/Studios/Studios")); );
const Galleries = lazy(() => import("./components/Galleries/Galleries")); const Scenes = lazyComponent(() => import("./components/Scenes/Scenes"));
const Settings = lazyComponent(() => import("./components/Settings/Settings"));
const Stats = lazyComponent(() => import("./components/Stats"));
const Studios = lazyComponent(() => import("./components/Studios/Studios"));
const Galleries = lazyComponent(
() => import("./components/Galleries/Galleries")
);
const Movies = lazy(() => import("./components/Movies/Movies")); const Movies = lazyComponent(() => import("./components/Movies/Movies"));
const Tags = lazy(() => import("./components/Tags/Tags")); const Tags = lazyComponent(() => import("./components/Tags/Tags"));
const Images = lazy(() => import("./components/Images/Images")); const Images = lazyComponent(() => import("./components/Images/Images"));
const Setup = lazy(() => import("./components/Setup/Setup")); const Setup = lazyComponent(() => import("./components/Setup/Setup"));
const Migrate = lazy(() => import("./components/Setup/Migrate")); const Migrate = lazyComponent(() => import("./components/Setup/Migrate"));
const SceneFilenameParser = lazy( const SceneFilenameParser = lazyComponent(
() => import("./components/SceneFilenameParser/SceneFilenameParser") () => import("./components/SceneFilenameParser/SceneFilenameParser")
); );
const SceneDuplicateChecker = lazy( const SceneDuplicateChecker = lazyComponent(
() => import("./components/SceneDuplicateChecker/SceneDuplicateChecker") () => import("./components/SceneDuplicateChecker/SceneDuplicateChecker")
); );

View File

@@ -1,4 +1,6 @@
import React from "react"; import React from "react";
import { FormattedMessage } from "react-intl";
import { isLazyComponentError } from "src/utils/lazyComponent";
interface IErrorBoundaryProps { interface IErrorBoundaryProps {
children?: React.ReactNode; children?: React.ReactNode;
@@ -10,6 +12,7 @@ type ErrorInfo = {
interface IErrorBoundaryState { interface IErrorBoundaryState {
error?: Error; error?: Error;
errorHelpId?: string;
errorInfo?: ErrorInfo; errorInfo?: ErrorInfo;
} }
@@ -23,22 +26,35 @@ export class ErrorBoundary extends React.Component<
} }
public componentDidCatch(error: Error, errorInfo: ErrorInfo) { public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
let errorHelpId: string | undefined;
if (isLazyComponentError(error)) {
errorHelpId = "errors.lazy_component_error_help";
}
this.setState({ this.setState({
error, error,
errorHelpId,
errorInfo, errorInfo,
}); });
} }
public render() { public render() {
if (this.state.errorInfo) { const { error, errorHelpId, errorInfo } = this.state;
if (errorInfo) {
// Error path // Error path
return ( return (
<div> <div>
<h2>Something went wrong.</h2> <h2>
<FormattedMessage id="errors.something_went_wrong" />
</h2>
{errorHelpId && (
<h5>
<FormattedMessage id={errorHelpId} />
</h5>
)}
<details className="error-message"> <details className="error-message">
{this.state.error && this.state.error.toString()} {error?.toString()}
<br /> <br />
{this.state.errorInfo.componentStack} {errorInfo.componentStack.trim().replaceAll(/^\s*/gm, " ")}
</details> </details>
</div> </div>
); );

View File

@@ -1,6 +1,7 @@
import React, { lazy, Suspense, useState } from "react"; import React, { Suspense, useState } from "react";
import { lazyComponent } from "src/utils/lazyComponent";
const Manual = lazy(() => import("./Manual")); const Manual = lazyComponent(() => import("./Manual"));
interface IManualContextState { interface IManualContextState {
openManual: (tab?: string) => void; openManual: (tab?: string) => void;

View File

@@ -1,12 +1,5 @@
import { Tab, Nav, Dropdown, Button, ButtonGroup } from "react-bootstrap"; import { Tab, Nav, Dropdown, Button, ButtonGroup } from "react-bootstrap";
import React, { import React, { useEffect, useState, useMemo, useContext, useRef } from "react";
useEffect,
useState,
useMemo,
useContext,
lazy,
useRef,
} from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import { useParams, useLocation, useHistory, Link } from "react-router-dom"; import { useParams, useLocation, useHistory, Link } from "react-router-dom";
import { Helmet } from "react-helmet"; import { Helmet } from "react-helmet";
@@ -36,29 +29,38 @@ import { OrganizedButton } from "./OrganizedButton";
import { ConfigurationContext } from "src/hooks/Config"; import { ConfigurationContext } from "src/hooks/Config";
import { getPlayerPosition } from "src/components/ScenePlayer/util"; import { getPlayerPosition } from "src/components/ScenePlayer/util";
import { faEllipsisV } from "@fortawesome/free-solid-svg-icons"; import { faEllipsisV } from "@fortawesome/free-solid-svg-icons";
import { lazyComponent } from "src/utils/lazyComponent";
const SubmitStashBoxDraft = lazy( const SubmitStashBoxDraft = lazyComponent(
() => import("src/components/Dialogs/SubmitDraft") () => import("src/components/Dialogs/SubmitDraft")
); );
const ScenePlayer = lazy( const ScenePlayer = lazyComponent(
() => import("src/components/ScenePlayer/ScenePlayer") () => import("src/components/ScenePlayer/ScenePlayer")
); );
const GalleryViewer = lazy( const GalleryViewer = lazyComponent(
() => import("src/components/Galleries/GalleryViewer") () => import("src/components/Galleries/GalleryViewer")
); );
const ExternalPlayerButton = lazy(() => import("./ExternalPlayerButton")); const ExternalPlayerButton = lazyComponent(
() => import("./ExternalPlayerButton")
);
const QueueViewer = lazy(() => import("./QueueViewer")); const QueueViewer = lazyComponent(() => import("./QueueViewer"));
const SceneMarkersPanel = lazy(() => import("./SceneMarkersPanel")); const SceneMarkersPanel = lazyComponent(() => import("./SceneMarkersPanel"));
const SceneFileInfoPanel = lazy(() => import("./SceneFileInfoPanel")); const SceneFileInfoPanel = lazyComponent(() => import("./SceneFileInfoPanel"));
const SceneEditPanel = lazy(() => import("./SceneEditPanel")); const SceneEditPanel = lazyComponent(() => import("./SceneEditPanel"));
const SceneDetailPanel = lazy(() => import("./SceneDetailPanel")); const SceneDetailPanel = lazyComponent(() => import("./SceneDetailPanel"));
const SceneMoviePanel = lazy(() => import("./SceneMoviePanel")); const SceneMoviePanel = lazyComponent(() => import("./SceneMoviePanel"));
const SceneGalleriesPanel = lazy(() => import("./SceneGalleriesPanel")); const SceneGalleriesPanel = lazyComponent(
const DeleteScenesDialog = lazy(() => import("../DeleteScenesDialog")); () => import("./SceneGalleriesPanel")
const GenerateDialog = lazy(() => import("../../Dialogs/GenerateDialog")); );
const SceneVideoFilterPanel = lazy(() => import("./SceneVideoFilterPanel")); const DeleteScenesDialog = lazyComponent(() => import("../DeleteScenesDialog"));
const GenerateDialog = lazyComponent(
() => import("../../Dialogs/GenerateDialog")
);
const SceneVideoFilterPanel = lazyComponent(
() => import("./SceneVideoFilterPanel")
);
import { objectPath, objectTitle } from "src/core/files"; import { objectPath, objectTitle } from "src/core/files";
interface IProps { interface IProps {

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState, useMemo, lazy } from "react"; import React, { useEffect, useState, useMemo } from "react";
import { FormattedMessage, useIntl } from "react-intl"; import { FormattedMessage, useIntl } from "react-intl";
import { import {
Button, Button,
@@ -50,9 +50,10 @@ import {
import { objectTitle } from "src/core/files"; import { objectTitle } from "src/core/files";
import { galleryTitle } from "src/core/galleries"; import { galleryTitle } from "src/core/galleries";
import { useRatingKeybinds } from "src/hooks/keybinds"; import { useRatingKeybinds } from "src/hooks/keybinds";
import { lazyComponent } from "src/utils/lazyComponent";
const SceneScrapeDialog = lazy(() => import("./SceneScrapeDialog")); const SceneScrapeDialog = lazyComponent(() => import("./SceneScrapeDialog"));
const SceneQueryModal = lazy(() => import("./SceneQueryModal")); const SceneQueryModal = lazyComponent(() => import("./SceneQueryModal"));
interface IProps { interface IProps {
scene: Partial<GQL.SceneDataFragment>; scene: Partial<GQL.SceneDataFragment>;

View File

@@ -1,14 +1,15 @@
import React, { lazy } from "react"; import React from "react";
import { Route, Switch } from "react-router-dom"; import { Route, Switch } from "react-router-dom";
import { useIntl } from "react-intl"; import { useIntl } from "react-intl";
import { Helmet } from "react-helmet"; import { Helmet } from "react-helmet";
import { TITLE_SUFFIX } from "src/components/Shared/constants"; import { TITLE_SUFFIX } from "src/components/Shared/constants";
import { PersistanceLevel } from "src/hooks/ListHook"; import { PersistanceLevel } from "src/hooks/ListHook";
import { lazyComponent } from "src/utils/lazyComponent";
const SceneList = lazy(() => import("./SceneList")); const SceneList = lazyComponent(() => import("./SceneList"));
const SceneMarkerList = lazy(() => import("./SceneMarkerList")); const SceneMarkerList = lazyComponent(() => import("./SceneMarkerList"));
const Scene = lazy(() => import("./SceneDetails/Scene")); const Scene = lazyComponent(() => import("./SceneDetails/Scene"));
const SceneCreate = lazy(() => import("./SceneDetails/SceneCreate")); const SceneCreate = lazyComponent(() => import("./SceneDetails/SceneCreate"));
const Scenes: React.FC = () => { const Scenes: React.FC = () => {
const intl = useIntl(); const intl = useIntl();

View File

@@ -1,7 +1,8 @@
import React, { lazy, Suspense, useCallback, useState } from "react"; import React, { Suspense, useCallback, useState } from "react";
import { lazyComponent } from "src/utils/lazyComponent";
import { ILightboxImage } from "./types"; import { ILightboxImage } from "./types";
const LightboxComponent = lazy(() => import("./Lightbox")); const LightboxComponent = lazyComponent(() => import("./Lightbox"));
export interface IState { export interface IState {
images: ILightboxImage[]; images: ILightboxImage[];

View File

@@ -739,7 +739,7 @@ div.dropdown-menu {
} }
.error-message { .error-message {
white-space: "pre-wrap"; white-space: pre-wrap;
} }
.btn-toolbar .form-control { .btn-toolbar .form-control {

View File

@@ -840,6 +840,10 @@
"warmth": "Warmth" "warmth": "Warmth"
}, },
"empty_server": "Add some scenes to your server to view recommendations on this page.", "empty_server": "Add some scenes to your server to view recommendations on this page.",
"errors": {
"something_went_wrong": "Something went wrong.",
"lazy_component_error_help": "If you recently upgraded Stash, please reload the page or clear your browser cache."
},
"ethnicity": "Ethnicity", "ethnicity": "Ethnicity",
"existing_value": "existing value", "existing_value": "existing value",
"eye_color": "Eye Colour", "eye_color": "Eye Colour",

View File

@@ -0,0 +1,24 @@
import { ComponentType, lazy } from "react";
interface ILazyComponentError {
__lazyComponentError?: true;
}
export const isLazyComponentError = (e: unknown) => {
return !!(e as ILazyComponentError).__lazyComponentError;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const lazyComponent = <T extends ComponentType<any>>(
factory: Parameters<typeof lazy<T>>[0]
) => {
return lazy<T>(async () => {
try {
return await factory();
} catch (e) {
// set flag to identify lazy component loading errors
(e as ILazyComponentError).__lazyComponentError = true;
throw e;
}
});
};