From c56f471d65e2085cd629bc21f831b729ebef377d Mon Sep 17 00:00:00 2001 From: izawartka <59137928+izawartka@users.noreply.github.com> Date: Mon, 9 Jun 2025 22:38:49 +0200 Subject: [PATCH] Added scenery loaded dialog +Added a dialog shown after scenery parsing is complete, displaying whether any warnings/errors occurred and, if so, showing the parser logs *Improved parser's error handling *Fixed a typo in the switch parsing related warning --- .../SceneryLoadedDialog.css | 14 ++++++ .../SceneryLoadedDialog.js | 46 +++++++++++++++++++ .../SceneryParserLogView.css | 22 +++++++++ .../SceneryParserLogView.js | 27 +++++++++++ src/components/toolbar/FileSelect.js | 20 +++++--- src/helpers/constants.js | 3 +- src/model/scenery-parser-log.js | 19 ++++++++ src/model/switch.js | 2 +- 8 files changed, 145 insertions(+), 8 deletions(-) create mode 100644 src/components/scenery-loaded-dialog/SceneryLoadedDialog.css create mode 100644 src/components/scenery-loaded-dialog/SceneryLoadedDialog.js create mode 100644 src/components/scenery-loaded-dialog/SceneryParserLogView.css create mode 100644 src/components/scenery-loaded-dialog/SceneryParserLogView.js diff --git a/src/components/scenery-loaded-dialog/SceneryLoadedDialog.css b/src/components/scenery-loaded-dialog/SceneryLoadedDialog.css new file mode 100644 index 0000000..09c0e35 --- /dev/null +++ b/src/components/scenery-loaded-dialog/SceneryLoadedDialog.css @@ -0,0 +1,14 @@ +.scenery-loaded-dialog-content { + display: flex; + flex-direction: column; + gap: 1em; +} + +.scenery-loaded-dialog-content .main-message { + padding: 0.5em; +} + +.scenery-loaded-dialog-content .scenery-parser-log-view { + max-height: 30vh; + overflow-y: auto; +} diff --git a/src/components/scenery-loaded-dialog/SceneryLoadedDialog.js b/src/components/scenery-loaded-dialog/SceneryLoadedDialog.js new file mode 100644 index 0000000..97dc1b5 --- /dev/null +++ b/src/components/scenery-loaded-dialog/SceneryLoadedDialog.js @@ -0,0 +1,46 @@ +import { forwardRef, useImperativeHandle } from 'react'; +import SceneryParserLog from '../../model/scenery-parser-log'; +import SceneryParserLogView from '../scenery-loaded-dialog/SceneryParserLogView'; +import '../dialog/Dialog.css'; +import './SceneryLoadedDialog.css' +import Constants from '../../helpers/constants'; + +const SceneryLoadedDialog = forwardRef((props, ref) => { + const {onClose, loadingError} = props; + + const onBgClick = () => { + onClose(); + } + + useImperativeHandle(ref, () => ({ + onBgClick: onBgClick + })); + + const warningsOrError = SceneryParserLog.hasWarnings || !!loadingError; + const showLog = Constants.parser.alwaysShowLogDialog || warningsOrError; + + const message = loadingError ? "An error occurred while loading the scenery" + :( SceneryParserLog.hasWarnings ? "Scenery loaded with warnings" + : "Scenery loaded successfully" ); + + const additionalErr = (loadingError && !SceneryParserLog.hasErrors) ? + loadingError.message : undefined; + + return ( +
+
+
+ {message} +
+ {showLog && } +
+
+ +
+
+ ); +}); + +export default SceneryLoadedDialog; diff --git a/src/components/scenery-loaded-dialog/SceneryParserLogView.css b/src/components/scenery-loaded-dialog/SceneryParserLogView.css new file mode 100644 index 0000000..baef0ae --- /dev/null +++ b/src/components/scenery-loaded-dialog/SceneryParserLogView.css @@ -0,0 +1,22 @@ +.scenery-parser-log-view { + padding: 1em; + border-radius: 8px; + border: 2px solid #444; + background-color: #0004; +} + +.scenery-parser-log-view ul { + list-style-type: none; + padding: 0; + margin: 0; + text-indent: -1em; + padding-left: 1em; +} + +.scenery-parser-log-view .log-entry.warn { + color: #aa4; +} + +.scenery-parser-log-view .log-entry.error { + color: #f44; +} \ No newline at end of file diff --git a/src/components/scenery-loaded-dialog/SceneryParserLogView.js b/src/components/scenery-loaded-dialog/SceneryParserLogView.js new file mode 100644 index 0000000..3226daf --- /dev/null +++ b/src/components/scenery-loaded-dialog/SceneryParserLogView.js @@ -0,0 +1,27 @@ +import './SceneryParserLogView.css'; + +export default function SceneryParserLogView({ log, additionalErr }) { + const extendedLog = additionalErr ? [...log, { logType: 'error', message: additionalErr }] : log; + + return ( +
+ {extendedLog.length === 0 ? ( +
No log entries.
+ ) : ( + + )} +
+ ); +} + +function LogEntry({ entry }) { + const { logType, message } = entry; + + return ( +
  • + {message} +
  • + ); +} \ No newline at end of file diff --git a/src/components/toolbar/FileSelect.js b/src/components/toolbar/FileSelect.js index e1b79a3..6159860 100644 --- a/src/components/toolbar/FileSelect.js +++ b/src/components/toolbar/FileSelect.js @@ -1,6 +1,9 @@ import { useContext } from "react"; import MainContext from "../../contexts/MainContext"; import Scenery from "../../model/scenery"; +import { showCustomDialog } from "../../services/dialogService"; +import SceneryLoadedDialog from "../scenery-loaded-dialog/SceneryLoadedDialog"; +import SceneryParserLog from "../../model/scenery-parser-log"; export default function FileSelect() { const {setScenery, setIsLoading} = useContext(MainContext); @@ -9,17 +12,22 @@ export default function FileSelect() { const file = event.target.files[0]; if (!file) return; + SceneryParserLog.clear(); setIsLoading(true); const reader = new FileReader(); reader.readAsText(file); reader.onload = (e) => { - setIsLoading(false); - if(!e.target || !e.target.result) { - console.error("File reading failed or no content found."); - } + let loadingError = null; + try { + const scenery = Scenery.fromText(e.target?.result); + setScenery(scenery); + } catch (error) { + loadingError = error; + setScenery(null); + } - const scenery = Scenery.fromText(e.target.result); - setScenery(scenery); + setIsLoading(false); + showCustomDialog(); } }; diff --git a/src/helpers/constants.js b/src/helpers/constants.js index 37b77b8..0a85c17 100644 --- a/src/helpers/constants.js +++ b/src/helpers/constants.js @@ -6,7 +6,8 @@ const Constants = { parser: { forceAutoSwitches: false, logNewAutoSwitches: true, - sceneryInfoVersion: 29 + sceneryInfoVersion: 29, + alwaysShowLogDialog: false }, warnings: { all: false, // enable all warnings diff --git a/src/model/scenery-parser-log.js b/src/model/scenery-parser-log.js index cd631c6..7990931 100644 --- a/src/model/scenery-parser-log.js +++ b/src/model/scenery-parser-log.js @@ -1,13 +1,32 @@ import Constants from "../helpers/constants"; export default class SceneryParserLog { + static log = []; + static hasWarnings = false; + static hasErrors = false; + + static clear() { + SceneryParserLog.log = []; + SceneryParserLog.hasWarnings = false; + SceneryParserLog.hasErrors = false; + } + static warn(type, message) { if(!Constants.warnings[type] && !Constants.warnings.all) return; + SceneryParserLog.hasWarnings = true; + SceneryParserLog.log.push({logType: 'warn', type, message}); console.warn(`[SceneryParserLog] ${message}`); } static error(type, message) { + SceneryParserLog.hasErrors = true; + SceneryParserLog.log.push({logType: 'error', type, message}); + throw new Error(`[SceneryParserLog] ${message}`); } + + static getLog() { + return [...SceneryParserLog.log]; + } } diff --git a/src/model/switch.js b/src/model/switch.js index 73c0403..97ee7bb 100644 --- a/src/model/switch.js +++ b/src/model/switch.js @@ -63,7 +63,7 @@ export default class Switch extends SceneryObject { let def = DefinedSwitches[this.bare_model] || null; if(!def && !Constants.parser.forceAutoSwitches) { - SceneryParserLog.warn('switchUndefinedModel', `Switch ${this.id} has undefined model "${this.bare_model}", trying to generate auto model definition`); + SceneryParserLog.warn('switchUndefinedModel', `Switch ${this.id} has an undefined model "${this.bare_model}", trying to generate auto model definition`); } if(!def || Constants.parser.forceAutoSwitches) {