diff --git a/src/components/structures/ErrorView.tsx b/src/components/structures/ErrorView.tsx new file mode 100644 index 0000000000..566a84808a --- /dev/null +++ b/src/components/structures/ErrorView.tsx @@ -0,0 +1,40 @@ +/* +Copyright 2020 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import * as React from "react"; +import * as PropTypes from "prop-types"; + +interface IProps { + title: React.ReactNode; + message: React.ReactNode; +} + +const ErrorView: React.FC = ({title, message}) => { + return
+
+

{ title }

+

{ message }

+
+
; +}; + +ErrorView.propTypes = { + title: PropTypes.object.isRequired, // jsx for title + message: PropTypes.object.isRequired, // jsx to display +}; + +export default ErrorView; + diff --git a/src/vector/app.js b/src/vector/app.js index 12706d9026..d0acce6374 100644 --- a/src/vector/app.js +++ b/src/vector/app.js @@ -126,7 +126,7 @@ function onTokenLoginCompleted() { window.location.href = formatted; } -export async function loadApp(fragParams: {}, acceptBrowser: boolean, configError: Error|void) { +export async function loadApp(fragParams: {}, acceptBrowser: boolean) { // XXX: the way we pass the path to the worker script from webpack via html in body's dataset is a hack // but alternatives seem to require changing the interface to passing Workers to js-sdk const vectorIndexeddbWorkerScript = document.body.dataset.vectorIndexeddbWorkerScript; @@ -146,36 +146,9 @@ export async function loadApp(fragParams: {}, acceptBrowser: boolean, configErro const params = parseQs(window.location); - // Now that we've loaded the theme (CSS), display the config syntax error if needed. - if (configError && configError.err && configError.err instanceof SyntaxError) { - const errorMessage = ( -
-

- {_t( - "Your Riot configuration contains invalid JSON. Please correct the problem " + - "and reload the page.", - )} -

-

- {_t( - "The message from the parser is: %(message)s", - {message: configError.err.message || _t("Invalid JSON")}, - )} -

-
- ); - - const GenericErrorPage = sdk.getComponent("structures.GenericErrorPage"); - return ; - } - const urlWithoutQuery = window.location.protocol + '//' + window.location.host + window.location.pathname; console.log("Vector starting at " + urlWithoutQuery); - if (configError) { - return
- Unable to load config file: please refresh the page to try again. -
; - } else if (acceptBrowser) { + if (acceptBrowser) { platform.startUpdater(); try { diff --git a/src/vector/index.ts b/src/vector/index.ts index bd5de6bff7..27b48df885 100644 --- a/src/vector/index.ts +++ b/src/vector/index.ts @@ -97,6 +97,8 @@ async function start() { loadLanguage, loadTheme, loadApp, + showError, + _t, } = await import( /* webpackChunkName: "init" */ /* webpackPreload: true */ @@ -130,12 +132,18 @@ async function start() { // load config requires the platform to be ready const loadConfigPromise = loadConfig(); - let configError; try { // await config here await loadConfigPromise; - } catch (err) { - configError = err; + } catch (error) { + // Now that we've loaded the theme (CSS), display the config syntax error if needed. + if (error.err && error.err instanceof SyntaxError) { + return showError(_t("Your Riot is misconfigured"), [ + _t("Your Riot configuration contains invalid JSON. Please correct the problem and reload the page."), + _t("The message from the parser is: %(message)s", { message: error.err.message || _t("Invalid JSON")}), + ]); + } + return showError(_t("Unable to load config file: please refresh the page to try again.")); } // Load language after loading config.json so that settingsDefaults.language can be applied @@ -150,9 +158,27 @@ async function start() { await loadThemePromise; await loadLanguagePromise; - // Finally, load the app. All of the other react-sdk imports are in this file which causes the skinner to - // run on the components. - await loadApp(fragparts.params, acceptBrowser, configError); + if (!acceptBrowser) { + await new Promise(resolve => { + // todo + }); + } + + // Finally, load the app. All of the other react-sdk imports are in this file which causes the skinner to + // run on the components. + await loadApp(fragparts.params, acceptBrowser); + } catch (err) { + console.trace(err); + // check errors in this order: + // Browser Compatibility (skippable) + // config.json + // runtime errors + const { showError } = await import( + /* webpackChunkName: "init" */ + /* webpackPreload: true */ + "./init"); + await showError(err); + } } start().catch(err => { if (!acceptBrowser) { diff --git a/src/vector/init.tsx b/src/vector/init.tsx index a0fe3f4def..8e92935107 100644 --- a/src/vector/init.tsx +++ b/src/vector/init.tsx @@ -21,6 +21,7 @@ limitations under the License. import olmWasmPath from "olm/olm.wasm"; import Olm from 'olm'; import * as ReactDOM from "react-dom"; +import * as React from "react"; import * as languageHandler from "matrix-react-sdk/src/languageHandler"; import SettingsStore from "matrix-react-sdk/src/settings/SettingsStore"; @@ -134,12 +135,31 @@ export async function loadTheme() { setTheme(); } -export async function loadApp(fragParams: {}, acceptBrowser: boolean, configError: Error|void) { +export async function loadApp(fragParams: {}, acceptBrowser: boolean) { // load app.js async so that its code is not executed immediately and we can catch any exceptions const module = await import( /* webpackChunkName: "riot-web-app" */ /* webpackPreload: true */ "./app"); - window.matrixChat = ReactDOM.render(await module.loadApp(fragParams, acceptBrowser, configError), + window.matrixChat = ReactDOM.render(await module.loadApp(fragParams, acceptBrowser), document.getElementById('matrixchat')); } + +export async function showError(title: string, messages?: string[]) { + const ErrorView = (await import( + /* webpackChunkName: "error-view" */ + /* webpackPreload: true */ + "../components/structures/ErrorView")).default; + const message =
+ {messages && messages.map(msg =>

+ {languageHandler._t( + "Your Riot configuration contains invalid JSON. Please correct the problem and reload the page.", + )} +

)} +
; + + window.matrixChat = ReactDOM.render(, + document.getElementById('matrixchat')); +} + +export const _t = languageHandler._t;