mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 21:04:37 +03:00
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:
@@ -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")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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>;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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[];
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
24
ui/v2.5/src/utils/lazyComponent.ts
Normal file
24
ui/v2.5/src/utils/lazyComponent.ts
Normal 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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user