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 05cf8165a..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..6e9c16281
--- /dev/null
+++ b/frontend/src/Components/Error/ErrorBoundaryError.tsx
@@ -0,0 +1,72 @@
+import React, { useEffect, useState } from 'react';
+import StackTrace from 'stacktrace-js';
+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 = 'There was an error loading this content',
+ 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.Radarr.version}
}
+
+
+ );
+}
+
+export default ErrorBoundaryError;
diff --git a/frontend/typings/Globals.d.ts b/frontend/typings/Globals.d.ts
index 60260a3ad..29136ac48 100644
--- a/frontend/typings/Globals.d.ts
+++ b/frontend/typings/Globals.d.ts
@@ -1 +1,11 @@
declare module '*.module.css';
+
+interface Window {
+ Radarr: {
+ apiKey: string;
+ instanceName: string;
+ theme: string;
+ urlBase: string;
+ version: string;
+ };
+}
diff --git a/package.json b/package.json
index af66e9316..5d1ba641e 100644
--- a/package.json
+++ b/package.json
@@ -83,6 +83,7 @@
"redux-localstorage": "0.4.1",
"redux-thunk": "2.3.0",
"reselect": "4.1.5",
+ "stacktrace-js": "2.0.2",
"swiper": "8.3.2",
"typescript": "4.9.4"
},
diff --git a/yarn.lock b/yarn.lock
index a4aafd8d8..34fd98939 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3266,6 +3266,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"
@@ -7323,6 +7330,11 @@ source-map-url@^0.4.0:
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56"
integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==
+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.5.6:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
@@ -7381,6 +7393,13 @@ ssr-window@^4.0.0, ssr-window@^4.0.2:
resolved "https://registry.yarnpkg.com/ssr-window/-/ssr-window-4.0.2.tgz#dc6b3ee37be86ac0e3ddc60030f7b3bc9b8553be"
integrity sha512-ISv/Ch+ig7SOtw7G2+qkwfVASzazUnvlDTwypdLoPoySv+6MqlOV10VwPSE6EWkGjhW50lUmghPmpYZXMu/+AQ==
+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"
+
stack-utils@^2.0.3:
version "2.0.6"
resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f"
@@ -7388,6 +7407,28 @@ stack-utils@^2.0.3:
dependencies:
escape-string-regexp "^2.0.0"
+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"
+
static-extend@^0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"