From 37c355da51b654cea7309678c32a83a5cbe43d1f Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 16 Feb 2023 16:45:09 -0800 Subject: [PATCH] Improved UI error messages (stack trace and version) --- .../Components/Error/ErrorBoundaryError.css | 4 + .../Error/ErrorBoundaryError.css.d.ts | 1 + .../Components/Error/ErrorBoundaryError.js | 60 --------------- .../Components/Error/ErrorBoundaryError.tsx | 73 +++++++++++++++++++ frontend/typings/Globals.d.ts | 10 +++ package.json | 1 + yarn.lock | 41 +++++++++++ 7 files changed, 130 insertions(+), 60 deletions(-) delete mode 100644 frontend/src/Components/Error/ErrorBoundaryError.js create mode 100644 frontend/src/Components/Error/ErrorBoundaryError.tsx 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 e0181db96..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..e19575b47 --- /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 styles from './ErrorBoundaryError.css'; + +interface ErrorBoundaryErrorProps { + className: string; + messageClassName: string; + detailsClassName: string; + message: string; + error: Error; + info: { + componentStack: string; + }; +} + +function ErrorBoundaryError(props: ErrorBoundaryErrorProps) { + const t1 = 1; + const t2 = 2; + + 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(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.Sonarr.version}
} +
+
+ ); +} + +export default ErrorBoundaryError; diff --git a/frontend/typings/Globals.d.ts b/frontend/typings/Globals.d.ts index 60260a3ad..d11f99013 100644 --- a/frontend/typings/Globals.d.ts +++ b/frontend/typings/Globals.d.ts @@ -1 +1,11 @@ declare module '*.module.css'; + +interface Window { + Sonarr: { + apiKey: string; + instanceName: string; + theme: string; + urlBase: string; + version: string; + }; +} diff --git a/package.json b/package.json index 75ea581df..3d6dd5068 100644 --- a/package.json +++ b/package.json @@ -85,6 +85,7 @@ "redux-localstorage": "0.4.1", "redux-thunk": "2.4.1", "reselect": "4.1.5", + "stacktrace-js": "2.0.2", "typescript": "4.9.4" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index c7be12c32..76090bb5a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2974,6 +2974,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" @@ -6329,6 +6336,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" @@ -6370,6 +6382,13 @@ specificity@^0.4.1: resolved "https://registry.yarnpkg.com/specificity/-/specificity-0.4.1.tgz#aab5e645012db08ba182e151165738d00887b019" integrity sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg== +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" @@ -6377,6 +6396,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" + stdin@*: version "0.0.1" resolved "https://registry.yarnpkg.com/stdin/-/stdin-0.0.1.tgz#d3041981aaec3dfdbc77a1b38d6372e38f5fb71e"