Merge pull request #12869 from vector-im/t3chguy/app_load_tweaks

App load tweaks
This commit is contained in:
Michael Telatynski 2020-03-26 12:03:23 +00:00 committed by GitHub
commit 1d109bb067
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 157 additions and 135 deletions

22
src/@types/global.d.ts vendored Normal file
View File

@ -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.
*/
interface Window {
Olm: {
init: () => Promise<void>;
};
mxSendRageshake: (text: string, withLogs?: boolean) => void;
}

View File

@ -18,8 +18,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import olmWasmPath from 'olm/olm.wasm';
import React from 'react'; import React from 'react';
// add React and ReactPerf to the global namespace, to make them easier to // add React and ReactPerf to the global namespace, to make them easier to
// access via the console // access via the console
@ -29,7 +27,6 @@ import ReactDOM from 'react-dom';
import * as sdk from 'matrix-react-sdk'; import * as sdk from 'matrix-react-sdk';
import PlatformPeg from 'matrix-react-sdk/src/PlatformPeg'; import PlatformPeg from 'matrix-react-sdk/src/PlatformPeg';
import * as VectorConferenceHandler from 'matrix-react-sdk/src/VectorConferenceHandler'; import * as VectorConferenceHandler from 'matrix-react-sdk/src/VectorConferenceHandler';
import * as languageHandler from 'matrix-react-sdk/src/languageHandler';
import {_t, _td, newTranslatableError} from 'matrix-react-sdk/src/languageHandler'; import {_t, _td, newTranslatableError} from 'matrix-react-sdk/src/languageHandler';
import AutoDiscoveryUtils from 'matrix-react-sdk/src/utils/AutoDiscoveryUtils'; import AutoDiscoveryUtils from 'matrix-react-sdk/src/utils/AutoDiscoveryUtils';
import {AutoDiscovery} from "matrix-js-sdk/src/autodiscovery"; import {AutoDiscovery} from "matrix-js-sdk/src/autodiscovery";
@ -40,14 +37,11 @@ import url from 'url';
import {parseQs, parseQsFromFragment} from './url_utils'; import {parseQs, parseQsFromFragment} from './url_utils';
import {MatrixClientPeg} from 'matrix-react-sdk/src/MatrixClientPeg'; import {MatrixClientPeg} from 'matrix-react-sdk/src/MatrixClientPeg';
import SettingsStore from "matrix-react-sdk/src/settings/SettingsStore";
import SdkConfig from "matrix-react-sdk/src/SdkConfig"; import SdkConfig from "matrix-react-sdk/src/SdkConfig";
import {setTheme} from "matrix-react-sdk/src/theme"; import {setTheme} from "matrix-react-sdk/src/theme";
import Olm from 'olm';
import CallHandler from 'matrix-react-sdk/src/CallHandler'; import CallHandler from 'matrix-react-sdk/src/CallHandler';
import {loadConfig, preparePlatform} from "./initial-load"; import {loadConfig, preparePlatform, loadLanguage, loadOlm} from "./init";
let lastLocationHashSet = null; let lastLocationHashSet = null;
@ -193,7 +187,7 @@ export async function loadApp() {
const platform = PlatformPeg.get(); const platform = PlatformPeg.get();
// Load the config from the platform // Load the config from the platform
const configInfo = await loadConfig(); const configError = await loadConfig();
// Load language after loading config.json so that settingsDefaults.language can be applied // Load language after loading config.json so that settingsDefaults.language can be applied
await loadLanguage(); await loadLanguage();
@ -222,7 +216,7 @@ export async function loadApp() {
await setTheme(); await setTheme();
// Now that we've loaded the theme (CSS), display the config syntax error if needed. // Now that we've loaded the theme (CSS), display the config syntax error if needed.
if (configInfo.configSyntaxError) { if (configError && configError.err && configError.err instanceof SyntaxError) {
const errorMessage = ( const errorMessage = (
<div> <div>
<p> <p>
@ -234,7 +228,7 @@ export async function loadApp() {
<p> <p>
{_t( {_t(
"The message from the parser is: %(message)s", "The message from the parser is: %(message)s",
{message: configInfo.configError.err.message || _t("Invalid JSON")}, {message: configError.err.message || _t("Invalid JSON")},
)} )}
</p> </p>
</div> </div>
@ -254,7 +248,7 @@ export async function loadApp() {
const urlWithoutQuery = window.location.protocol + '//' + window.location.host + window.location.pathname; const urlWithoutQuery = window.location.protocol + '//' + window.location.host + window.location.pathname;
console.log("Vector starting at " + urlWithoutQuery); console.log("Vector starting at " + urlWithoutQuery);
if (configInfo.configError) { if (configError) {
window.matrixChat = ReactDOM.render(<div className="error"> window.matrixChat = ReactDOM.render(<div className="error">
Unable to load config file: please refresh the page to try again. Unable to load config file: please refresh the page to try again.
</div>, document.getElementById('matrixchat')); </div>, document.getElementById('matrixchat'));
@ -308,62 +302,6 @@ export async function loadApp() {
} }
} }
function loadOlm() {
/* Load Olm. We try the WebAssembly version first, and then the legacy,
* asm.js version if that fails. For this reason we need to wait for this
* to finish before continuing to load the rest of the app. In future
* we could somehow pass a promise down to react-sdk and have it wait on
* that so olm can be loading in parallel with the rest of the app.
*
* We also need to tell the Olm js to look for its wasm file at the same
* level as index.html. It really should be in the same place as the js,
* ie. in the bundle directory, but as far as I can tell this is
* completely impossible with webpack. We do, however, use a hashed
* filename to avoid caching issues.
*/
return Olm.init({
locateFile: () => olmWasmPath,
}).then(() => {
console.log("Using WebAssembly Olm");
}).catch((e) => {
console.log("Failed to load Olm: trying legacy version", e);
return new Promise((resolve, reject) => {
const s = document.createElement('script');
s.src = 'olm_legacy.js'; // XXX: This should be cache-busted too
s.onload = resolve;
s.onerror = reject;
document.body.appendChild(s);
}).then(() => {
// Init window.Olm, ie. the one just loaded by the script tag,
// not 'Olm' which is still the failed wasm version.
return window.Olm.init();
}).then(() => {
console.log("Using legacy Olm");
}).catch((e) => {
console.log("Both WebAssembly and asm.js Olm failed!", e);
});
});
}
async function loadLanguage() {
const prefLang = SettingsStore.getValue("language", null, /*excludeDefault=*/true);
let langs = [];
if (!prefLang) {
languageHandler.getLanguagesFromBrowser().forEach((l) => {
langs.push(...languageHandler.getNormalizedLanguageKeys(l));
});
} else {
langs = [prefLang];
}
try {
await languageHandler.setLanguage(langs);
document.documentElement.setAttribute("lang", languageHandler.getCurrentLanguage());
} catch (e) {
console.error("Unable to set language", e);
}
}
async function verifyServerConfig() { async function verifyServerConfig() {
let validatedConfig; let validatedConfig;
try { try {

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2018 New Vector Ltd Copyright 2018, 2020 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -18,22 +18,25 @@ import request from 'browser-request';
// Load the config file. First try to load up a domain-specific config of the // Load the config file. First try to load up a domain-specific config of the
// form "config.$domain.json" and if that fails, fall back to config.json. // form "config.$domain.json" and if that fails, fall back to config.json.
export async function getVectorConfig(relativeLocation) { export async function getVectorConfig(relativeLocation: string='') {
if (relativeLocation === undefined) relativeLocation = '';
if (relativeLocation !== '' && !relativeLocation.endsWith('/')) relativeLocation += '/'; if (relativeLocation !== '' && !relativeLocation.endsWith('/')) relativeLocation += '/';
const specificConfigPromise = getConfig(`${relativeLocation}config.${document.domain}.json`);
const generalConfigPromise = getConfig(relativeLocation + "config.json");
try { try {
const configJson = await getConfig(`${relativeLocation}config.${document.domain}.json`); const configJson = await specificConfigPromise;
// 404s succeed with an empty json config, so check that there are keys // 404s succeed with an empty json config, so check that there are keys
if (Object.keys(configJson).length === 0) { if (Object.keys(configJson).length === 0) {
throw new Error(); // throw to enter the catch throw new Error(); // throw to enter the catch
} }
return configJson; return configJson;
} catch (e) { } catch (e) {
return await getConfig(relativeLocation + "config.json"); return await generalConfigPromise;
} }
} }
function getConfig(configJsonFilename) { function getConfig(configJsonFilename: string): Promise<{}> {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
request( request(
{ method: "GET", url: configJsonFilename, qs: { cachebuster: Date.now() } }, { method: "GET", url: configJsonFilename, qs: { cachebuster: Date.now() } },

114
src/vector/init.ts Normal file
View File

@ -0,0 +1,114 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018, 2019, 2020 New Vector Ltd
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
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.
*/
// @ts-ignore
import olmWasmPath from "olm/olm.wasm";
import Olm from 'olm';
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 SdkConfig from "matrix-react-sdk/src/SdkConfig";
export function preparePlatform() {
if ((<any>window).ipcRenderer) {
console.log("Using Electron platform");
const plaf = new ElectronPlatform();
PlatformPeg.set(plaf);
} else {
console.log("Using Web platform");
PlatformPeg.set(new WebPlatform());
}
}
export async function loadConfig(): Promise<Error | void> {
const platform = PlatformPeg.get();
let configJson;
try {
configJson = await platform.getConfig();
} catch (e) {
return e;
} finally {
// XXX: We call this twice, once here and once in MatrixChat as a prop. We call it here to ensure
// granular settings are loaded correctly and to avoid duplicating the override logic for the theme.
//
// Note: this isn't called twice for some wrappers, like the Jitsi wrapper.
SdkConfig.put(configJson || {});
}
}
export function loadOlm(): Promise<void> {
/* Load Olm. We try the WebAssembly version first, and then the legacy,
* asm.js version if that fails. For this reason we need to wait for this
* to finish before continuing to load the rest of the app. In future
* we could somehow pass a promise down to react-sdk and have it wait on
* that so olm can be loading in parallel with the rest of the app.
*
* We also need to tell the Olm js to look for its wasm file at the same
* level as index.html. It really should be in the same place as the js,
* ie. in the bundle directory, but as far as I can tell this is
* completely impossible with webpack. We do, however, use a hashed
* filename to avoid caching issues.
*/
return Olm.init({
locateFile: () => olmWasmPath,
}).then(() => {
console.log("Using WebAssembly Olm");
}).catch((e) => {
console.log("Failed to load Olm: trying legacy version", e);
return new Promise((resolve, reject) => {
const s = document.createElement('script');
s.src = 'olm_legacy.js'; // XXX: This should be cache-busted too
s.onload = resolve;
s.onerror = reject;
document.body.appendChild(s);
}).then(() => {
// Init window.Olm, ie. the one just loaded by the script tag,
// not 'Olm' which is still the failed wasm version.
return window.Olm.init();
}).then(() => {
console.log("Using legacy Olm");
}).catch((e) => {
console.log("Both WebAssembly and asm.js Olm failed!", e);
});
});
}
export async function loadLanguage() {
const prefLang = SettingsStore.getValue("language", null, /*excludeDefault=*/true);
let langs = [];
if (!prefLang) {
languageHandler.getLanguagesFromBrowser().forEach((l) => {
langs.push(...languageHandler.getNormalizedLanguageKeys(l));
});
} else {
langs = [prefLang];
}
try {
await languageHandler.setLanguage(langs);
document.documentElement.setAttribute("lang", languageHandler.getCurrentLanguage());
} catch (e) {
console.error("Unable to set language", e);
}
}

View File

@ -1,58 +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 ElectronPlatform from './platform/ElectronPlatform';
import WebPlatform from './platform/WebPlatform';
import PlatformPeg from 'matrix-react-sdk/src/PlatformPeg';
import SdkConfig from "matrix-react-sdk/src/SdkConfig";
export function preparePlatform() {
if ((<any>window).ipcRenderer) {
console.log("Using Electron platform");
const plaf = new ElectronPlatform();
PlatformPeg.set(plaf);
} else {
console.log("Using Web platform");
PlatformPeg.set(new WebPlatform());
}
}
export async function loadConfig(): Promise<{configError?: Error, configSyntaxError: boolean}> {
const platform = PlatformPeg.get();
let configJson;
let configError;
let configSyntaxError = false;
try {
configJson = await platform.getConfig();
} catch (e) {
configError = e;
if (e && e.err && e.err instanceof SyntaxError) {
console.error("SyntaxError loading config:", e);
configSyntaxError = true;
configJson = {}; // to prevent errors between here and loading CSS for the error box
}
}
// XXX: We call this twice, once here and once in MatrixChat as a prop. We call it here to ensure
// granular settings are loaded correctly and to avoid duplicating the override logic for the theme.
//
// Note: this isn't called twice for some wrappers, like the Jitsi wrapper.
SdkConfig.put(configJson);
return {configError, configSyntaxError};
}

View File

@ -50,7 +50,7 @@ function initRageshake() {
initRageshake(); initRageshake();
global.mxSendRageshake = function(text, withLogs) { window.mxSendRageshake = function(text: string, withLogs?: boolean) {
if (withLogs === undefined) withLogs = true; if (withLogs === undefined) withLogs = true;
if (!text || !text.trim()) { if (!text || !text.trim()) {
console.error("Cannot send a rageshake without a message - please tell us what went wrong"); console.error("Cannot send a rageshake without a message - please tell us what went wrong");

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2018 New Vector Ltd Copyright 2018, 2020 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -20,7 +20,7 @@ import * as qs from 'querystring';
// so we're re-using query string like format // so we're re-using query string like format
// //
// returns {location, params} // returns {location, params}
export function parseQsFromFragment(location) { export function parseQsFromFragment(location: Location) {
// if we have a fragment, it will start with '#', which we need to drop. // if we have a fragment, it will start with '#', which we need to drop.
// (if we don't, this will return ''). // (if we don't, this will return '').
const fragment = location.hash.substring(1); const fragment = location.hash.substring(1);
@ -41,6 +41,6 @@ export function parseQsFromFragment(location) {
return result; return result;
} }
export function parseQs(location) { export function parseQs(location: Location) {
return qs.parse(location.search.substring(1)); return qs.parse(location.search.substring(1));
} }

View File

@ -56,6 +56,9 @@ module.exports = (env, argv) => {
enforce: true, enforce: true,
// Do not add `chunks: 'all'` here because you'll break the app entry point. // Do not add `chunks: 'all'` here because you'll break the app entry point.
}, },
default: {
reuseExistingChunk: true,
},
}, },
}, },