diff --git a/frontend/src/Components/Error/ErrorBoundaryError.css b/frontend/src/Components/Error/ErrorBoundaryError.css
index b6d1f917e..3e7a04302 100644
--- a/frontend/src/Components/Error/ErrorBoundaryError.css
+++ b/frontend/src/Components/Error/ErrorBoundaryError.css
@@ -25,6 +25,10 @@
white-space: pre-wrap;
}
+.version {
+ margin-top: 20px;
+}
+
@media only screen and (max-width: $breakpointMedium) {
.image {
height: 250px;
diff --git a/frontend/src/Components/Error/ErrorBoundaryError.css.d.ts b/frontend/src/Components/Error/ErrorBoundaryError.css.d.ts
index 1d4e56b65..e19fd804d 100644
--- a/frontend/src/Components/Error/ErrorBoundaryError.css.d.ts
+++ b/frontend/src/Components/Error/ErrorBoundaryError.css.d.ts
@@ -6,6 +6,7 @@ interface CssExports {
'image': string;
'imageContainer': string;
'message': string;
+ 'version': string;
}
export const cssExports: CssExports;
export default cssExports;
diff --git a/frontend/src/Components/Error/ErrorBoundaryError.js b/frontend/src/Components/Error/ErrorBoundaryError.js
deleted file mode 100644
index f99930437..000000000
--- a/frontend/src/Components/Error/ErrorBoundaryError.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import styles from './ErrorBoundaryError.css';
-
-function ErrorBoundaryError(props) {
- const {
- className,
- messageClassName,
- detailsClassName,
- message,
- error,
- info
- } = props;
-
- return (
-
-
- {message}
-
-
-
-
-
-
-
- {
- error &&
-
- {error.toString()}
-
- }
-
-
- {info.componentStack}
-
-
-
- );
-}
-
-ErrorBoundaryError.propTypes = {
- className: PropTypes.string.isRequired,
- messageClassName: PropTypes.string.isRequired,
- detailsClassName: PropTypes.string.isRequired,
- message: PropTypes.string.isRequired,
- error: PropTypes.object.isRequired,
- info: PropTypes.object.isRequired
-};
-
-ErrorBoundaryError.defaultProps = {
- className: styles.container,
- messageClassName: styles.message,
- detailsClassName: styles.details,
- message: 'There was an error loading this content'
-};
-
-export default ErrorBoundaryError;
diff --git a/frontend/src/Components/Error/ErrorBoundaryError.tsx b/frontend/src/Components/Error/ErrorBoundaryError.tsx
new file mode 100644
index 000000000..e7e74d10a
--- /dev/null
+++ b/frontend/src/Components/Error/ErrorBoundaryError.tsx
@@ -0,0 +1,73 @@
+import React, { useEffect, useState } from 'react';
+import StackTrace from 'stacktrace-js';
+import translate from 'Utilities/String/translate';
+import styles from './ErrorBoundaryError.css';
+
+interface ErrorBoundaryErrorProps {
+ className: string;
+ messageClassName: string;
+ detailsClassName: string;
+ message: string;
+ error: Error;
+ info: {
+ componentStack: string;
+ };
+}
+
+function ErrorBoundaryError(props: ErrorBoundaryErrorProps) {
+ const {
+ className = styles.container,
+ messageClassName = styles.message,
+ detailsClassName = styles.details,
+ message = translate('ErrorLoadingContent'),
+ error,
+ info,
+ } = props;
+
+ const [detailedError, setDetailedError] = useState<
+ StackTrace.StackFrame[] | null
+ >(null);
+
+ useEffect(() => {
+ if (error) {
+ StackTrace.fromError(error).then((de) => {
+ setDetailedError(de);
+ });
+ } else {
+ setDetailedError(null);
+ }
+ }, [error, setDetailedError]);
+
+ return (
+
+
{message}
+
+
+
+
+
+
+ {error ? {error.message}
: null}
+
+ {detailedError ? (
+ detailedError.map((d, index) => {
+ return (
+
+ {` at ${d.functionName} (${d.fileName}:${d.lineNumber}:${d.columnNumber})`}
+
+ );
+ })
+ ) : (
+ {info.componentStack}
+ )}
+
+ {Version: {window.Lidarr.version}
}
+
+
+ );
+}
+
+export default ErrorBoundaryError;
diff --git a/package.json b/package.json
index df7c13a08..c64d18bf2 100644
--- a/package.json
+++ b/package.json
@@ -83,6 +83,7 @@
"redux-localstorage": "0.4.1",
"redux-thunk": "2.3.0",
"reselect": "4.1.8",
+ "stacktrace-js": "2.0.2",
"typescript": "4.9.5"
},
"devDependencies": {
diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json
index c719d0edb..42d553a0d 100644
--- a/src/NzbDrone.Core/Localization/Core/en.json
+++ b/src/NzbDrone.Core/Localization/Core/en.json
@@ -341,6 +341,7 @@
"Episode": "Episode",
"EpisodeDoesNotHaveAnAbsoluteEpisodeNumber": "Episode does not have an absolute episode number",
"Error": "Error",
+ "ErrorLoadingContent": "There was an error loading this content",
"ErrorLoadingContents": "Error loading contents",
"ErrorLoadingPreviews": "Error loading previews",
"ErrorRestoringBackup": "Error restoring backup",
diff --git a/yarn.lock b/yarn.lock
index f6a956df4..5458d5866 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2892,6 +2892,13 @@ error-ex@^1.3.1:
dependencies:
is-arrayish "^0.2.1"
+error-stack-parser@^2.0.6:
+ version "2.1.4"
+ resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286"
+ integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==
+ dependencies:
+ stackframe "^1.3.4"
+
error@^7.0.0:
version "7.2.1"
resolved "https://registry.yarnpkg.com/error/-/error-7.2.1.tgz#eab21a4689b5f684fc83da84a0e390de82d94894"
@@ -6028,6 +6035,11 @@ source-map-support@~0.5.20:
buffer-from "^1.0.0"
source-map "^0.6.0"
+source-map@0.5.6:
+ version "0.5.6"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
+ integrity sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==
+
source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
@@ -6064,6 +6076,35 @@ spdx-license-ids@^3.0.0:
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz#7189a474c46f8d47c7b0da4b987bb45e908bd2d5"
integrity sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==
+stack-generator@^2.0.5:
+ version "2.0.10"
+ resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.10.tgz#8ae171e985ed62287d4f1ed55a1633b3fb53bb4d"
+ integrity sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==
+ dependencies:
+ stackframe "^1.3.4"
+
+stackframe@^1.3.4:
+ version "1.3.4"
+ resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310"
+ integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==
+
+stacktrace-gps@^3.0.4:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz#0c40b24a9b119b20da4525c398795338966a2fb0"
+ integrity sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==
+ dependencies:
+ source-map "0.5.6"
+ stackframe "^1.3.4"
+
+stacktrace-js@2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/stacktrace-js/-/stacktrace-js-2.0.2.tgz#4ca93ea9f494752d55709a081d400fdaebee897b"
+ integrity sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==
+ dependencies:
+ error-stack-parser "^2.0.6"
+ stack-generator "^2.0.5"
+ stacktrace-gps "^3.0.4"
+
streamqueue@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/streamqueue/-/streamqueue-1.1.2.tgz#6c99c7c20d62b57f5819296bf9ec942542380192"