forked from matrix/element-web
Merge pull request #12869 from vector-im/t3chguy/app_load_tweaks
App load tweaks
This commit is contained in:
commit
1d109bb067
|
@ -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;
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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() } },
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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};
|
|
||||||
}
|
|
|
@ -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");
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue