From 7b930da3436423c0428ff5093778367717afa275 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 4 Apr 2020 17:21:59 +0100 Subject: [PATCH 01/10] Make app load more async Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/vector/index.js | 30 +++++++++++++++++++----------- src/vector/init.ts | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 11 deletions(-) diff --git a/src/vector/index.js b/src/vector/index.js index ff31d7b62c..6d4411cd71 100644 --- a/src/vector/index.js +++ b/src/vector/index.js @@ -33,15 +33,23 @@ if ('serviceWorker' in navigator) { navigator.serviceWorker.register('sw.js'); } -// Ensure the skin is the very first thing to load for the react-sdk. We don't even want to reference -// the SDK until we have to in imports. -console.log("Loading skin..."); -import * as sdk from 'matrix-react-sdk'; -import * as skin from "../component-index"; -sdk.loadSkin(skin); -console.log("Skin loaded!"); +// React depends on Map & Set which we check for using modernizr's es6collections +// if modernizr fails we may not have a functional react to show the error message. +// try in react but fallback to an `alert` +async function start() { + // load init.ts async so that its code is not executed immediately and we can catch any exceptions + const {loadSkin, loadApp} = await import( + /* webpackChunkName: "init" */ + /* webpackPreload: true */ + "./init"); -// Finally, load the app. All of the other react-sdk imports are in this file which causes the skinner to -// run on the components. We use `require` here to make sure webpack doesn't optimize this into an async -// import and thus running before the skin can load. -require("./app").loadApp(); + await loadSkin(); + await loadApp(); +} +start().catch(err => { + // try show the error in React + console.error(err); +}).catch(err => { + // fall back to showing the error in an alert + console.error(err); +}); diff --git a/src/vector/init.ts b/src/vector/init.ts index 96745f53cc..9b720c7a15 100644 --- a/src/vector/init.ts +++ b/src/vector/init.ts @@ -112,3 +112,35 @@ export async function loadLanguage() { console.error("Unable to set language", e); } } + +export async function loadSkin() { + // Ensure the skin is the very first thing to load for the react-sdk. We don't even want to reference + // the SDK until we have to in imports. + console.log("Loading skin..."); + const [sdk, skin] = await Promise.all([ + import( + /* webpackChunkName: "matrix-react-sdk" */ + /* webpackPreload: true */ + "matrix-react-sdk"), + import( + /* webpackChunkName: "riot-web-component-index" */ + /* webpackPreload: true */ + "../component-index"), + ]); + sdk.loadSkin(skin); + console.log("Skin loaded!"); +} + +export async function loadApp() { + // Finally, load the app. All of the other react-sdk imports are in this file which causes the skinner to + // run on the components. We use `require` here to make sure webpack doesn't optimize this into an async + // import and thus running before the skin can load. + const module = await import( + /* webpackChunkName: "riot-web-app" */ + /* webpackPreload: true */ + "./app"); + await module.loadApp(); +} + +// throw new Error("foobar"); +window.Map = undefined; From 37ed89c25fa6a89600e1f033e55f47245ea30901 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 4 Apr 2020 17:34:33 +0100 Subject: [PATCH 02/10] move rageshake init into init.ts to allow for code-splitting Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/vector/index.js | 13 +++++++++++-- src/vector/init.ts | 8 ++++++-- src/vector/rageshakesetup.ts | 4 +--- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/vector/index.js b/src/vector/index.js index 6d4411cd71..44c827726c 100644 --- a/src/vector/index.js +++ b/src/vector/index.js @@ -25,7 +25,6 @@ require('gfm.css/gfm.css'); require('highlight.js/styles/github.css'); // These are things that can run before the skin loads - be careful not to reference the react-sdk though. -import './rageshakesetup'; import './modernizr'; // load service worker if available on this platform @@ -33,16 +32,26 @@ if ('serviceWorker' in navigator) { navigator.serviceWorker.register('sw.js'); } +async function settled(prom) { + try { + await prom; + } catch (e) { + console.error(e); + } +} + // React depends on Map & Set which we check for using modernizr's es6collections // if modernizr fails we may not have a functional react to show the error message. // try in react but fallback to an `alert` async function start() { // load init.ts async so that its code is not executed immediately and we can catch any exceptions - const {loadSkin, loadApp} = await import( + const {rageshakePromise, loadSkin, loadApp} = await import( /* webpackChunkName: "init" */ /* webpackPreload: true */ "./init"); + await settled(rageshakePromise); // give rageshake a chance to load/fail + await loadSkin(); await loadApp(); } diff --git a/src/vector/init.ts b/src/vector/init.ts index 9b720c7a15..f53b9231b9 100644 --- a/src/vector/init.ts +++ b/src/vector/init.ts @@ -21,13 +21,17 @@ limitations under the License. import olmWasmPath from "olm/olm.wasm"; import Olm from 'olm'; -import * as languageHandler from 'matrix-react-sdk/src/languageHandler'; +import * as languageHandler from "matrix-react-sdk/src/languageHandler"; import SettingsStore from "matrix-react-sdk/src/settings/SettingsStore"; import ElectronPlatform from "./platform/ElectronPlatform"; import WebPlatform from "./platform/WebPlatform"; -import PlatformPeg from 'matrix-react-sdk/src/PlatformPeg'; +import PlatformPeg from "matrix-react-sdk/src/PlatformPeg"; import SdkConfig from "matrix-react-sdk/src/SdkConfig"; +import { initRageshake } from "./rageshakesetup"; + + +export const rageshakePromise = initRageshake(); export function preparePlatform() { if ((window).ipcRenderer) { diff --git a/src/vector/rageshakesetup.ts b/src/vector/rageshakesetup.ts index 6445f4e95c..cef9d53823 100644 --- a/src/vector/rageshakesetup.ts +++ b/src/vector/rageshakesetup.ts @@ -30,7 +30,7 @@ import * as rageshake from "matrix-react-sdk/src/rageshake/rageshake"; import SdkConfig from "matrix-react-sdk/src/SdkConfig"; import sendBugReport from "matrix-react-sdk/src/rageshake/submit-rageshake"; -function initRageshake() { +export function initRageshake() { rageshake.init().then(() => { console.log("Initialised rageshake."); console.log("To fix line numbers in Chrome: " + @@ -48,8 +48,6 @@ function initRageshake() { }); } -initRageshake(); - window.mxSendRageshake = function(text: string, withLogs?: boolean) { if (withLogs === undefined) withLogs = true; if (!text || !text.trim()) { From 4b6164d823cf53667fab2a15b8496934e597c50d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 4 Apr 2020 23:42:19 +0100 Subject: [PATCH 03/10] iterate app load order tweaks Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/vector/index.js | 6 ++++++ src/vector/init.ts | 8 ++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/vector/index.js b/src/vector/index.js index 44c827726c..a919cefea2 100644 --- a/src/vector/index.js +++ b/src/vector/index.js @@ -53,12 +53,18 @@ async function start() { await settled(rageshakePromise); // give rageshake a chance to load/fail await loadSkin(); + + // Finally, load the app. All of the other react-sdk imports are in this file which causes the skinner to + // run on the components. We use `require` here to make sure webpack doesn't optimize this into an async + // import and thus running before the skin can load. await loadApp(); } start().catch(err => { // try show the error in React + console.log("Show React error page"); console.error(err); }).catch(err => { // fall back to showing the error in an alert + console.log("Show fallback error page"); console.error(err); }); diff --git a/src/vector/init.ts b/src/vector/init.ts index f53b9231b9..2344a9387e 100644 --- a/src/vector/init.ts +++ b/src/vector/init.ts @@ -121,6 +121,7 @@ export async function loadSkin() { // Ensure the skin is the very first thing to load for the react-sdk. We don't even want to reference // the SDK until we have to in imports. console.log("Loading skin..."); + // load these async so that its code is not executed immediately and we can catch any exceptions const [sdk, skin] = await Promise.all([ import( /* webpackChunkName: "matrix-react-sdk" */ @@ -136,15 +137,10 @@ export async function loadSkin() { } export async function loadApp() { - // Finally, load the app. All of the other react-sdk imports are in this file which causes the skinner to - // run on the components. We use `require` here to make sure webpack doesn't optimize this into an async - // import and thus running before the skin can load. + // 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"); await module.loadApp(); } - -// throw new Error("foobar"); -window.Map = undefined; From b5318b4ebc0c3a117eee4b25ef90536b0da7791a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 5 Apr 2020 00:05:59 +0100 Subject: [PATCH 04/10] fix global.d.ts Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/@types/global.d.ts | 3 ++ src/vector/app.js | 66 +++++++++++++++++------------------------- src/vector/init.ts | 3 +- 3 files changed, 31 insertions(+), 41 deletions(-) diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 646fe6ea14..90411e7b08 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -14,9 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ +type ReactNode = import("react").ReactNode; + interface Window { Olm: { init: () => Promise; }; mxSendRageshake: (text: string, withLogs?: boolean) => void; + matrixChat: ReactNode; } diff --git a/src/vector/app.js b/src/vector/app.js index 131e1ca41b..dcdc476998 100644 --- a/src/vector/app.js +++ b/src/vector/app.js @@ -23,7 +23,6 @@ import React from 'react'; // access via the console global.React = React; -import ReactDOM from 'react-dom'; import * as sdk from 'matrix-react-sdk'; import PlatformPeg from 'matrix-react-sdk/src/PlatformPeg'; import * as VectorConferenceHandler from 'matrix-react-sdk/src/VectorConferenceHandler'; @@ -235,11 +234,7 @@ export async function loadApp() { ); const GenericErrorPage = sdk.getComponent("structures.GenericErrorPage"); - window.matrixChat = ReactDOM.render( - , - document.getElementById('matrixchat'), - ); - return; + return ; } const validBrowser = checkBrowserFeatures(); @@ -249,31 +244,29 @@ export async function loadApp() { const urlWithoutQuery = window.location.protocol + '//' + window.location.host + window.location.pathname; console.log("Vector starting at " + urlWithoutQuery); if (configError) { - window.matrixChat = ReactDOM.render(
+ return
Unable to load config file: please refresh the page to try again. -
, document.getElementById('matrixchat')); +
; } else if (validBrowser || acceptInvalidBrowser) { platform.startUpdater(); - // Don't bother loading the app until the config is verified - verifyServerConfig().then((newConfig) => { + try { + // Don't bother loading the app until the config is verified + const config = await verifyServerConfig(); const MatrixChat = sdk.getComponent('structures.MatrixChat'); - window.matrixChat = ReactDOM.render( - , - document.getElementById('matrixchat'), - ); - }).catch(err => { + return ; + } catch (err) { console.error(err); let errorMessage = err.translatedMessage @@ -282,23 +275,17 @@ export async function loadApp() { // Like the compatibility page, AWOOOOOGA at the user const GenericErrorPage = sdk.getComponent("structures.GenericErrorPage"); - window.matrixChat = ReactDOM.render( - , - document.getElementById('matrixchat'), - ); - }); + return ; + } } else { console.error("Browser is missing required features."); // take to a different landing page to AWOOOOOGA at the user const CompatibilityPage = sdk.getComponent("structures.CompatibilityPage"); - window.matrixChat = ReactDOM.render( - , - document.getElementById('matrixchat'), - ); + return ; } } @@ -384,7 +371,6 @@ async function verifyServerConfig() { } } - validatedConfig.isDefault = true; // Just in case we ever have to debug this diff --git a/src/vector/init.ts b/src/vector/init.ts index 2344a9387e..1933b78811 100644 --- a/src/vector/init.ts +++ b/src/vector/init.ts @@ -20,6 +20,7 @@ limitations under the License. // @ts-ignore import olmWasmPath from "olm/olm.wasm"; import Olm from 'olm'; +import * as ReactDOM from "react-dom"; import * as languageHandler from "matrix-react-sdk/src/languageHandler"; import SettingsStore from "matrix-react-sdk/src/settings/SettingsStore"; @@ -142,5 +143,5 @@ export async function loadApp() { /* webpackChunkName: "riot-web-app" */ /* webpackPreload: true */ "./app"); - await module.loadApp(); + window.matrixChat = ReactDOM.render(await module.loadApp(), document.getElementById('matrixchat')); } From d4297560940fe8dd700e2e9602ca55db9f0e5809 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 5 Apr 2020 00:08:31 +0100 Subject: [PATCH 05/10] delint Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/vector/init.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vector/init.ts b/src/vector/init.ts index 1933b78811..57941e88cb 100644 --- a/src/vector/init.ts +++ b/src/vector/init.ts @@ -131,6 +131,7 @@ export async function loadSkin() { import( /* webpackChunkName: "riot-web-component-index" */ /* webpackPreload: true */ + // @ts-ignore - this module is generated so may fail lint "../component-index"), ]); sdk.loadSkin(skin); From 20442413bf84e6a28801e24ec850c08466fbea87 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 5 Apr 2020 00:12:37 +0100 Subject: [PATCH 06/10] remove future error handling path Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/vector/index.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/vector/index.js b/src/vector/index.js index a919cefea2..9ad72870e1 100644 --- a/src/vector/index.js +++ b/src/vector/index.js @@ -59,12 +59,4 @@ async function start() { // import and thus running before the skin can load. await loadApp(); } -start().catch(err => { - // try show the error in React - console.log("Show React error page"); - console.error(err); -}).catch(err => { - // fall back to showing the error in an alert - console.log("Show fallback error page"); - console.error(err); -}); +start(); From 093b7bbf720f4eb60288fd97a3012716f939557e Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 5 Apr 2020 00:27:59 +0100 Subject: [PATCH 07/10] convert index.js to typescript Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/vector/{index.js => index.ts} | 2 +- src/vector/rageshakesetup.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) rename src/vector/{index.js => index.ts} (98%) diff --git a/src/vector/index.js b/src/vector/index.ts similarity index 98% rename from src/vector/index.js rename to src/vector/index.ts index 9ad72870e1..74db1cd687 100644 --- a/src/vector/index.js +++ b/src/vector/index.ts @@ -32,7 +32,7 @@ if ('serviceWorker' in navigator) { navigator.serviceWorker.register('sw.js'); } -async function settled(prom) { +async function settled(prom: Promise) { try { await prom; } catch (e) { diff --git a/src/vector/rageshakesetup.ts b/src/vector/rageshakesetup.ts index cef9d53823..e495557429 100644 --- a/src/vector/rageshakesetup.ts +++ b/src/vector/rageshakesetup.ts @@ -31,7 +31,8 @@ import SdkConfig from "matrix-react-sdk/src/SdkConfig"; import sendBugReport from "matrix-react-sdk/src/rageshake/submit-rageshake"; export function initRageshake() { - rageshake.init().then(() => { + const prom = rageshake.init(); + prom.then(() => { console.log("Initialised rageshake."); console.log("To fix line numbers in Chrome: " + "Meatball menu → Settings → Blackboxing → Add /rageshake\\.js$"); @@ -46,6 +47,7 @@ export function initRageshake() { }, (err) => { console.error("Failed to initialise rageshake: " + err); }); + return prom; } window.mxSendRageshake = function(text: string, withLogs?: boolean) { From b1575524aa853fa0653d9400937c28e7cd1fe8c5 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sun, 5 Apr 2020 00:55:36 +0100 Subject: [PATCH 08/10] Switch things to typescript, use @types/modernizr, fix global.d.ts. Move mobile_guide redirect to index.ts Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- package.json | 1 + src/@types/global.d.ts | 24 +++++++++++----- src/vector/app.js | 63 ++--------------------------------------- src/vector/index.ts | 62 +++++++++++++++++++++++++++++++++++++++- src/vector/init.ts | 5 ++-- src/vector/url_utils.ts | 2 +- webpack.config.js | 2 +- yarn.lock | 5 ++++ 8 files changed, 92 insertions(+), 72 deletions(-) diff --git a/package.json b/package.json index 22370b1706..fcb2b3e5d3 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,7 @@ "@babel/preset-typescript": "^7.7.4", "@babel/register": "^7.7.4", "@babel/runtime": "^7.7.6", + "@types/modernizr": "^3.5.3", "@types/react": "16.9", "@types/react-dom": "^16.9.4", "autoprefixer": "^9.7.3", diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 90411e7b08..a19e20b5c3 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -14,12 +14,22 @@ See the License for the specific language governing permissions and limitations under the License. */ -type ReactNode = import("react").ReactNode; +import {ReactNode} from "react"; +import "modernizr"; -interface Window { - Olm: { - init: () => Promise; - }; - mxSendRageshake: (text: string, withLogs?: boolean) => void; - matrixChat: ReactNode; +declare global { + interface Window { + Modernizr: ModernizrAPI & FeatureDetects; + Olm: { + init: () => Promise; + }; + + mxSendRageshake: (text: string, withLogs?: boolean) => void; + matrixChat: ReactNode; + } + + // workaround for https://github.com/microsoft/TypeScript/issues/30933 + interface ObjectConstructor { + fromEntries?(xs: [string|number|symbol, any][]): object + } } diff --git a/src/vector/app.js b/src/vector/app.js index dcdc476998..836b2ef1d0 100644 --- a/src/vector/app.js +++ b/src/vector/app.js @@ -44,41 +44,6 @@ import {loadConfig, preparePlatform, loadLanguage, loadOlm} from "./init"; let lastLocationHashSet = null; -function checkBrowserFeatures() { - if (!window.Modernizr) { - console.error("Cannot check features - Modernizr global is missing."); - return false; - } - - // custom checks atop Modernizr because it doesn't have ES2018/ES2019 checks in it for some features we depend on, - // Modernizr requires rules to be lowercase with no punctuation: - // ES2018: http://www.ecma-international.org/ecma-262/9.0/#sec-promise.prototype.finally - window.Modernizr.addTest("promiseprototypefinally", () => - window.Promise && window.Promise.prototype && typeof window.Promise.prototype.finally === "function"); - // ES2019: http://www.ecma-international.org/ecma-262/10.0/#sec-object.fromentries - window.Modernizr.addTest("objectfromentries", () => - window.Object && typeof window.Object.fromEntries === "function"); - - const featureList = Object.keys(window.Modernizr); - - let featureComplete = true; - for (let i = 0; i < featureList.length; i++) { - if (window.Modernizr[featureList[i]] === undefined) { - console.error( - "Looked for feature '%s' but Modernizr has no results for this. " + - "Has it been configured correctly?", featureList[i], - ); - return false; - } - if (window.Modernizr[featureList[i]] === false) { - console.error("Browser missing feature: '%s'", featureList[i]); - // toggle flag rather than return early so we log all missing features rather than just the first. - featureComplete = false; - } - } - return featureComplete; -} - // Parse the given window.location and return parameters that can be used when calling // MatrixChat.showScreen(screen, params) function getScreenFromLocation(location) { @@ -163,7 +128,7 @@ function onTokenLoginCompleted() { window.location.href = formatted; } -export async function loadApp() { +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; @@ -191,26 +156,8 @@ export async function loadApp() { // Load language after loading config.json so that settingsDefaults.language can be applied await loadLanguage(); - const fragparts = parseQsFromFragment(window.location); const params = parseQs(window.location); - // don't try to redirect to the native apps if we're - // verifying a 3pid (but after we've loaded the config) - // or if the user is following a deep link - // (https://github.com/vector-im/riot-web/issues/7378) - const preventRedirect = fragparts.params.client_secret || fragparts.location.length > 0; - - if (!preventRedirect) { - const isIos = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; - const isAndroid = /Android/.test(navigator.userAgent); - if (isIos || isAndroid) { - if (document.cookie.indexOf("riot_mobile_redirect_to_guide=false") === -1) { - window.location = "mobile_guide/"; - return; - } - } - } - // as quickly as we possibly can, set a default theme... await setTheme(); @@ -237,17 +184,13 @@ export async function loadApp() { return ; } - const validBrowser = checkBrowserFeatures(); - - const acceptInvalidBrowser = window.localStorage && window.localStorage.getItem('mx_accepts_unsupported_browser'); - 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 (validBrowser || acceptInvalidBrowser) { + } else if (acceptBrowser) { platform.startUpdater(); try { @@ -260,7 +203,7 @@ export async function loadApp() { ConferenceHandler={VectorConferenceHandler} config={config} realQueryParams={params} - startingFragmentQueryParams={fragparts.params} + startingFragmentQueryParams={fragParams} enableGuest={!config.disable_guests} onTokenLoginCompleted={onTokenLoginCompleted} initialScreenAfterLogin={getScreenFromLocation(window.location)} diff --git a/src/vector/index.ts b/src/vector/index.ts index 74db1cd687..d844827a85 100644 --- a/src/vector/index.ts +++ b/src/vector/index.ts @@ -25,6 +25,7 @@ require('gfm.css/gfm.css'); require('highlight.js/styles/github.css'); // These are things that can run before the skin loads - be careful not to reference the react-sdk though. +import {parseQsFromFragment} from "./url_utils"; import './modernizr'; // load service worker if available on this platform @@ -40,6 +41,41 @@ async function settled(prom: Promise) { } } +function checkBrowserFeatures() { + if (!window.Modernizr) { + console.error("Cannot check features - Modernizr global is missing."); + return false; + } + + // custom checks atop Modernizr because it doesn't have ES2018/ES2019 checks in it for some features we depend on, + // Modernizr requires rules to be lowercase with no punctuation: + // ES2018: http://www.ecma-international.org/ecma-262/9.0/#sec-promise.prototype.finally + window.Modernizr.addTest("promiseprototypefinally", () => + window.Promise && window.Promise.prototype && typeof window.Promise.prototype.finally === "function"); + // ES2019: http://www.ecma-international.org/ecma-262/10.0/#sec-object.fromentries + window.Modernizr.addTest("objectfromentries", () => + window.Object && typeof window.Object.fromEntries === "function"); + + const featureList = Object.keys(window.Modernizr); + + let featureComplete = true; + for (let i = 0; i < featureList.length; i++) { + if (window.Modernizr[featureList[i]] === undefined) { + console.error( + "Looked for feature '%s' but Modernizr has no results for this. " + + "Has it been configured correctly?", featureList[i], + ); + return false; + } + if (window.Modernizr[featureList[i]] === false) { + console.error("Browser missing feature: '%s'", featureList[i]); + // toggle flag rather than return early so we log all missing features rather than just the first. + featureComplete = false; + } + } + return featureComplete; +} + // React depends on Map & Set which we check for using modernizr's es6collections // if modernizr fails we may not have a functional react to show the error message. // try in react but fallback to an `alert` @@ -52,11 +88,35 @@ async function start() { await settled(rageshakePromise); // give rageshake a chance to load/fail + const fragparts = parseQsFromFragment(window.location); + + // don't try to redirect to the native apps if we're + // verifying a 3pid (but after we've loaded the config) + // or if the user is following a deep link + // (https://github.com/vector-im/riot-web/issues/7378) + const preventRedirect = fragparts.params.client_secret || fragparts.location.length > 0; + + if (!preventRedirect) { + const isIos = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; + const isAndroid = /Android/.test(navigator.userAgent); + if (isIos || isAndroid) { + if (document.cookie.indexOf("riot_mobile_redirect_to_guide=false") === -1) { + window.location.href = "mobile_guide/"; + return; + } + } + } + await loadSkin(); + let acceptBrowser = checkBrowserFeatures(); + if (!acceptBrowser && window.localStorage) { + acceptBrowser = Boolean(window.localStorage.getItem("mx_accepts_unsupported_browser")); + } + // Finally, load the app. All of the other react-sdk imports are in this file which causes the skinner to // run on the components. We use `require` here to make sure webpack doesn't optimize this into an async // import and thus running before the skin can load. - await loadApp(); + await loadApp(fragparts.params, acceptBrowser); } start(); diff --git a/src/vector/init.ts b/src/vector/init.ts index 57941e88cb..8b8a62bb3c 100644 --- a/src/vector/init.ts +++ b/src/vector/init.ts @@ -138,11 +138,12 @@ export async function loadSkin() { console.log("Skin loaded!"); } -export async function loadApp() { +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(), document.getElementById('matrixchat')); + window.matrixChat = ReactDOM.render(await module.loadApp(fragParams, acceptBrowser), + document.getElementById('matrixchat')); } diff --git a/src/vector/url_utils.ts b/src/vector/url_utils.ts index d35de505ed..1e14fb5c86 100644 --- a/src/vector/url_utils.ts +++ b/src/vector/url_utils.ts @@ -32,7 +32,7 @@ export function parseQsFromFragment(location: Location) { const result = { location: decodeURIComponent(hashparts[0]), - params: {}, + params: {}, }; if (hashparts.length > 1) { diff --git a/webpack.config.js b/webpack.config.js index 9d8f333c87..6e84842d6d 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -31,7 +31,7 @@ module.exports = (env, argv) => { ...development, entry: { - "bundle": "./src/vector/index.js", + "bundle": "./src/vector/index.ts", "indexeddb-worker": "./src/vector/indexeddb-worker.js", "mobileguide": "./src/vector/mobile_guide/index.js", "jitsi": "./src/vector/jitsi/index.ts", diff --git a/yarn.lock b/yarn.lock index ad7be626ed..a46162cf56 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1245,6 +1245,11 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== +"@types/modernizr@^3.5.3": + version "3.5.3" + resolved "https://registry.yarnpkg.com/@types/modernizr/-/modernizr-3.5.3.tgz#8ef99e6252191c1d88647809109dc29884ba6d7a" + integrity sha512-jhMOZSS0UGYTS9pqvt6q3wtT3uvOSve5piTEmTMx3zzTuBLvSIMxSIBIc3d5lajVD5h4xc41AMZD2M5orN3PxA== + "@types/node@*": version "13.9.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.0.tgz#5b6ee7a77faacddd7de719017d0bc12f52f81589" From e788433c248b5c969b8014715d3cfb6642ad2600 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 6 Apr 2020 12:22:20 +0100 Subject: [PATCH 09/10] Combine two async imports into one by way of skin.ts Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/vector/init.ts | 15 ++++----------- src/vector/skin.ts | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 11 deletions(-) create mode 100644 src/vector/skin.ts diff --git a/src/vector/init.ts b/src/vector/init.ts index 8b8a62bb3c..c43f48e3db 100644 --- a/src/vector/init.ts +++ b/src/vector/init.ts @@ -123,17 +123,10 @@ export async function loadSkin() { // the SDK until we have to in imports. console.log("Loading skin..."); // load these async so that its code is not executed immediately and we can catch any exceptions - const [sdk, skin] = await Promise.all([ - import( - /* webpackChunkName: "matrix-react-sdk" */ - /* webpackPreload: true */ - "matrix-react-sdk"), - import( - /* webpackChunkName: "riot-web-component-index" */ - /* webpackPreload: true */ - // @ts-ignore - this module is generated so may fail lint - "../component-index"), - ]); + const {sdk, skin} = await import( + /* webpackChunkName: "skin" */ + /* webpackPreload: true */ + "./skin"); sdk.loadSkin(skin); console.log("Skin loaded!"); } diff --git a/src/vector/skin.ts b/src/vector/skin.ts new file mode 100644 index 0000000000..c0c527fdb7 --- /dev/null +++ b/src/vector/skin.ts @@ -0,0 +1,22 @@ +/* +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 sdk from "matrix-react-sdk"; +// @ts-ignore - this module is generated so may fail lint +import * as skin from "../component-index"; + +// we re-export here so that we can async-load this one file and webpack will bundle them together. +export {sdk, skin}; From 1b9112b876c050a9c6ef6f741470592134c6017f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 6 Apr 2020 12:24:56 +0100 Subject: [PATCH 10/10] Revert "Combine two async imports into one by way of skin.ts" This reverts commit e788433c Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/vector/init.ts | 15 +++++++++++---- src/vector/skin.ts | 22 ---------------------- 2 files changed, 11 insertions(+), 26 deletions(-) delete mode 100644 src/vector/skin.ts diff --git a/src/vector/init.ts b/src/vector/init.ts index c43f48e3db..8b8a62bb3c 100644 --- a/src/vector/init.ts +++ b/src/vector/init.ts @@ -123,10 +123,17 @@ export async function loadSkin() { // the SDK until we have to in imports. console.log("Loading skin..."); // load these async so that its code is not executed immediately and we can catch any exceptions - const {sdk, skin} = await import( - /* webpackChunkName: "skin" */ - /* webpackPreload: true */ - "./skin"); + const [sdk, skin] = await Promise.all([ + import( + /* webpackChunkName: "matrix-react-sdk" */ + /* webpackPreload: true */ + "matrix-react-sdk"), + import( + /* webpackChunkName: "riot-web-component-index" */ + /* webpackPreload: true */ + // @ts-ignore - this module is generated so may fail lint + "../component-index"), + ]); sdk.loadSkin(skin); console.log("Skin loaded!"); } diff --git a/src/vector/skin.ts b/src/vector/skin.ts deleted file mode 100644 index c0c527fdb7..0000000000 --- a/src/vector/skin.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* -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 sdk from "matrix-react-sdk"; -// @ts-ignore - this module is generated so may fail lint -import * as skin from "../component-index"; - -// we re-export here so that we can async-load this one file and webpack will bundle them together. -export {sdk, skin};