/* Copyright 2015, 2016 OpenMarket Ltd Copyright 2017 Vector Creations Ltd Copyright 2018, 2019 New Vector Ltd Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> Copyright 2020 The Matrix.org Foundation C.I.C. 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 React from 'react'; // add React and ReactPerf to the global namespace, to make them easier to // access via the console global.React = React; import * as sdk from 'matrix-react-sdk'; import PlatformPeg from 'matrix-react-sdk/src/PlatformPeg'; import * as VectorConferenceHandler from 'matrix-react-sdk/src/VectorConferenceHandler'; import {_t, _td, newTranslatableError} from 'matrix-react-sdk/src/languageHandler'; import AutoDiscoveryUtils from 'matrix-react-sdk/src/utils/AutoDiscoveryUtils'; import {AutoDiscovery} from "matrix-js-sdk/src/autodiscovery"; import * as Lifecycle from "matrix-react-sdk/src/Lifecycle"; import url from 'url'; import {parseQs, parseQsFromFragment} from './url_utils'; import {MatrixClientPeg} from 'matrix-react-sdk/src/MatrixClientPeg'; import SdkConfig from "matrix-react-sdk/src/SdkConfig"; import {setTheme} from "matrix-react-sdk/src/theme"; import CallHandler from 'matrix-react-sdk/src/CallHandler'; 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) { const fragparts = parseQsFromFragment(location); return { screen: fragparts.location.substring(1), params: fragparts.params, }; } // Here, we do some crude URL analysis to allow // deep-linking. function routeUrl(location) { if (!window.matrixChat) return; console.log("Routing URL ", location.href); const s = getScreenFromLocation(location); window.matrixChat.showScreen(s.screen, s.params); } function onHashChange(ev) { if (decodeURIComponent(window.location.hash) === lastLocationHashSet) { // we just set this: no need to route it! return; } routeUrl(window.location); } // This will be called whenever the SDK changes screens, // so a web page can update the URL bar appropriately. function onNewScreen(screen) { console.log("newscreen "+screen); const hash = '#/' + screen; lastLocationHashSet = hash; window.location.hash = hash; } // We use this to work out what URL the SDK should // pass through when registering to allow the user to // click back to the client having registered. // It's up to us to recognise if we're loaded with // this URL and tell MatrixClient to resume registration. // // If we're in electron, we should never pass through a file:// URL otherwise // the identity server will try to 302 the browser to it, which breaks horribly. // so in that instance, hardcode to use riot.im/app for now instead. function makeRegistrationUrl(params) { let url; if (window.location.protocol === "vector:") { url = 'https://riot.im/app/#/register'; } else { url = ( window.location.protocol + '//' + window.location.host + window.location.pathname + '#/register' ); } const keys = Object.keys(params); for (let i = 0; i < keys.length; ++i) { if (i === 0) { url += '?'; } else { url += '&'; } const k = keys[i]; url += k + '=' + encodeURIComponent(params[k]); } return url; } function onTokenLoginCompleted() { // if we did a token login, we're now left with the token, hs and is // url as query params in the url; a little nasty but let's redirect to // clear them. const parsedUrl = url.parse(window.location.href); parsedUrl.search = ""; const formatted = url.format(parsedUrl); console.log("Redirecting to " + formatted + " to drop loginToken " + "from queryparams"); window.location.href = formatted; } export async function loadApp() { // 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; if (!vectorIndexeddbWorkerScript) { // If this is missing, something has probably gone wrong with // the bundling. The js-sdk will just fall back to accessing // indexeddb directly with no worker script, but we want to // make sure the indexeddb script is present, so fail hard. throw new Error("Missing indexeddb worker script!"); } MatrixClientPeg.setIndexedDbWorkerScript(vectorIndexeddbWorkerScript); CallHandler.setConferenceHandler(VectorConferenceHandler); window.addEventListener('hashchange', onHashChange); await loadOlm(); // set the platform for react sdk preparePlatform(); const platform = PlatformPeg.get(); // Load the config from the platform const configError = await loadConfig(); // 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(); // 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")}, )}