diff --git a/.eslintrc.js b/.eslintrc.js index 07e3e54e0e..3e00c9ebaa 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,85 +1,90 @@ module.exports = { plugins: ["matrix-org"], - extends: [ - "plugin:matrix-org/babel", - "plugin:matrix-org/react", - ], + extends: ["plugin:matrix-org/babel", "plugin:matrix-org/react"], env: { browser: true, node: true, }, rules: { // Things we do that break the ideal style - "quotes": "off", + quotes: "off", }, settings: { react: { - version: 'detect', + version: "detect", }, }, - overrides: [{ - files: [ - "src/**/*.{ts,tsx}", - "test/**/*.{ts,tsx}", - "module_system/**/*.{ts,tsx}", - ], - extends: [ - "plugin:matrix-org/typescript", - "plugin:matrix-org/react", - ], - // NOTE: These rules are frozen and new rules should not be added here. - // New changes belong in https://github.com/matrix-org/eslint-plugin-matrix-org/ - rules: { - // Things we do that break the ideal style - "prefer-promise-reject-errors": "off", - "quotes": "off", + overrides: [ + { + files: ["src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}", "module_system/**/*.{ts,tsx}"], + extends: ["plugin:matrix-org/typescript", "plugin:matrix-org/react"], + // NOTE: These rules are frozen and new rules should not be added here. + // New changes belong in https://github.com/matrix-org/eslint-plugin-matrix-org/ + rules: { + // Things we do that break the ideal style + "prefer-promise-reject-errors": "off", + "quotes": "off", - // We disable this while we're transitioning - "@typescript-eslint/no-explicit-any": "off", - // We're okay with assertion errors when we ask for them - "@typescript-eslint/no-non-null-assertion": "off", + // We disable this while we're transitioning + "@typescript-eslint/no-explicit-any": "off", + // We're okay with assertion errors when we ask for them + "@typescript-eslint/no-non-null-assertion": "off", - // Ban matrix-js-sdk/src imports in favour of matrix-js-sdk/src/matrix imports to prevent unleashing hell. - "no-restricted-imports": ["error", { - "paths": [{ - "name": "matrix-js-sdk", - "message": "Please use matrix-js-sdk/src/matrix instead", - }, { - "name": "matrix-js-sdk/", - "message": "Please use matrix-js-sdk/src/matrix instead", - }, { - "name": "matrix-js-sdk/src", - "message": "Please use matrix-js-sdk/src/matrix instead", - }, { - "name": "matrix-js-sdk/src/", - "message": "Please use matrix-js-sdk/src/matrix instead", - }, { - "name": "matrix-js-sdk/src/index", - "message": "Please use matrix-js-sdk/src/matrix instead", - }, { - "name": "matrix-react-sdk", - "message": "Please use matrix-react-sdk/src/index instead", - }, { - "name": "matrix-react-sdk/", - "message": "Please use matrix-react-sdk/src/index instead", - }], - "patterns": [{ - "group": ["matrix-js-sdk/lib", "matrix-js-sdk/lib/", "matrix-js-sdk/lib/**"], - "message": "Please use matrix-js-sdk/src/* instead", - }, { - "group": ["matrix-react-sdk/lib", "matrix-react-sdk/lib/", "matrix-react-sdk/lib/**"], - "message": "Please use matrix-react-sdk/src/* instead", - }], - }], + // Ban matrix-js-sdk/src imports in favour of matrix-js-sdk/src/matrix imports to prevent unleashing hell. + "no-restricted-imports": [ + "error", + { + paths: [ + { + name: "matrix-js-sdk", + message: "Please use matrix-js-sdk/src/matrix instead", + }, + { + name: "matrix-js-sdk/", + message: "Please use matrix-js-sdk/src/matrix instead", + }, + { + name: "matrix-js-sdk/src", + message: "Please use matrix-js-sdk/src/matrix instead", + }, + { + name: "matrix-js-sdk/src/", + message: "Please use matrix-js-sdk/src/matrix instead", + }, + { + name: "matrix-js-sdk/src/index", + message: "Please use matrix-js-sdk/src/matrix instead", + }, + { + name: "matrix-react-sdk", + message: "Please use matrix-react-sdk/src/index instead", + }, + { + name: "matrix-react-sdk/", + message: "Please use matrix-react-sdk/src/index instead", + }, + ], + patterns: [ + { + group: ["matrix-js-sdk/lib", "matrix-js-sdk/lib/", "matrix-js-sdk/lib/**"], + message: "Please use matrix-js-sdk/src/* instead", + }, + { + group: ["matrix-react-sdk/lib", "matrix-react-sdk/lib/", "matrix-react-sdk/lib/**"], + message: "Please use matrix-react-sdk/src/* instead", + }, + ], + }, + ], + }, }, - }, { - files: [ - "test/**/*.{ts,tsx}", - ], - rules: { - // We don't need super strict typing in test utilities - "@typescript-eslint/explicit-function-return-type": "off", - "@typescript-eslint/explicit-member-accessibility": "off", + { + files: ["test/**/*.{ts,tsx}"], + rules: { + // We don't need super strict typing in test utilities + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/explicit-member-accessibility": "off", + }, }, - }], + ], }; diff --git a/.github/ISSUE_TEMPLATE/bug-desktop.yml b/.github/ISSUE_TEMPLATE/bug-desktop.yml index be3d623d88..529c0a0ebc 100644 --- a/.github/ISSUE_TEMPLATE/bug-desktop.yml +++ b/.github/ISSUE_TEMPLATE/bug-desktop.yml @@ -2,75 +2,75 @@ name: Bug report for the Element desktop app (not in a browser) description: File a bug report if you are using the desktop Element application. labels: [T-Defect] body: - - type: markdown - attributes: - value: | - Thanks for taking the time to fill out this bug report! + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! - Please report security issues by email to security@matrix.org - - type: textarea - id: reproduction-steps - attributes: - label: Steps to reproduce - description: Please attach screenshots, videos or logs if you can. - placeholder: Tell us what you see! - value: | - 1. Where are you starting? What can you see? - 2. What do you click? - 3. More steps… - validations: - required: true - - type: textarea - id: result - attributes: - label: Outcome - placeholder: Tell us what went wrong - value: | - #### What did you expect? + Please report security issues by email to security@matrix.org + - type: textarea + id: reproduction-steps + attributes: + label: Steps to reproduce + description: Please attach screenshots, videos or logs if you can. + placeholder: Tell us what you see! + value: | + 1. Where are you starting? What can you see? + 2. What do you click? + 3. More steps… + validations: + required: true + - type: textarea + id: result + attributes: + label: Outcome + placeholder: Tell us what went wrong + value: | + #### What did you expect? - #### What happened instead? - validations: - required: true - - type: input - id: os - attributes: - label: Operating system - placeholder: Windows, macOS, Ubuntu, Arch Linux… - validations: - required: false - - type: input - id: version - attributes: - label: Application version - description: You can find the version information in Settings -> Help & About. - placeholder: e.g. Element version 1.7.34, olm version 3.2.3 - validations: - required: false - - type: input - id: source - attributes: - label: How did you install the app? - description: Where did you install the app from? Please give a link or a description. - placeholder: e.g. From https://element.io/get-started - validations: - required: false - - type: input - id: homeserver - attributes: - label: Homeserver - description: | - Which server is your account registered on? If it is a local or non-public homeserver, please tell us what is the homeserver implementation (ex: Synapse/Dendrite/etc.) and the version. - placeholder: e.g. matrix.org or Synapse 1.50.0rc1 - validations: - required: false - - type: dropdown - id: rageshake - attributes: - label: Will you send logs? - description: | - Did you know that you can send a /rageshake command from your application to submit logs for this issue? Trigger the defect, then type `/rageshake` into the message input area followed by a description of the problem and send the command. You will be able to add a link to this defect report and submit anonymous logs to the developers. - options: - - 'Yes' - - 'No' - validations: - required: true + #### What happened instead? + validations: + required: true + - type: input + id: os + attributes: + label: Operating system + placeholder: Windows, macOS, Ubuntu, Arch Linux… + validations: + required: false + - type: input + id: version + attributes: + label: Application version + description: You can find the version information in Settings -> Help & About. + placeholder: e.g. Element version 1.7.34, olm version 3.2.3 + validations: + required: false + - type: input + id: source + attributes: + label: How did you install the app? + description: Where did you install the app from? Please give a link or a description. + placeholder: e.g. From https://element.io/get-started + validations: + required: false + - type: input + id: homeserver + attributes: + label: Homeserver + description: | + Which server is your account registered on? If it is a local or non-public homeserver, please tell us what is the homeserver implementation (ex: Synapse/Dendrite/etc.) and the version. + placeholder: e.g. matrix.org or Synapse 1.50.0rc1 + validations: + required: false + - type: dropdown + id: rageshake + attributes: + label: Will you send logs? + description: | + Did you know that you can send a /rageshake command from your application to submit logs for this issue? Trigger the defect, then type `/rageshake` into the message input area followed by a description of the problem and send the command. You will be able to add a link to this defect report and submit anonymous logs to the developers. + options: + - "Yes" + - "No" + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/bug-web.yml b/.github/ISSUE_TEMPLATE/bug-web.yml index 99691f8a7a..24ab78a153 100644 --- a/.github/ISSUE_TEMPLATE/bug-web.yml +++ b/.github/ISSUE_TEMPLATE/bug-web.yml @@ -2,83 +2,83 @@ name: Bug report for Element Web (in browser) description: File a bug report if you are using Element in a web browser like Firefox, Chrome, Edge, and so on. labels: [T-Defect] body: - - type: markdown - attributes: - value: | - Thanks for taking the time to fill out this bug report! + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! - Please report security issues by email to security@matrix.org - - type: textarea - id: reproduction-steps - attributes: - label: Steps to reproduce - description: Please attach screenshots, videos or logs if you can. - placeholder: Tell us what you see! - value: | - 1. Where are you starting? What can you see? - 2. What do you click? - 3. More steps… - validations: - required: true - - type: textarea - id: result - attributes: - label: Outcome - placeholder: Tell us what went wrong - value: | - #### What did you expect? + Please report security issues by email to security@matrix.org + - type: textarea + id: reproduction-steps + attributes: + label: Steps to reproduce + description: Please attach screenshots, videos or logs if you can. + placeholder: Tell us what you see! + value: | + 1. Where are you starting? What can you see? + 2. What do you click? + 3. More steps… + validations: + required: true + - type: textarea + id: result + attributes: + label: Outcome + placeholder: Tell us what went wrong + value: | + #### What did you expect? - #### What happened instead? - validations: - required: true - - type: input - id: os - attributes: - label: Operating system - placeholder: Windows, macOS, Ubuntu, Arch Linux… - validations: - required: false - - type: input - id: browser - attributes: - label: Browser information - description: Which browser are you using? Which version? - placeholder: e.g. Chromium Version 92.0.4515.131 - validations: - required: false - - type: input - id: webapp-url - attributes: - label: URL for webapp - description: Which URL are you using to access the webapp? If a private server, tell us what version of Element Web you are using. - placeholder: e.g. develop.element.io, app.element.io - validations: - required: false - - type: input - id: version - attributes: - label: Application version - description: You can find the version information in Settings -> Help & About. - placeholder: e.g. Element version 1.7.34, olm version 3.2.3 - validations: - required: false - - type: input - id: homeserver - attributes: - label: Homeserver - description: | - Which server is your account registered on? If it is a local or non-public homeserver, please tell us what is the homeserver implementation (ex: Synapse/Dendrite/etc.) and the version. - placeholder: e.g. matrix.org or Synapse 1.50.0rc1 - validations: - required: false - - type: dropdown - id: rageshake - attributes: - label: Will you send logs? - description: | - Did you know that you can send a /rageshake command from the web application to submit logs for this issue? Trigger the defect, then type `/rageshake` into the message input area followed by a description of the problem and send the command. You will be able to add a link to this defect report and submit anonymous logs to the developers. - options: - - 'Yes' - - 'No' - validations: - required: true + #### What happened instead? + validations: + required: true + - type: input + id: os + attributes: + label: Operating system + placeholder: Windows, macOS, Ubuntu, Arch Linux… + validations: + required: false + - type: input + id: browser + attributes: + label: Browser information + description: Which browser are you using? Which version? + placeholder: e.g. Chromium Version 92.0.4515.131 + validations: + required: false + - type: input + id: webapp-url + attributes: + label: URL for webapp + description: Which URL are you using to access the webapp? If a private server, tell us what version of Element Web you are using. + placeholder: e.g. develop.element.io, app.element.io + validations: + required: false + - type: input + id: version + attributes: + label: Application version + description: You can find the version information in Settings -> Help & About. + placeholder: e.g. Element version 1.7.34, olm version 3.2.3 + validations: + required: false + - type: input + id: homeserver + attributes: + label: Homeserver + description: | + Which server is your account registered on? If it is a local or non-public homeserver, please tell us what is the homeserver implementation (ex: Synapse/Dendrite/etc.) and the version. + placeholder: e.g. matrix.org or Synapse 1.50.0rc1 + validations: + required: false + - type: dropdown + id: rageshake + attributes: + label: Will you send logs? + description: | + Did you know that you can send a /rageshake command from the web application to submit logs for this issue? Trigger the defect, then type `/rageshake` into the message input area followed by a description of the problem and send the command. You will be able to add a link to this defect report and submit anonymous logs to the developers. + options: + - "Yes" + - "No" + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index cbda0e6635..b34e449368 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ blank_issues_enabled: false contact_links: - - name: Questions & support - url: https://matrix.to/#/#element-web:matrix.org - about: Please ask and answer questions here. + - name: Questions & support + url: https://matrix.to/#/#element-web:matrix.org + about: Please ask and answer questions here. diff --git a/.github/ISSUE_TEMPLATE/enhancement.yml b/.github/ISSUE_TEMPLATE/enhancement.yml index 02ed7ddbf2..e6784b1a90 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.yml +++ b/.github/ISSUE_TEMPLATE/enhancement.yml @@ -2,35 +2,35 @@ name: Enhancement request description: Do you have a suggestion or feature request? labels: [T-Enhancement] body: - - type: markdown - attributes: - value: | - Thank you for taking the time to propose an enhancement to an existing feature. If you would like to propose a new feature or a major cross-platform change, please [start a discussion here](https://github.com/vector-im/element-meta/discussions/new?category=ideas). - - type: textarea - id: usecase - attributes: - label: Your use case - description: What would you like to be able to do? Please feel welcome to include screenshots or mock ups. - placeholder: Tell us what you would like to do! - value: | - #### What would you like to do? + - type: markdown + attributes: + value: | + Thank you for taking the time to propose an enhancement to an existing feature. If you would like to propose a new feature or a major cross-platform change, please [start a discussion here](https://github.com/vector-im/element-meta/discussions/new?category=ideas). + - type: textarea + id: usecase + attributes: + label: Your use case + description: What would you like to be able to do? Please feel welcome to include screenshots or mock ups. + placeholder: Tell us what you would like to do! + value: | + #### What would you like to do? - #### Why would you like to do it? + #### Why would you like to do it? - #### How would you like to achieve it? - validations: - required: true - - type: textarea - id: alternative - attributes: - label: Have you considered any alternatives? - placeholder: A clear and concise description of any alternative solutions or features you've considered. - validations: - required: false - - type: textarea - id: additional-context - attributes: - label: Additional context - placeholder: Is there anything else you'd like to add? - validations: - required: false + #### How would you like to achieve it? + validations: + required: true + - type: textarea + id: alternative + attributes: + label: Have you considered any alternatives? + placeholder: A clear and concise description of any alternative solutions or features you've considered. + validations: + required: false + - type: textarea + id: additional-context + attributes: + label: Additional context + placeholder: Is there anything else you'd like to add? + validations: + required: false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 4bace80f3f..c343afc442 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,9 +2,9 @@ ## Checklist -* [ ] Tests written for new code (and old code if feasible) -* [ ] Linter and other CI checks pass -* [ ] Sign-off given on the changes (see [CONTRIBUTING.md](https://github.com/vector-im/element-web/blob/develop/CONTRIBUTING.md)) +- [ ] Tests written for new code (and old code if feasible) +- [ ] Linter and other CI checks pass +- [ ] Sign-off given on the changes (see [CONTRIBUTING.md](https://github.com/vector-im/element-web/blob/develop/CONTRIBUTING.md)) diff --git a/scripts/analyse_unused_exports.js b/scripts/analyse_unused_exports.js index 36014b1dc8..bf21a7ea61 100755 --- a/scripts/analyse_unused_exports.js +++ b/scripts/analyse_unused_exports.js @@ -1,5 +1,5 @@ #!/usr/bin/env node -'use strict'; +"use strict"; const fs = require("fs"); const { exec } = require("node:child_process"); diff --git a/scripts/build-jitsi.js b/scripts/build-jitsi.js index e5ce21468d..f7ea841b01 100644 --- a/scripts/build-jitsi.js +++ b/scripts/build-jitsi.js @@ -21,11 +21,13 @@ if (process.env.HTTPS_PROXY) { options.agent = new ProxyAgent(process.env.HTTPS_PROXY, { tunnel: true }); } -fetch("https://meet.element.io/libs/external_api.min.js", options).then(res => { - const stream = fs.createWriteStream(fname); - return new Promise((resolve, reject) => { - res.body.pipe(stream); - res.body.on('error', err => reject(err)); - res.body.on('finish', () => resolve()); - }); -}).then(() => console.log('Done with Jitsi download')); +fetch("https://meet.element.io/libs/external_api.min.js", options) + .then((res) => { + const stream = fs.createWriteStream(fname); + return new Promise((resolve, reject) => { + res.body.pipe(stream); + res.body.on("error", (err) => reject(err)); + res.body.on("finish", () => resolve()); + }); + }) + .then(() => console.log("Done with Jitsi download")); diff --git a/scripts/copy-res.js b/scripts/copy-res.js index 07826b3e83..5969048e6d 100755 --- a/scripts/copy-res.js +++ b/scripts/copy-res.js @@ -11,52 +11,52 @@ const loaderUtils = require("loader-utils"); // This could readily be automated, but it's nice to explicitly // control when new languages are available. const INCLUDE_LANGS = [ - {'value': 'bg', 'label': 'Български'}, - {'value': 'ca', 'label': 'Català'}, - {'value': 'cs', 'label': 'čeština'}, - {'value': 'da', 'label': 'Dansk'}, - {'value': 'de_DE', 'label': 'Deutsch'}, - {'value': 'el', 'label': 'Ελληνικά'}, - {'value': 'en_EN', 'label': 'English'}, - {'value': 'en_US', 'label': 'English (US)'}, - {'value': 'eo', 'label': 'Esperanto'}, - {'value': 'es', 'label': 'Español'}, - {'value': 'et', 'label': 'Eesti'}, - {'value': 'eu', 'label': 'Euskara'}, - {'value': 'fi', 'label': 'Suomi'}, - {'value': 'fr', 'label': 'Français'}, - {'value': 'gl', 'label': 'Galego'}, - {'value': 'he', 'label': 'עברית'}, - {'value': 'hi', 'label': 'हिन्दी'}, - {'value': 'hu', 'label': 'Magyar'}, - {'value': 'id', 'label': 'Bahasa Indonesia'}, - {'value': 'is', 'label': 'íslenska'}, - {'value': 'it', 'label': 'Italiano'}, - {'value': 'ja', 'label': '日本語'}, - {'value': 'kab', 'label': 'Taqbaylit'}, - {'value': 'ko', 'label': '한국어'}, - {'value': 'lo', 'label': 'ລາວ'}, - {'value': 'lt', 'label': 'Lietuvių'}, - {'value': 'lv', 'label': 'Latviešu'}, - {'value': 'nb_NO', 'label': 'Norwegian Bokmål'}, - {'value': 'nl', 'label': 'Nederlands'}, - {'value': 'nn', 'label': 'Norsk Nynorsk'}, - {'value': 'pl', 'label': 'Polski'}, - {'value': 'pt', 'label': 'Português'}, - {'value': 'pt_BR', 'label': 'Português do Brasil'}, - {'value': 'ru', 'label': 'Русский'}, - {'value': 'sk', 'label': 'Slovenčina'}, - {'value': 'sq', 'label': 'Shqip'}, - {'value': 'sr', 'label': 'српски'}, - {'value': 'sv', 'label': 'Svenska'}, - {'value': 'te', 'label': 'తెలుగు'}, - {'value': 'th', 'label': 'ไทย'}, - {'value': 'tr', 'label': 'Türkçe'}, - {'value': 'uk', 'label': 'українська мова'}, - {'value': 'vi', 'label': 'Tiếng Việt'}, - {'value': 'vls', 'label': 'West-Vlaams'}, - {'value': 'zh_Hans', 'label': '简体中文'}, // simplified chinese - {'value': 'zh_Hant', 'label': '繁體中文'}, // traditional chinese + { value: "bg", label: "Български" }, + { value: "ca", label: "Català" }, + { value: "cs", label: "čeština" }, + { value: "da", label: "Dansk" }, + { value: "de_DE", label: "Deutsch" }, + { value: "el", label: "Ελληνικά" }, + { value: "en_EN", label: "English" }, + { value: "en_US", label: "English (US)" }, + { value: "eo", label: "Esperanto" }, + { value: "es", label: "Español" }, + { value: "et", label: "Eesti" }, + { value: "eu", label: "Euskara" }, + { value: "fi", label: "Suomi" }, + { value: "fr", label: "Français" }, + { value: "gl", label: "Galego" }, + { value: "he", label: "עברית" }, + { value: "hi", label: "हिन्दी" }, + { value: "hu", label: "Magyar" }, + { value: "id", label: "Bahasa Indonesia" }, + { value: "is", label: "íslenska" }, + { value: "it", label: "Italiano" }, + { value: "ja", label: "日本語" }, + { value: "kab", label: "Taqbaylit" }, + { value: "ko", label: "한국어" }, + { value: "lo", label: "ລາວ" }, + { value: "lt", label: "Lietuvių" }, + { value: "lv", label: "Latviešu" }, + { value: "nb_NO", label: "Norwegian Bokmål" }, + { value: "nl", label: "Nederlands" }, + { value: "nn", label: "Norsk Nynorsk" }, + { value: "pl", label: "Polski" }, + { value: "pt", label: "Português" }, + { value: "pt_BR", label: "Português do Brasil" }, + { value: "ru", label: "Русский" }, + { value: "sk", label: "Slovenčina" }, + { value: "sq", label: "Shqip" }, + { value: "sr", label: "српски" }, + { value: "sv", label: "Svenska" }, + { value: "te", label: "తెలుగు" }, + { value: "th", label: "ไทย" }, + { value: "tr", label: "Türkçe" }, + { value: "uk", label: "українська мова" }, + { value: "vi", label: "Tiếng Việt" }, + { value: "vls", label: "West-Vlaams" }, + { value: "zh_Hans", label: "简体中文" }, // simplified chinese + { value: "zh_Hant", label: "繁體中文" }, // traditional chinese ]; // cpx includes globbed parts of the filename in the destination, but excludes @@ -77,15 +77,13 @@ const COPY_LIST = [ ["contribute.json", "webapp"], ]; -const parseArgs = require('minimist'); -const Cpx = require('cpx'); -const chokidar = require('chokidar'); -const fs = require('fs'); -const rimraf = require('rimraf'); +const parseArgs = require("minimist"); +const Cpx = require("cpx"); +const chokidar = require("chokidar"); +const fs = require("fs"); +const rimraf = require("rimraf"); -const argv = parseArgs( - process.argv.slice(2), {} -); +const argv = parseArgs(process.argv.slice(2), {}); const watch = argv.w; const verbose = argv.v; @@ -98,12 +96,12 @@ function errCheck(err) { } // Check if webapp exists -if (!fs.existsSync('webapp')) { - fs.mkdirSync('webapp'); +if (!fs.existsSync("webapp")) { + fs.mkdirSync("webapp"); } // Check if i18n exists -if (!fs.existsSync('webapp/i18n/')) { - fs.mkdirSync('webapp/i18n/'); +if (!fs.existsSync("webapp/i18n/")) { + fs.mkdirSync("webapp/i18n/"); } function next(i, err) { @@ -132,7 +130,9 @@ function next(i, err) { }); } - const cb = (err) => { next(i + 1, err) }; + const cb = (err) => { + next(i + 1, err); + }; if (watch) { if (opts.directwatch) { @@ -140,14 +140,12 @@ function next(i, err) { // which in the case of config.json is '.', which inevitably takes // ages to crawl. So we create our own watcher on the files // instead. - const copy = () => { cpx.copy(errCheck) }; - chokidar.watch(source) - .on('add', copy) - .on('change', copy) - .on('ready', cb) - .on('error', errCheck); + const copy = () => { + cpx.copy(errCheck); + }; + chokidar.watch(source).on("add", copy).on("change", copy).on("ready", cb).on("error", errCheck); } else { - cpx.on('watch-ready', cb); + cpx.on("watch-ready", cb); cpx.on("watch-error", cb); cpx.watch(); } @@ -157,17 +155,14 @@ function next(i, err) { } function genLangFile(lang, dest) { - const reactSdkFile = 'node_modules/matrix-react-sdk/src/i18n/strings/' + lang + '.json'; - const riotWebFile = 'src/i18n/strings/' + lang + '.json'; + const reactSdkFile = "node_modules/matrix-react-sdk/src/i18n/strings/" + lang + ".json"; + const riotWebFile = "src/i18n/strings/" + lang + ".json"; let translations = {}; - [reactSdkFile, riotWebFile].forEach(function(f) { + [reactSdkFile, riotWebFile].forEach(function (f) { if (fs.existsSync(f)) { try { - Object.assign( - translations, - JSON.parse(fs.readFileSync(f).toString()) - ); + Object.assign(translations, JSON.parse(fs.readFileSync(f).toString())); } catch (e) { console.error("Failed: " + f, e); throw e; @@ -192,16 +187,16 @@ function genLangFile(lang, dest) { function genLangList(langFileMap) { const languages = {}; - INCLUDE_LANGS.forEach(function(lang) { + INCLUDE_LANGS.forEach(function (lang) { const normalizedLanguage = lang.value.toLowerCase().replace("_", "-"); - const languageParts = normalizedLanguage.split('-'); + const languageParts = normalizedLanguage.split("-"); if (languageParts.length == 2 && languageParts[0] == languageParts[1]) { - languages[languageParts[0]] = {'fileName': langFileMap[lang.value], 'label': lang.label}; + languages[languageParts[0]] = { fileName: langFileMap[lang.value], label: lang.label }; } else { - languages[normalizedLanguage] = {'fileName': langFileMap[lang.value], 'label': lang.label}; + languages[normalizedLanguage] = { fileName: langFileMap[lang.value], label: lang.label }; } }); - fs.writeFile('webapp/i18n/languages.json', JSON.stringify(languages, null, 4), function(err) { + fs.writeFile("webapp/i18n/languages.json", JSON.stringify(languages, null, 4), function (err) { if (err) { console.error("Copy Error occured: " + err); throw new Error("Failed to generate languages.json"); @@ -230,7 +225,7 @@ function weblateToCounterpart(inTrs) { const outTrs = {}; for (const key of Object.keys(inTrs)) { - const keyParts = key.split('|', 2); + const keyParts = key.split("|", 2); if (keyParts.length === 2) { let obj = outTrs[keyParts[0]]; if (obj === undefined) { @@ -239,7 +234,7 @@ function weblateToCounterpart(inTrs) { // This is a transitional edge case if a string went from singular to pluralised and both still remain // in the translation json file. Use the singular translation as `other` and merge pluralisation atop. obj = outTrs[keyParts[0]] = { - "other": inTrs[key], + other: inTrs[key], }; console.warn("Found entry in i18n file in both singular and pluralised form", keyParts[0]); } @@ -258,8 +253,8 @@ regenerate the file, adding its content-hashed filename to langFileMap and regenerating languages.json with the new filename */ function watchLanguage(lang, dest, langFileMap) { - const reactSdkFile = 'node_modules/matrix-react-sdk/src/i18n/strings/' + lang + '.json'; - const riotWebFile = 'src/i18n/strings/' + lang + '.json'; + const reactSdkFile = "node_modules/matrix-react-sdk/src/i18n/strings/" + lang + ".json"; + const riotWebFile = "src/i18n/strings/" + lang + ".json"; // XXX: Use a debounce because for some reason if we read the language // file immediately after the FS event is received, the file contents @@ -271,16 +266,13 @@ function watchLanguage(lang, dest, langFileMap) { } makeLangDebouncer = setTimeout(() => { const filename = genLangFile(lang, dest); - langFileMap[lang]=filename; + langFileMap[lang] = filename; genLangList(langFileMap); }, 500); }; - [reactSdkFile, riotWebFile].forEach(function(f) { - chokidar.watch(f) - .on('add', makeLang) - .on('change', makeLang) - .on('error', errCheck); + [reactSdkFile, riotWebFile].forEach(function (f) { + chokidar.watch(f).on("add", makeLang).on("change", makeLang).on("error", errCheck); }); } @@ -294,7 +286,7 @@ const I18N_FILENAME_MAP = INCLUDE_LANGS.reduce((m, l) => { genLangList(I18N_FILENAME_MAP); if (watch) { - INCLUDE_LANGS.forEach(l => watchLanguage(l.value, I18N_DEST, I18N_FILENAME_MAP)); + INCLUDE_LANGS.forEach((l) => watchLanguage(l.value, I18N_DEST, I18N_FILENAME_MAP)); } // non-language resources diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 74a65af14f..5047aeeed5 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -19,20 +19,20 @@ import type { Renderer } from "react-dom"; import type { logger } from "matrix-js-sdk/src/logger"; type ElectronChannel = - "app_onAction" | - "before-quit" | - "check_updates" | - "install_update" | - "ipcCall" | - "ipcReply" | - "loudNotification" | - "preferences" | - "seshat" | - "seshatReply" | - "setBadgeCount" | - "update-downloaded" | - "userDownloadCompleted" | - "userDownloadAction"; + | "app_onAction" + | "before-quit" + | "check_updates" + | "install_update" + | "ipcCall" + | "ipcReply" + | "loudNotification" + | "preferences" + | "seshat" + | "seshatReply" + | "setBadgeCount" + | "update-downloaded" + | "userDownloadCompleted" + | "userDownloadAction"; declare global { interface Window { diff --git a/src/@types/raw-loader.d.ts b/src/@types/raw-loader.d.ts index ea3df880f6..c35dd356a8 100644 --- a/src/@types/raw-loader.d.ts +++ b/src/@types/raw-loader.d.ts @@ -1,4 +1,4 @@ -declare module '!!raw-loader!*' { +declare module "!!raw-loader!*" { const contents: string; export default contents; } diff --git a/src/async-components/structures/CompatibilityView.tsx b/src/async-components/structures/CompatibilityView.tsx index be96a083c3..193d3e85b0 100644 --- a/src/async-components/structures/CompatibilityView.tsx +++ b/src/async-components/structures/CompatibilityView.tsx @@ -16,7 +16,7 @@ limitations under the License. import * as React from "react"; import { _t } from "matrix-react-sdk/src/languageHandler"; -import SdkConfig from 'matrix-react-sdk/src/SdkConfig'; +import SdkConfig from "matrix-react-sdk/src/SdkConfig"; // directly import the style here as this layer does not support rethemedex at this time so no matrix-react-sdk // PostCSS variables will be accessible. @@ -32,115 +32,131 @@ const CompatibilityView: React.FC = ({ onAccept }) => { let ios = null; const iosCustomUrl = mobileBuilds?.ios; - if (iosCustomUrl !== null) { // could be undefined or a string - ios = <> -

iOS (iPhone or iPad)

- - Apple App Store - - ; + if (iosCustomUrl !== null) { + // could be undefined or a string + ios = ( + <> +

+ iOS (iPhone or iPad) +

+ + Apple App Store + + + ); } - let android = [

Android

]; + let android = [ +

+ Android +

, + ]; const andCustomUrl = mobileBuilds?.android; const fdroidCustomUrl = mobileBuilds?.fdroid; - if (andCustomUrl !== null) { // undefined or string - android.push( - Google Play Store - ); + if (andCustomUrl !== null) { + // undefined or string + android.push( + + Google Play Store + , + ); } - if (fdroidCustomUrl !== null) { // undefined or string - android.push( - F-Droid - ); + if (fdroidCustomUrl !== null) { + // undefined or string + android.push( + + F-Droid + , + ); } - if (android.length === 1) { // just a header, meaning no links + if (android.length === 1) { + // just a header, meaning no links android = []; } - let mobileHeader =

{ _t("Use %(brand)s on mobile", { brand }) }

; + let mobileHeader =

{_t("Use %(brand)s on mobile", { brand })}

; if (!android.length && !ios) { mobileHeader = null; } - return
-
-
- - Element - -

{ _t("Unsupported browser") }

-
+ return ( +
+
+
+ + Element + +

{_t("Unsupported browser")}

+
-
-
-
-

{ _t("Your browser can't run %(brand)s", { brand }) }

-

- { _t( - "%(brand)s uses advanced browser features which aren't " + - "supported by your current browser.", - { brand }, - ) } -

-

- { _t( - 'Please install Chrome, Firefox, ' + - 'or Safari for the best experience.', - {}, - { - 'chromeLink': (sub) => { sub }, - 'firefoxLink': (sub) => { sub }, - 'safariLink': (sub) => { sub }, - }, - ) } -

-

- { _t( - "You can continue using your current browser, but some or all features may not work " + - "and the look and feel of the application may be incorrect.", - ) } -

- +
+
+
+

{_t("Your browser can't run %(brand)s", { brand })}

+

+ {_t( + "%(brand)s uses advanced browser features which aren't " + + "supported by your current browser.", + { brand }, + )} +

+

+ {_t( + "Please install Chrome, Firefox, " + + "or Safari for the best experience.", + {}, + { + chromeLink: (sub) => {sub}, + firefoxLink: (sub) => {sub}, + safariLink: (sub) => {sub}, + }, + )} +

+

+ {_t( + "You can continue using your current browser, but some or all features may not work " + + "and the look and feel of the application may be incorrect.", + )} +

+ +
-
-
-
-
- { mobileHeader } - { ios } - { android } +
+
+
+ {mobileHeader} + {ios} + {android} +
-
-
-
; + ); }; export default CompatibilityView; diff --git a/src/async-components/structures/ErrorView.tsx b/src/async-components/structures/ErrorView.tsx index 3c3bfc3c3e..b34226f3b4 100644 --- a/src/async-components/structures/ErrorView.tsx +++ b/src/async-components/structures/ErrorView.tsx @@ -28,34 +28,33 @@ interface IProps { } const ErrorView: React.FC = ({ title, messages }) => { - return
-
-
- - Element - -

{ _t("Failed to start") }

-
-
-
-
-

{ title }

- { messages && messages.map(msg =>

- { msg } -

) } + return ( +
+
+
+ + Element + +

{_t("Failed to start")}

+
+
+
+
+

{title}

+ {messages && messages.map((msg) =>

{msg}

)} +
-
-
-
; + ); }; export default ErrorView; - diff --git a/src/components/views/auth/VectorAuthFooter.tsx b/src/components/views/auth/VectorAuthFooter.tsx index 82ba659d4c..26ad2c64ac 100644 --- a/src/components/views/auth/VectorAuthFooter.tsx +++ b/src/components/views/auth/VectorAuthFooter.tsx @@ -15,31 +15,33 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { ReactElement } from 'react'; -import SdkConfig from 'matrix-react-sdk/src/SdkConfig'; -import { _t } from 'matrix-react-sdk/src/languageHandler'; +import React, { ReactElement } from "react"; +import SdkConfig from "matrix-react-sdk/src/SdkConfig"; +import { _t } from "matrix-react-sdk/src/languageHandler"; const VectorAuthFooter = (): ReactElement => { const brandingConfig = SdkConfig.getObject("branding"); const links = brandingConfig?.get("auth_footer_links") ?? [ - { "text": "Blog", "url": "https://element.io/blog" }, - { "text": "Twitter", "url": "https://twitter.com/element_hq" }, - { "text": "GitHub", "url": "https://github.com/vector-im/element-web" }, + { text: "Blog", url: "https://element.io/blog" }, + { text: "Twitter", url: "https://twitter.com/element_hq" }, + { text: "GitHub", url: "https://github.com/vector-im/element-web" }, ]; const authFooterLinks = []; for (const linkEntry of links) { authFooterLinks.push( - { linkEntry.text } + {linkEntry.text} , ); } return ( ); }; diff --git a/src/components/views/auth/VectorAuthHeaderLogo.tsx b/src/components/views/auth/VectorAuthHeaderLogo.tsx index 2e7cce5756..688a929d11 100644 --- a/src/components/views/auth/VectorAuthHeaderLogo.tsx +++ b/src/components/views/auth/VectorAuthHeaderLogo.tsx @@ -15,8 +15,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import * as React from 'react'; -import SdkConfig from 'matrix-react-sdk/src/SdkConfig'; +import * as React from "react"; +import SdkConfig from "matrix-react-sdk/src/SdkConfig"; export default class VectorAuthHeaderLogo extends React.PureComponent { public render(): React.ReactElement { diff --git a/src/components/views/auth/VectorAuthPage.tsx b/src/components/views/auth/VectorAuthPage.tsx index b06aade8fb..560fe424aa 100644 --- a/src/components/views/auth/VectorAuthPage.tsx +++ b/src/components/views/auth/VectorAuthPage.tsx @@ -14,8 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import * as React from 'react'; -import SdkConfig from 'matrix-react-sdk/src/SdkConfig'; +import * as React from "react"; +import SdkConfig from "matrix-react-sdk/src/SdkConfig"; import VectorAuthFooter from "./VectorAuthFooter"; @@ -48,25 +48,25 @@ export default class VectorAuthPage extends React.PureComponent { }; const modalStyle: React.CSSProperties = { - position: 'relative', - background: 'initial', + position: "relative", + background: "initial", }; const blurStyle: React.CSSProperties = { - position: 'absolute', + position: "absolute", top: 0, right: 0, bottom: 0, left: 0, - filter: 'blur(40px)', + filter: "blur(40px)", background: pageStyle.background, }; const modalContentStyle: React.CSSProperties = { - display: 'flex', + display: "flex", zIndex: 1, - background: 'rgba(255, 255, 255, 0.59)', - borderRadius: '8px', + background: "rgba(255, 255, 255, 0.59)", + borderRadius: "8px", }; return ( @@ -74,7 +74,7 @@ export default class VectorAuthPage extends React.PureComponent {
- { this.props.children } + {this.props.children}
diff --git a/src/favicon.ts b/src/favicon.ts index 6899bbebb4..1ae1e5bd9e 100644 --- a/src/favicon.ts +++ b/src/favicon.ts @@ -70,8 +70,8 @@ export default class Favicon { this.baseImage.setAttribute("crossOrigin", "anonymous"); this.baseImage.onload = (): void => { // get height and width of the favicon - this.canvas.height = (this.baseImage.height > 0) ? this.baseImage.height : 32; - this.canvas.width = (this.baseImage.width > 0) ? this.baseImage.width : 32; + this.canvas.height = this.baseImage.height > 0 ? this.baseImage.height : 32; + this.canvas.width = this.baseImage.width > 0 ? this.baseImage.width : 32; this.context = this.canvas.getContext("2d"); this.ready(); }; @@ -89,7 +89,10 @@ export default class Favicon { this.context.drawImage(this.baseImage, 0, 0, this.canvas.width, this.canvas.height); } - private options(n: number | string, params: IParams): { + private options( + n: number | string, + params: IParams, + ): { n: string | number; len: number; x: number; @@ -98,7 +101,7 @@ export default class Favicon { h: number; } { const opt = { - n: ((typeof n) === "number") ? Math.abs(n as number | 0) : n, + n: typeof n === "number" ? Math.abs(n as number | 0) : n, len: ("" + n).length, // badge positioning constants as percentages x: 0.4, @@ -174,8 +177,8 @@ export default class Favicon { this.context.stroke(); this.context.fillStyle = params.textColor; - if ((typeof opt.n) === "number" && opt.n > 999) { - const count = ((opt.n > 9999) ? 9 : Math.floor(opt.n as number / 1000)) + "k+"; + if (typeof opt.n === "number" && opt.n > 999) { + const count = (opt.n > 9999 ? 9 : Math.floor((opt.n as number) / 1000)) + "k+"; this.context.fillText(count, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.2)); } else { this.context.fillText("" + opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15)); @@ -209,7 +212,7 @@ export default class Favicon { newIcon.setAttribute("href", url); old.parentNode?.removeChild(old); } else { - this.icons.forEach(icon => { + this.icons.forEach((icon) => { icon.setAttribute("href", url); }); } @@ -236,7 +239,7 @@ export default class Favicon { const icons: HTMLLinkElement[] = []; const links = window.document.getElementsByTagName("head")[0].getElementsByTagName("link"); for (const link of links) { - if ((/(^|\s)icon(\s|$)/i).test(link.getAttribute("rel"))) { + if (/(^|\s)icon(\s|$)/i.test(link.getAttribute("rel"))) { icons.push(link); } } @@ -252,7 +255,7 @@ export default class Favicon { window.document.getElementsByTagName("head")[0].appendChild(elms[0]); } - elms.forEach(item => { + elms.forEach((item) => { item.setAttribute("type", "image/png"); }); return elms; diff --git a/src/vector/app.tsx b/src/vector/app.tsx index 0ae5bd3040..5e0cec7f90 100644 --- a/src/vector/app.tsx +++ b/src/vector/app.tsx @@ -21,10 +21,10 @@ limitations under the License. // To ensure we load the browser-matrix version first import "matrix-js-sdk/src/browser-index"; -import React, { ReactElement } from 'react'; -import PlatformPeg from 'matrix-react-sdk/src/PlatformPeg'; -import { _td, newTranslatableError } from 'matrix-react-sdk/src/languageHandler'; -import AutoDiscoveryUtils from 'matrix-react-sdk/src/utils/AutoDiscoveryUtils'; +import React, { ReactElement } from "react"; +import PlatformPeg from "matrix-react-sdk/src/PlatformPeg"; +import { _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 SdkConfig, { parseSsoRedirectOptions } from "matrix-react-sdk/src/SdkConfig"; @@ -34,7 +34,7 @@ import { createClient } from "matrix-js-sdk/src/matrix"; import { SnakedObject } from "matrix-react-sdk/src/utils/SnakedObject"; import MatrixChat from "matrix-react-sdk/src/components/structures/MatrixChat"; -import { parseQs } from './url_utils'; +import { parseQs } from "./url_utils"; import VectorBasePlatform from "./platform/VectorBasePlatform"; import { getScreenFromLocation, init as initRouting, onNewScreen } from "./routing"; @@ -58,25 +58,20 @@ window.matrixLogger = logger; function makeRegistrationUrl(params: object): string { let url; if (window.location.protocol === "vector:") { - url = 'https://app.element.io/#/register'; + url = "https://app.element.io/#/register"; } else { - url = ( - window.location.protocol + '//' + - window.location.host + - window.location.pathname + - '#/register' - ); + 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 += '?'; + url += "?"; } else { - url += '&'; + url += "&"; } const k = keys[i]; - url += k + '=' + encodeURIComponent(params[k]); + url += k + "=" + encodeURIComponent(params[k]); } return url; } @@ -99,7 +94,7 @@ export async function loadApp(fragParams: {}): Promise { const params = parseQs(window.location); - const urlWithoutQuery = window.location.protocol + '//' + window.location.host + window.location.pathname; + const urlWithoutQuery = window.location.protocol + "//" + window.location.host + window.location.pathname; logger.log("Vector starting at " + urlWithoutQuery); (platform as VectorBasePlatform).startUpdater(); @@ -115,7 +110,7 @@ export async function loadApp(fragParams: {}): Promise { const ssoRedirects = parseSsoRedirectOptions(config); let autoRedirect = ssoRedirects.immediate === true; // XXX: This path matching is a bit brittle, but better to do it early instead of in the app code. - const isWelcomeOrLanding = window.location.hash === '#/welcome' || window.location.hash === '#'; + const isWelcomeOrLanding = window.location.hash === "#/welcome" || window.location.hash === "#"; if (!autoRedirect && ssoRedirects.on_welcome_page && isWelcomeOrLanding) { autoRedirect = true; } @@ -133,20 +128,21 @@ export async function loadApp(fragParams: {}): Promise { return; } - const defaultDeviceName = snakedConfig.get("default_device_display_name") - ?? platform.getDefaultDeviceDisplayName(); + const defaultDeviceName = snakedConfig.get("default_device_display_name") ?? platform.getDefaultDeviceDisplayName(); - return ; + return ( + + ); } async function verifyServerConfig(): Promise { @@ -164,18 +160,20 @@ async function verifyServerConfig(): Promise { // validators for that purpose. const config = SdkConfig.get(); - let wkConfig = config['default_server_config']; // overwritten later under some conditions - const serverName = config['default_server_name']; - const hsUrl = config['default_hs_url']; - const isUrl = config['default_is_url']; + let wkConfig = config["default_server_config"]; // overwritten later under some conditions + const serverName = config["default_server_name"]; + const hsUrl = config["default_hs_url"]; + const isUrl = config["default_is_url"]; - const incompatibleOptions = [wkConfig, serverName, hsUrl].filter(i => !!i); + const incompatibleOptions = [wkConfig, serverName, hsUrl].filter((i) => !!i); if (incompatibleOptions.length > 1) { // noinspection ExceptionCaughtLocallyJS - throw newTranslatableError(_td( - "Invalid configuration: can only specify one of default_server_config, default_server_name, " + - "or default_hs_url.", - )); + throw newTranslatableError( + _td( + "Invalid configuration: can only specify one of default_server_config, default_server_name, " + + "or default_hs_url.", + ), + ); } if (incompatibleOptions.length < 1) { // noinspection ExceptionCaughtLocallyJS @@ -186,17 +184,17 @@ async function verifyServerConfig(): Promise { logger.log("Config uses a default_hs_url - constructing a default_server_config using this information"); logger.warn( "DEPRECATED CONFIG OPTION: In the future, default_hs_url will not be accepted. Please use " + - "default_server_config instead.", + "default_server_config instead.", ); wkConfig = { "m.homeserver": { - "base_url": hsUrl, + base_url: hsUrl, }, }; if (isUrl) { wkConfig["m.identity_server"] = { - "base_url": isUrl, + base_url: isUrl, }; } } @@ -211,7 +209,7 @@ async function verifyServerConfig(): Promise { logger.log("Config uses a default_server_name - doing .well-known lookup"); logger.warn( "DEPRECATED CONFIG OPTION: In the future, default_server_name will not be accepted. Please " + - "use default_server_config instead.", + "use default_server_config instead.", ); discoveryResult = await AutoDiscovery.findClientConfig(serverName); } @@ -238,7 +236,7 @@ async function verifyServerConfig(): Promise { // Add the newly built config to the actual config for use by the app logger.log("Updating SdkConfig with validated discovery information"); - SdkConfig.add({ "validated_server_config": validatedConfig }); + SdkConfig.add({ validated_server_config: validatedConfig }); return SdkConfig.get(); } diff --git a/src/vector/devcss.ts b/src/vector/devcss.ts index 2f67803385..be4016b61f 100644 --- a/src/vector/devcss.ts +++ b/src/vector/devcss.ts @@ -28,7 +28,7 @@ limitations under the License. * * For more details, see webpack.config.js:184 (string-replace-loader) */ -if (process.env.NODE_ENV === 'development') { +if (process.env.NODE_ENV === "development") { ("use theming"); /** * Clean up old hot-module script injections as they hog up memory @@ -40,8 +40,7 @@ if (process.env.NODE_ENV === 'development') { const elements = Array.from(document.querySelectorAll("script[src*=hot-update]")); if (elements.length > 1) { const oldInjects = elements.slice(0, elements.length - 1); - oldInjects.forEach(e => e.remove()); + oldInjects.forEach((e) => e.remove()); } }, 1000); } - diff --git a/src/vector/getconfig.ts b/src/vector/getconfig.ts index 3d4bbd7bd4..4633b57d8d 100644 --- a/src/vector/getconfig.ts +++ b/src/vector/getconfig.ts @@ -18,8 +18,8 @@ import type { IConfigOptions } from "matrix-react-sdk/src/IConfigOptions"; // 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. -export async function getVectorConfig(relativeLocation=''): Promise { - if (relativeLocation !== '' && !relativeLocation.endsWith('/')) relativeLocation += '/'; +export async function getVectorConfig(relativeLocation = ""): Promise { + if (relativeLocation !== "" && !relativeLocation.endsWith("/")) relativeLocation += "/"; const specificConfigPromise = getConfig(`${relativeLocation}config.${document.domain}.json`); const generalConfigPromise = getConfig(relativeLocation + "config.json"); diff --git a/src/vector/index.ts b/src/vector/index.ts index 067121e939..dcffa5e2cc 100644 --- a/src/vector/index.ts +++ b/src/vector/index.ts @@ -22,13 +22,13 @@ import { logger } from "matrix-js-sdk/src/logger"; // 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'; +import "./modernizr"; // Require common CSS here; this will make webpack process it into bundle.css. // Our own CSS (which is themed) is imported via separate webpack entry points // in webpack.config.js -require('gfm.css/gfm.css'); -require('katex/dist/katex.css'); +require("gfm.css/gfm.css"); +require("katex/dist/katex.css"); /** * This require is necessary only for purposes of CSS hot-reload, as otherwise @@ -37,8 +37,8 @@ require('katex/dist/katex.css'); * * On production build it's going to be an empty module, so don't worry about that. */ -require('./devcss'); -require('./localstorage-fix'); +require("./devcss"); +require("./localstorage-fix"); async function settled(...promises: Array>): Promise { for (const prom of promises) { @@ -60,19 +60,16 @@ function checkBrowserFeatures(): boolean { // in it for some features we depend on. // Modernizr requires rules to be lowercase with no punctuation. // ES2018: http://262.ecma-international.org/9.0/#sec-promise.prototype.finally - window.Modernizr.addTest("promiseprototypefinally", () => - typeof window.Promise?.prototype?.finally === "function"); + window.Modernizr.addTest("promiseprototypefinally", () => typeof window.Promise?.prototype?.finally === "function"); // ES2020: http://262.ecma-international.org/#sec-promise.allsettled - window.Modernizr.addTest("promiseallsettled", () => - typeof window.Promise?.allSettled === "function"); + window.Modernizr.addTest("promiseallsettled", () => typeof window.Promise?.allSettled === "function"); // ES2018: https://262.ecma-international.org/9.0/#sec-get-regexp.prototype.dotAll - window.Modernizr.addTest("regexpdotall", () => ( - window.RegExp?.prototype && - !!Object.getOwnPropertyDescriptor(window.RegExp.prototype, "dotAll")?.get - )); + window.Modernizr.addTest( + "regexpdotall", + () => window.RegExp?.prototype && !!Object.getOwnPropertyDescriptor(window.RegExp.prototype, "dotAll")?.get, + ); // ES2019: http://262.ecma-international.org/10.0/#sec-object.fromentries - window.Modernizr.addTest("objectfromentries", () => - typeof window.Object?.fromEntries === "function"); + window.Modernizr.addTest("objectfromentries", () => typeof window.Object?.fromEntries === "function"); const featureList = Object.keys(window.Modernizr); @@ -80,8 +77,8 @@ function checkBrowserFeatures(): boolean { for (const feature of featureList) { if (window.Modernizr[feature] === undefined) { logger.error( - "Looked for feature '%s' but Modernizr has no results for this. " + - "Has it been configured correctly?", feature, + "Looked for feature '%s' but Modernizr has no results for this. " + "Has it been configured correctly?", + feature, ); return false; } @@ -120,7 +117,8 @@ async function start(): Promise { } = await import( /* webpackChunkName: "init" */ /* webpackPreload: true */ - "./init"); + "./init" + ); try { // give rageshake a chance to load/fail, we don't actually assert rageshake loads, we allow it to fail if no IDB @@ -178,12 +176,12 @@ async function start(): Promise { // error handling begins here // ########################## if (!acceptBrowser) { - await new Promise(resolve => { + await new Promise((resolve) => { logger.error("Browser is missing required features."); // take to a different landing page to AWOOOOOGA at the user showIncompatibleBrowser(() => { if (window.localStorage) { - window.localStorage.setItem('mx_accepts_unsupported_browser', String(true)); + window.localStorage.setItem("mx_accepts_unsupported_browser", String(true)); } logger.log("User accepts the compatibility risks."); resolve(); @@ -199,12 +197,13 @@ async function start(): Promise { if (error.err && error.err instanceof SyntaxError) { // This uses the default brand since the app config is unavailable. return showError(_t("Your Element is misconfigured"), [ - _t("Your Element 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") }, + "Your Element 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.")); @@ -237,7 +236,7 @@ async function start(): Promise { } } -start().catch(err => { +start().catch((err) => { logger.error(err); // show the static error in an iframe to not lose any context / console data // with some basic styling to make the iframe full page diff --git a/src/vector/indexeddb-worker.ts b/src/vector/indexeddb-worker.ts index e196cdfb35..113bc87d6c 100644 --- a/src/vector/indexeddb-worker.ts +++ b/src/vector/indexeddb-worker.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { IndexedDBStoreWorker } from 'matrix-js-sdk/src/indexeddb-worker'; +import { IndexedDBStoreWorker } from "matrix-js-sdk/src/indexeddb-worker"; const remoteWorker = new IndexedDBStoreWorker(postMessage as InstanceType["postMessage"]); diff --git a/src/vector/init.tsx b/src/vector/init.tsx index 133ebefdf6..6ab4e4cacf 100644 --- a/src/vector/init.tsx +++ b/src/vector/init.tsx @@ -20,7 +20,7 @@ limitations under the License. // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore import olmWasmPath from "@matrix-org/olm/olm.wasm"; -import Olm from '@matrix-org/olm'; +import Olm from "@matrix-org/olm"; import * as ReactDOM from "react-dom"; import * as React from "react"; import * as languageHandler from "matrix-react-sdk/src/languageHandler"; @@ -45,7 +45,7 @@ export function preparePlatform(): void { if (window.electron) { logger.log("Using Electron platform"); PlatformPeg.set(new ElectronPlatform()); - } else if (window.matchMedia('(display-mode: standalone)').matches) { + } else if (window.matchMedia("(display-mode: standalone)").matches) { logger.log("Using PWA platform"); PlatformPeg.set(new PWAPlatform()); } else { @@ -90,30 +90,35 @@ export function loadOlm(): Promise { */ return Olm.init({ locateFile: () => olmWasmPath, - }).then(() => { - logger.log("Using WebAssembly Olm"); - }).catch((wasmLoadError) => { - logger.log("Failed to load Olm: trying legacy version", wasmLoadError); - 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(() => { - logger.log("Using legacy Olm"); - }).catch((legacyLoadError) => { - logger.log("Both WebAssembly and asm.js Olm failed!", legacyLoadError); + }) + .then(() => { + logger.log("Using WebAssembly Olm"); + }) + .catch((wasmLoadError) => { + logger.log("Failed to load Olm: trying legacy version", wasmLoadError); + 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(() => { + logger.log("Using legacy Olm"); + }) + .catch((legacyLoadError) => { + logger.log("Both WebAssembly and asm.js Olm failed!", legacyLoadError); + }); }); - }); } export async function loadLanguage(): Promise { - const prefLang = SettingsStore.getValue("language", null, /*excludeDefault=*/true); + const prefLang = SettingsStore.getValue("language", null, /*excludeDefault=*/ true); let langs = []; if (!prefLang) { @@ -140,25 +145,35 @@ export async function loadApp(fragParams: {}): Promise { const module = await import( /* webpackChunkName: "element-web-app" */ /* webpackPreload: true */ - "./app"); - window.matrixChat = ReactDOM.render(await module.loadApp(fragParams), - document.getElementById('matrixchat')); + "./app" + ); + window.matrixChat = ReactDOM.render(await module.loadApp(fragParams), document.getElementById("matrixchat")); } export async function showError(title: string, messages?: string[]): Promise { - const ErrorView = (await import( - /* webpackChunkName: "error-view" */ - "../async-components/structures/ErrorView")).default; - window.matrixChat = ReactDOM.render(, - document.getElementById('matrixchat')); + const ErrorView = ( + await import( + /* webpackChunkName: "error-view" */ + "../async-components/structures/ErrorView" + ) + ).default; + window.matrixChat = ReactDOM.render( + , + document.getElementById("matrixchat"), + ); } export async function showIncompatibleBrowser(onAccept): Promise { - const CompatibilityView = (await import( - /* webpackChunkName: "compatibility-view" */ - "../async-components/structures/CompatibilityView")).default; - window.matrixChat = ReactDOM.render(, - document.getElementById('matrixchat')); + const CompatibilityView = ( + await import( + /* webpackChunkName: "compatibility-view" */ + "../async-components/structures/CompatibilityView" + ) + ).default; + window.matrixChat = ReactDOM.render( + , + document.getElementById("matrixchat"), + ); } export async function loadModules(): Promise { diff --git a/src/vector/jitsi/index.html b/src/vector/jitsi/index.html index 1a05c60277..be84a62c2b 100644 --- a/src/vector/jitsi/index.html +++ b/src/vector/jitsi/index.html @@ -1,24 +1,24 @@ - - - Jitsi Widget - - -
-
-
-
- - -

Jitsi Video Conference

-
- + + + Jitsi Widget + + +
+
+
+
+ + +

Jitsi Video Conference

+
+ +
+
-
-
- - - + + + diff --git a/src/vector/jitsi/index.pcss b/src/vector/jitsi/index.pcss index 64e427ff9e..e9d90538fe 100644 --- a/src/vector/jitsi/index.pcss +++ b/src/vector/jitsi/index.pcss @@ -17,10 +17,10 @@ limitations under the License. /* TODO: Match the user's theme: https://github.com/vector-im/element-web/issues/12794 */ @font-face { - font-family: 'Nunito'; + font-family: "Nunito"; font-style: normal; font-weight: 400; - src: url('~matrix-react-sdk/res/fonts/Nunito/Nunito-Regular.ttf') format('truetype'); + src: url("~matrix-react-sdk/res/fonts/Nunito/Nunito-Regular.ttf") format("truetype"); } $dark-fg: #edf3ff; @@ -38,7 +38,8 @@ body.theme-light { color: $light-fg; } -body, html { +body, +html { padding: 0; margin: 0; } @@ -92,7 +93,7 @@ body, html { margin-top: -$icon-size; /* to visually center the form */ &::before { - content: ''; + content: ""; background-size: contain; background-color: $dark-fg; mask-repeat: no-repeat; diff --git a/src/vector/jitsi/index.ts b/src/vector/jitsi/index.ts index 81a9735187..95d3301113 100644 --- a/src/vector/jitsi/index.ts +++ b/src/vector/jitsi/index.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { KJUR } from 'jsrsasign'; +import { KJUR } from "jsrsasign"; import { IOpenIDCredentials, IWidgetApiRequest, @@ -34,7 +34,7 @@ import { getVectorConfig } from "../getconfig"; // We have to trick webpack into loading our CSS for us. require("./index.pcss"); -const JITSI_OPENIDTOKEN_JWT_AUTH = 'openidtoken-jwt'; +const JITSI_OPENIDTOKEN_JWT_AUTH = "openidtoken-jwt"; // Dev note: we use raw JS without many dependencies to reduce bundle size. // We do not need all of React to render a Jitsi conference. @@ -82,9 +82,9 @@ const setupCompleted = (async (): Promise => { // If we have these params, expect a widget API to be available (ie. to be in an iframe // inside a matrix client). Otherwise, assume we're on our own, eg. have been popped // out into a browser. - const parentUrl = qsParam('parentUrl', true); - const widgetId = qsParam('widgetId', true); - const theme = qsParam('theme', true); + const parentUrl = qsParam("parentUrl", true); + const widgetId = qsParam("widgetId", true); + const theme = qsParam("theme", true); if (theme) { document.body.classList.add(`theme-${theme.replace(" ", "_")}`); @@ -93,10 +93,10 @@ const setupCompleted = (async (): Promise => { // Set this up as early as possible because Element will be hitting it almost immediately. let widgetApiReady: Promise; if (parentUrl && widgetId) { - const parentOrigin = new URL(qsParam('parentUrl')).origin; + const parentOrigin = new URL(qsParam("parentUrl")).origin; widgetApi = new WidgetApi(qsParam("widgetId"), parentOrigin); - widgetApiReady = new Promise(resolve => widgetApi.once("ready", resolve)); + widgetApiReady = new Promise((resolve) => widgetApi.once("ready", resolve)); widgetApi.requestCapabilities(VideoConferenceCapabilities); widgetApi.start(); @@ -134,39 +134,39 @@ const setupCompleted = (async (): Promise => { meetApi = null; closeConference(); } else { - meetApi?.executeCommand('hangup'); + meetApi?.executeCommand("hangup"); } }); handleAction(ElementWidgetActions.MuteAudio, async () => { - if (meetApi && !await meetApi.isAudioMuted()) { - meetApi.executeCommand('toggleAudio'); + if (meetApi && !(await meetApi.isAudioMuted())) { + meetApi.executeCommand("toggleAudio"); } }); handleAction(ElementWidgetActions.UnmuteAudio, async () => { - if (meetApi && await meetApi.isAudioMuted()) { - meetApi.executeCommand('toggleAudio'); + if (meetApi && (await meetApi.isAudioMuted())) { + meetApi.executeCommand("toggleAudio"); } }); handleAction(ElementWidgetActions.MuteVideo, async () => { - if (meetApi && !await meetApi.isVideoMuted()) { - meetApi.executeCommand('toggleVideo'); + if (meetApi && !(await meetApi.isVideoMuted())) { + meetApi.executeCommand("toggleVideo"); } }); handleAction(ElementWidgetActions.UnmuteVideo, async () => { - if (meetApi && await meetApi.isVideoMuted()) { - meetApi.executeCommand('toggleVideo'); + if (meetApi && (await meetApi.isVideoMuted())) { + meetApi.executeCommand("toggleVideo"); } }); handleAction(ElementWidgetActions.TileLayout, async () => { - meetApi?.executeCommand('setTileView', true); + meetApi?.executeCommand("setTileView", true); }); handleAction(ElementWidgetActions.SpotlightLayout, async () => { - meetApi?.executeCommand('setTileView', false); + meetApi?.executeCommand("setTileView", false); }); handleAction(ElementWidgetActions.StartLiveStream, async ({ rtmpStreamKey }) => { if (!meetApi) throw new Error("Conference not joined"); - meetApi.executeCommand('startRecording', { - mode: 'stream', + meetApi.executeCommand("startRecording", { + mode: "stream", // this looks like it should be rtmpStreamKey but we may be on too old // a version of jitsi meet //rtmpStreamKey, @@ -178,23 +178,23 @@ const setupCompleted = (async (): Promise => { } // Populate the Jitsi params now - jitsiDomain = qsParam('conferenceDomain'); - conferenceId = qsParam('conferenceId'); - displayName = qsParam('displayName', true); - avatarUrl = qsParam('avatarUrl', true); // http not mxc - userId = qsParam('userId'); - jitsiAuth = qsParam('auth', true); - roomId = qsParam('roomId', true); - roomName = qsParam('roomName', true); - startAudioOnly = qsParam('isAudioOnly', true) === "true"; - isVideoChannel = qsParam('isVideoChannel', true) === "true"; - supportsScreensharing = qsParam('supportsScreensharing', true) === "true"; + jitsiDomain = qsParam("conferenceDomain"); + conferenceId = qsParam("conferenceId"); + displayName = qsParam("displayName", true); + avatarUrl = qsParam("avatarUrl", true); // http not mxc + userId = qsParam("userId"); + jitsiAuth = qsParam("auth", true); + roomId = qsParam("roomId", true); + roomName = qsParam("roomName", true); + startAudioOnly = qsParam("isAudioOnly", true) === "true"; + isVideoChannel = qsParam("isVideoChannel", true) === "true"; + supportsScreensharing = qsParam("supportsScreensharing", true) === "true"; // We've reached the point where we have to wait for the config, so do that then parse it. const instanceConfig = new SnakedObject((await configPromise) ?? {}); const jitsiConfig = instanceConfig.get("jitsi_widget") ?? {}; - skipOurWelcomeScreen = (new SnakedObject(jitsiConfig)) - .get("skip_built_in_welcome_screen") ?? false; + skipOurWelcomeScreen = + new SnakedObject(jitsiConfig).get("skip_built_in_welcome_screen") ?? false; // Either reveal the prejoin screen, or skip straight to Jitsi depending on the config. // We don't set up the call yet though as this might lead to failure without the widget API. @@ -238,10 +238,10 @@ function switchVisibleContainers(): void { } function toggleConferenceVisibility(inConference: boolean): void { - document.getElementById("jitsiContainer").style.visibility = inConference ? 'unset' : 'hidden'; + document.getElementById("jitsiContainer").style.visibility = inConference ? "unset" : "hidden"; // Video rooms have a separate UI for joining, so they should never show our join button document.getElementById("joinButtonContainer").style.visibility = - (inConference || isVideoChannel) ? 'hidden' : 'unset'; + inConference || isVideoChannel ? "hidden" : "unset"; } function skipToJitsiSplashScreen(): void { @@ -256,7 +256,7 @@ function skipToJitsiSplashScreen(): void { */ function createJWTToken(): string { // Header - const header = { alg: 'HS256', typ: 'JWT' }; + const header = { alg: "HS256", typ: "JWT" }; // Payload const payload = { // As per Jitsi token auth, `iss` needs to be set to something agreed between @@ -281,12 +281,7 @@ function createJWTToken(): string { // Sign JWT // The secret string here is irrelevant, we're only using the JWT // to transport data to Prosody in the Jitsi stack. - return KJUR.jws.JWS.sign( - 'HS256', - JSON.stringify(header), - JSON.stringify(payload), - 'notused', - ); + return KJUR.jws.JWS.sign("HS256", JSON.stringify(header), JSON.stringify(payload), "notused"); } async function notifyHangup(errorMessage?: string): Promise { @@ -318,9 +313,10 @@ function closeConference(): void { function joinConference(audioInput?: string | null, videoInput?: string | null): void { let jwt; if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) { - if (!openIdToken?.access_token) { // eslint-disable-line camelcase + if (!openIdToken?.access_token) { + // eslint-disable-line camelcase // We've failing to get a token, don't try to init conference - logger.warn('Expected to have an OpenID credential, cannot initialize widget.'); + logger.warn("Expected to have an OpenID credential, cannot initialize widget."); document.getElementById("widgetActionContainer").innerText = "Failed to load Jitsi widget"; return; } @@ -331,8 +327,8 @@ function joinConference(audioInput?: string | null, videoInput?: string | null): logger.warn( "[Jitsi Widget] The next few errors about failing to parse URL parameters are fine if " + - "they mention 'external_api' or 'jitsi' in the stack. They're just Jitsi Meet trying to parse " + - "our fragment values and not recognizing the options.", + "they mention 'external_api' or 'jitsi' in the stack. They're just Jitsi Meet trying to parse " + + "our fragment values and not recognizing the options.", ); const options = { @@ -400,7 +396,7 @@ function joinConference(audioInput?: string | null, videoInput?: string | null): meetApi.on("audioMuteStatusChanged", onAudioMuteStatusChanged); meetApi.on("videoMuteStatusChanged", onVideoMuteStatusChanged); - ["videoConferenceJoined", "participantJoined", "participantLeft"].forEach(event => { + ["videoConferenceJoined", "participantJoined", "participantLeft"].forEach((event) => { meetApi.on(event, updateParticipants); }); @@ -472,5 +468,4 @@ const updateParticipants = (): void => { }); }; -const onLog = ({ logLevel, args }): void => - (parent as unknown as typeof global).mx_rage_logger?.log(logLevel, ...args); +const onLog = ({ logLevel, args }): void => (parent as unknown as typeof global).mx_rage_logger?.log(logLevel, ...args); diff --git a/src/vector/localstorage-fix.ts b/src/vector/localstorage-fix.ts index d0ca05174f..3c3d46b8ac 100644 --- a/src/vector/localstorage-fix.ts +++ b/src/vector/localstorage-fix.ts @@ -4,8 +4,8 @@ * */ if (window.localStorage) { - Object.keys(window.localStorage).forEach(key => { - if (key.indexOf('loglevel:') === 0) { + Object.keys(window.localStorage).forEach((key) => { + if (key.indexOf("loglevel:") === 0) { window.localStorage.removeItem(key); } }); diff --git a/src/vector/mobile_guide/index.html b/src/vector/mobile_guide/index.html index 2def26fa83..c39041c501 100644 --- a/src/vector/mobile_guide/index.html +++ b/src/vector/mobile_guide/index.html @@ -1,489 +1,788 @@ - + - - Element Mobile Guide - - + + Element Mobile Guide + + - - - -
- -
+ .mx_FooterLink { + color: #0dbd8b; + text-decoration: none; + } -
-
- -

Set up Element on iOS or Android

-
-
-
-
-

Install the app

-

iOS (iPhone or iPad)

- - - Download on the App Store. - - - - - - - - - - - + .mx_Subtext { + font-size: 14px; + } + + .mx_SubtextTop { + margin-top: 32px; + } + + @media screen and (max-width: 1120px) { + body { + font-size: 20px; + } + + h1 { + font-size: 20px; + } + + h4 { + font-size: 16px; + } + + .mx_Button { + font-size: 18px; + padding: 14px 28px; + } + + .mx_HomePage_header { + justify-content: left; + } + + .mx_Spacer { + margin-top: 24px; + } + } + + + + +
+ +
+ +
+
+ +

Set up Element on iOS or Android

+
+
+
+
+

Install the app

+

iOS (iPhone or iPad)

+
+ + Download on the App Store. + + + + + + d="M110.13477,0 L9.53468,0 C9.16798,0 8.80568,0 8.43995,0.002 C8.1338,0.004 7.83009,0.00981 7.521,0.0147 C6.84951368,0.0226018036 6.17960647,0.0816762599 5.5171,0.19141 C4.85552408,0.303532628 4.21467252,0.51491574 3.61622,0.81841 C3.01849941,1.12447171 2.47234502,1.52216753 1.99757,1.99707 C1.5203025,2.47063699 1.12246318,3.01801638 0.81935,3.61816 C0.515400242,4.21710912 0.304640498,4.8589383 0.19435,5.52148 C0.0830109412,6.18318491 0.0230983806,6.85252054 0.01515,7.52348 C0.00587,7.83008 0.00489,8.1377 0,8.44434 L0,31.5586 C0.00489,31.8691 0.00587,32.1699 0.01515,32.4805 C0.023100818,33.1514262 0.0830133796,33.8207284 0.19435,34.4824 C0.304335953,35.145329 0.515108158,35.7875266 0.81935,36.3867 C1.12232555,36.9849029 1.52022081,37.5300662 1.99757,38.001 C2.47054482,38.478014 3.01705224,38.8759804 3.61622,39.1797 C4.21466797,39.4840065 4.85545044,39.6966478 5.5171,39.8105 C6.1797191,39.9193547 6.84955741,39.9784532 7.521,39.9873 C7.83009,39.9941 8.1338,39.998 8.43995,39.998 C8.80567,40 9.168,40 9.53468,40 L110.13477,40 C110.49417,40 110.85937,40 111.21877,39.998 C111.52347,39.998 111.83597,39.9941 112.14067,39.9873 C112.810843,39.9789237 113.47942,39.9198215 114.14067,39.8105 C114.804454,39.6958464 115.447534,39.4832614 116.04887,39.1797 C116.647497,38.8758046 117.1935,38.4778491 117.66607,38.001 C118.142188,37.5282218 118.540932,36.9834573 118.84767,36.3867 C119.149629,35.7870908 119.358395,35.1449445 119.46677,34.4824 C119.578285,33.8206376 119.640323,33.1514843 119.65237,32.4805 C119.65627,32.1699 119.65627,31.8691 119.65627,31.5586 C119.66407,31.1953 119.66407,30.834 119.66407,30.4648 L119.66407,9.53613 C119.66407,9.16992 119.66407,8.80664 119.65627,8.44434 C119.65627,8.1377 119.65627,7.83008 119.65237,7.52344 C119.640326,6.85242244 119.578287,6.18323567 119.46677,5.52144 C119.358093,4.8592816 119.149339,4.21750328 118.84767,3.61812 C118.230394,2.41519608 117.251573,1.4360254 116.04887,0.81832 C115.447532,0.51556738 114.804383,0.30424041 114.14067,0.19132 C113.479535,0.0811027831 112.810888,0.0220077649 112.14067,0.01456 C111.83597,0.00968 111.52347,0.00382 111.21877,0.00187 C110.85937,-0.00013 110.49417,-0.00013 110.13477,-0.00013 L110.13477,0 Z" + fill="#A6A6A6" + > + d="M8.44483,39.125 C8.14015,39.125 7.84283,39.1211 7.54054,39.1143 C6.91432177,39.1061407 6.28956717,39.051625 5.6714,38.9512 C5.0949903,38.8519282 4.53660424,38.6672645 4.01466,38.4033 C3.4975017,38.1415227 3.02581177,37.7982729 2.61766,37.3867 C2.20360033,36.9799753 1.85887673,36.508244 1.59715,35.9902 C1.33253926,35.4687891 1.14941288,34.9098996 1.05415,34.333 C0.951274358,33.7131149 0.895614374,33.0863132 0.88765,32.458 C0.88131,32.2471 0.87301,31.5449 0.87301,31.5449 L0.87301,8.44434 C0.87301,8.44434 0.88185,7.75293 0.8877,7.5498 C0.895325827,6.92248205 0.950662148,6.2966531 1.05323,5.67773 C1.14866872,5.09925008 1.33193687,4.53874887 1.59669,4.01563 C1.85745482,3.49794385 2.20026458,3.02586029 2.61183,2.61768 C3.02293283,2.20562142 3.49613161,1.86060412 4.01417,1.59521 C4.53491467,1.3320936 5.09224545,1.14873255 5.66749,1.05127 C6.28768733,0.949836139 6.91461084,0.894996084 7.543,0.88721 L8.44532,0.875 L111.21387,0.875 L112.12697,0.8877 C112.749643,0.895099198 113.370872,0.949450006 113.98537,1.05029 C114.566438,1.14897507 115.129577,1.33361553 115.65627,1.59814 C116.694088,2.13298605 117.538542,2.97916295 118.07127,4.01807 C118.331797,4.53757756 118.512239,5.09350676 118.60647,5.66699 C118.710259,6.29099138 118.768347,6.92173834 118.78027,7.5542 C118.78317,7.8374 118.78317,8.1416 118.78317,8.44434 C118.79107,8.81934 118.79107,9.17627 118.79107,9.53613 L118.79107,30.4648 C118.79107,30.8281 118.79107,31.1826 118.78317,31.54 C118.78317,31.8652 118.78317,32.1631 118.77927,32.4697 C118.767605,33.0908757 118.710486,33.7103642 118.60837,34.3232 C118.51506,34.9042612 118.33293,35.4675131 118.06837,35.9932 C117.804793,36.5056211 117.462049,36.9732662 117.05277,37.3789 C116.644251,37.7926846 116.171729,38.1379427 115.65337,38.4014 C115.128136,38.6673937 114.565826,38.8527403 113.98537,38.9512 C113.36725,39.052161 112.742435,39.1066802 112.11617,39.1143 C111.82327,39.1211 111.51657,39.125 111.21877,39.125 L110.13477,39.127 L8.44483,39.125 Z" + fill="#000000" + > + + + + + + + + + + + + + + + + + + - - - - - - - - - + + + + + + + + + + + + + + + + - - + + +

Android

+ + + + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + d="M156,0.96 C158.783515,0.96 161.04,3.21648486 161.04,6 L161.04,42 C161.04,44.7835151 158.783515,47.04 156,47.04 L6,47.04 C3.21648486,47.04 0.96,44.7835151 0.96,42 L0.96,6 C0.96,3.21648486 3.21648486,0.96 6,0.96 L156,0.96 Z M156,0 L6,0 C2.6862915,2.13162821e-15 0,2.6862915 0,6 L0,42 C0,45.3137085 2.6862915,48 6,48 L156,48 C159.313708,48 162,45.3137085 162,42 L162,6 C162,2.6862915 159.313708,2.13162821e-15 156,0 Z" + id="Shape" + fill="#A6A6A6" + > - + d="M56.904,12.288 C56.9452243,13.1774773 56.6199211,14.0449523 56.004,14.688 C55.3157397,15.4029639 54.3557806,15.791311 53.364,15.756 C51.8428083,15.7440945 50.4769778,14.8214594 49.8980302,13.4146952 C49.3190825,12.007931 49.6398426,10.3911869 50.712,9.312 C51.4086396,8.60079116 52.3686947,8.20981851 53.364,8.232 C53.8713092,8.23030972 54.3736031,8.33240198 54.84,8.532 C55.2742254,8.70714427 55.6607809,8.98266787 55.968,9.336 L55.332,9.972 C54.8464162,9.39441174 54.1174618,9.07882784 53.364,9.12 C52.6066192,9.11628465 51.8804152,9.42130519 51.3528432,9.96472697 C50.8252713,10.5081487 50.541874,11.2430609 50.568,12 C50.5519223,13.1438276 51.2255718,14.1850074 52.2755199,14.63911 C53.325468,15.0932125 54.5455111,14.8710548 55.368,14.076 C55.7372514,13.6758439 55.9502196,13.1562016 55.968,12.612 L53.364,12.612 L53.364,11.748 L56.856,11.748 C56.8880078,11.9262169 56.9040713,12.1069316 56.904,12.288 Z M62.4,9.288 L59.16,9.288 L59.16,11.568 L62.112,11.568 L62.112,12.432 L59.16,12.432 L59.16,14.712 L62.4,14.712 L62.4,15.6 L58.2,15.6 L58.2,8.4 L62.4,8.4 L62.4,9.288 Z M66.336,15.6 L65.412,15.6 L65.412,9.288 L63.396,9.288 L63.396,8.4 L68.4,8.4 L68.4,9.288 L66.336,9.288 L66.336,15.6 Z M71.928,15.6 L71.928,8.4 L72.852,8.4 L72.852,15.6 L71.928,15.6 Z M76.956,15.6 L76.032,15.6 L76.032,9.288 L74.016,9.288 L74.016,8.4 L78.96,8.4 L78.96,9.288 L76.956,9.288 L76.956,15.6 Z M88.332,14.664 C86.8715878,16.1162735 84.5124122,16.1162735 83.052,14.664 C81.6363227,13.1654806 81.6363227,10.8225194 83.052,9.324 C83.7503981,8.62049603 84.7006989,8.22483956 85.692,8.22483956 C86.6833011,8.22483956 87.6336019,8.62049603 88.332,9.324 C89.7537186,10.8201118 89.7537186,13.1678882 88.332,14.664 Z M83.736,14.064 C84.8175887,15.1410781 86.5664113,15.1410781 87.648,14.064 C88.710238,12.8930054 88.710238,11.1069946 87.648,9.936 C86.5664113,8.85892192 84.8175887,8.85892192 83.736,9.936 C82.673762,11.1069946 82.673762,12.8930054 83.736,14.064 Z M90.696,15.6 L90.696,8.4 L91.824,8.4 L95.328,14.004 L95.328,8.4 L96.252,8.4 L96.252,15.6 L95.292,15.6 L91.632,9.732 L91.632,15.6 L90.696,15.6 Z" + id="Shape" + stroke="#FFFFFF" + stroke-width="0.5" + fill="#FFFFFF" + > - + d="M81.768,26.1 C78.95451,26.1132401 76.6832087,28.4024174 76.692025,31.2159247 C76.7008414,34.0294321 78.9864446,36.3043299 81.7999624,36.2999373 C84.6134801,36.2955447 86.8919688,34.0135212 86.892,31.2 C86.9117676,29.8372485 86.3774779,28.5248517 85.4115062,27.5634044 C84.4455344,26.6019571 83.1306436,26.0738348 81.768,26.1 Z M81.768,34.296 C80.4998728,34.3846863 79.3064075,33.6891762 78.7584256,32.5421245 C78.2104436,31.3950728 78.419323,30.0296194 79.285159,29.0988456 C80.1509951,28.1680719 81.4978054,27.8611589 82.6814254,28.3248997 C83.8650454,28.7886405 84.6449026,29.9287792 84.648,31.2 C84.6870513,31.9952063 84.4032392,32.7724713 83.8609684,33.3554125 C83.3186976,33.9383536 82.5639505,34.2775396 81.768,34.296 Z M70.596,26.1 C67.7809194,26.1066237 65.503593,28.3928962 65.5080063,31.2079812 C65.5124195,34.0230661 67.796903,36.302187 70.6119906,36.2999843 C73.4270781,36.2977816 75.7079922,34.0150884 75.708,31.2 C75.7277676,29.8372485 75.1934779,28.5248517 74.2275062,27.5634044 C73.2615344,26.6019571 71.9466436,26.0738348 70.584,26.1 L70.596,26.1 Z M70.596,34.296 C69.3272109,34.3896877 68.1302658,33.6976456 67.5784221,32.5513159 C67.0265783,31.4049863 67.2321873,30.0377538 68.096776,29.1044321 C68.9613648,28.1711103 70.3089091,27.8617171 71.4940246,28.3244312 C72.6791401,28.7871453 73.4605457,29.9277613 73.464,31.2 C73.5030513,31.9952063 73.2192392,32.7724713 72.6769684,33.3554125 C72.1346976,33.9383536 71.3799505,34.2775396 70.584,34.296 L70.596,34.296 Z M57.288,27.672 L57.288,29.832 L62.472,29.832 C62.3907161,30.8493646 61.9678136,31.8093534 61.272,32.556 C60.2232275,33.6242696 58.7718519,34.1995896 57.276,34.14 C54.0948398,34.14 51.516,31.5611602 51.516,28.38 C51.516,25.1988398 54.0948398,22.62 57.276,22.62 C58.7373549,22.5961666 60.1485143,23.1528625 61.2,24.168 L62.724,22.644 C61.2899035,21.1918298 59.3288621,20.3823049 57.288,20.4 C54.3827529,20.2869348 51.6484723,21.7724061 50.1622249,24.2712685 C48.6759775,26.7701308 48.6759775,29.8818692 50.1622249,32.3807315 C51.6484723,34.8795939 54.3827529,36.3650652 57.288,36.252 C59.366118,36.3459052 61.3832889,35.5364115 62.82,34.032 C64.0772109,32.6507059 64.7484517,30.8349134 64.692,28.968 C64.6958722,28.5131858 64.6556846,28.0590658 64.572,27.612 L57.288,27.672 Z M111.66,29.352 C111.019296,27.4670521 109.281513,26.1732637 107.292,26.1 C105.965236,26.097406 104.696574,26.6440891 103.787255,27.6102403 C102.877937,28.5763915 102.409075,29.8758277 102.492,31.2 C102.462529,32.5551554 102.985397,33.864034 103.940463,34.8258889 C104.895528,35.7877438 106.20067,36.3198705 107.556,36.3 C109.26103,36.3091908 110.856772,35.4617346 111.804,34.044 L110.064,32.844 C109.535695,33.7247306 108.583028,34.2626004 107.556,34.26 C106.49231,34.3005421 105.51197,33.6866398 105.084,32.712 L111.912,29.892 L111.66,29.352 Z M104.7,31.056 C104.650862,30.3061971 104.905591,29.568174 105.40676,29.0083093 C105.907929,28.4484447 106.613352,28.113866 107.364,28.08 C108.155244,28.0278619 108.90128,28.4528191 109.26,29.16 L104.7,31.056 Z M99.156,36 L101.4,36 L101.4,21 L99.156,21 L99.156,36 Z M95.484,27.24 L95.4,27.24 C94.7159545,26.4752121 93.7380686,26.0386559 92.712,26.04 C89.9881763,26.172086 87.8476045,28.4189755 87.8476045,31.146 C87.8476045,33.8730245 89.9881763,36.119914 92.712,36.252 C93.7413947,36.2693085 94.7256191,35.8299226 95.4,35.052 L95.472,35.052 L95.472,35.784 C95.472,37.74 94.428,38.784 92.748,38.784 C91.6035636,38.7572129 90.5888737,38.0412401 90.18,36.972 L88.224,37.788 C88.977218,39.6201732 90.767077,40.81183 92.748,40.8 C95.376,40.8 97.548,39.252 97.548,35.484 L97.548,26.4 L95.484,26.4 L95.484,27.24 Z M92.916,34.296 C91.3114651,34.1545918 90.0808516,32.810754 90.0808516,31.2 C90.0808516,29.589246 91.3114651,28.2454082 92.916,28.104 C93.6913435,28.143789 94.4177831,28.4949242 94.9306198,29.0777958 C95.4434564,29.6606674 95.6992476,30.4259002 95.64,31.2 C95.7061618,31.9776593 95.451189,32.7485174 94.9343644,33.3333452 C94.4175397,33.9181731 93.6838925,34.2660166 92.904,34.296 L92.916,34.296 Z M122.172,21 L116.808,21 L116.808,36 L119.052,36 L119.052,30.312 L122.184,30.312 C123.927221,30.4372366 125.594369,29.5782107 126.504156,28.0859676 C127.413943,26.5937244 127.413943,24.7182756 126.504156,23.2260324 C125.594369,21.7337893 123.927221,20.8747634 122.184,21 L122.172,21 Z M122.172,28.2 L119.04,28.2 L119.04,23.088 L122.22,23.088 C123.641581,23.088 124.794,24.2404191 124.794,25.662 C124.794,27.0835809 123.641581,28.236 122.22,28.236 L122.172,28.2 Z M136.008,26.04 C134.338455,25.9402067 132.768824,26.8405056 132.012,28.332 L134.004,29.16 C134.405287,28.4204018 135.20535,27.9874267 136.044,28.056 C136.619434,27.9887935 137.197744,28.1555641 137.649034,28.5188522 C138.100324,28.8821403 138.386758,29.4114899 138.444,29.988 L138.444,30.144 C137.723271,29.7623989 136.919511,29.5645502 136.104,29.568 C133.956,29.568 131.784,30.768 131.784,32.94 C131.826825,33.8756506 132.246288,34.7541954 132.946958,35.3757568 C133.647627,35.9973182 134.569918,36.3090392 135.504,36.24 C136.655075,36.3226597 137.759483,35.7704558 138.384,34.8 L138.456,34.8 L138.456,36 L140.616,36 L140.616,30.228 C140.616,27.6 138.624,26.076 136.068,26.076 L136.008,26.04 Z M135.732,34.26 C135,34.26 133.98,33.888 133.98,32.988 C133.98,31.788 135.252,31.392 136.38,31.392 C137.09285,31.3744103 137.797357,31.5484649 138.42,31.896 C138.264835,33.239298 137.143828,34.2630486 135.792,34.296 L135.732,34.26 Z M148.488,26.4 L145.92,32.904 L145.848,32.904 L143.184,26.4 L140.784,26.4 L144.78,35.496 L142.5,40.548 L144.84,40.548 L150.984,26.4 L148.488,26.4 Z M128.316,36 L130.56,36 L130.56,21 L128.316,21 L128.316,36 Z" + id="Shape" + fill="#FFFFFF" + > + d="M12.528,9.048 C12.1368779,9.51733 11.9394644,10.1181539 11.976,10.728 L11.976,37.272 C11.9394644,37.8818461 12.1368779,38.48267 12.528,38.952 L12.612,39.036 L27.48,24.18 L27.48,23.832 L12.612,8.964 L12.528,9.048 Z" + id="Shape" + fill="url(#linearGradient-1)" + > + d="M32.4,29.136 L27.48,24.18 L27.48,23.832 L32.4,18.864 L32.508,18.924 L38.4,22.272 C40.08,23.22 40.08,24.78 38.4,25.74 L32.532,29.076 L32.4,29.136 Z" + id="Shape" + fill="url(#linearGradient-2)" + > + d="M32.544,29.064 L27.48,24 L12.528,38.952 C13.2344613,39.5730269 14.2829148,39.6032707 15.024,39.024 L32.556,29.064" + id="Shape" + fill="url(#linearGradient-3)" + > + d="M32.544,18.936 L15.012,8.976 C14.2709148,8.39672927 13.2224613,8.42697312 12.516,9.048 L27.48,24 L32.544,18.936 Z" + id="Shape" + fill="url(#linearGradient-4)" + > + d="M32.4,28.956 L15.012,38.856 C14.3004462,39.3880037 13.3235538,39.3880037 12.612,38.856 L12.528,38.94 L12.612,39.024 C13.3222149,39.5600151 14.3017851,39.5600151 15.012,39.024 L32.544,29.064 L32.4,28.956 Z" + id="Shape" + fill="#000000" + opacity="0.2" + > + d="M12.528,38.784 C12.1368779,38.31467 11.9394644,37.7138461 11.976,37.104 L11.976,37.284 C11.9394644,37.8938461 12.1368779,38.49467 12.528,38.964 L12.612,38.88 L12.528,38.784 Z M38.4,25.56 L32.4,28.956 L32.508,29.064 L38.4,25.728 C39.0804841,25.4083358 39.5381621,24.7492796 39.6,24 C39.4632354,24.6778055 39.0200471,25.2539503 38.4,25.56 Z" + id="Shape" + fill="#000000" + opacity="0.12" + > - + d="M15.012,9.144 L38.4,22.44 C39.0200471,22.7460497 39.4632354,23.3221945 39.6,24 C39.5381621,23.2507204 39.0804841,22.5916642 38.4,22.272 L15.012,8.976 C13.332,8.028 11.964,8.82 11.964,10.74 L11.964,10.92 C12,8.988 13.344,8.196 15.012,9.144 Z" + id="Shape" + fill="#FFFFFF" + opacity="0.25" + > - - - - -

Android

- - - - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Get it on F-Droid. - - - - - - - - - - - - - - - - - - - - - - - - + + + + + Get it on F-Droid. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +
+
+
+ +
-
- - -
- - + diff --git a/src/vector/mobile_guide/index.ts b/src/vector/mobile_guide/index.ts index 334f758905..0a0a351eef 100644 --- a/src/vector/mobile_guide/index.ts +++ b/src/vector/mobile_guide/index.ts @@ -1,17 +1,17 @@ import { logger } from "matrix-js-sdk/src/logger"; -import { getVectorConfig } from '../getconfig'; +import { getVectorConfig } from "../getconfig"; function onBackToElementClick(): void { // Cookie should expire in 4 hours - document.cookie = 'element_mobile_redirect_to_guide=false;path=/;max-age=14400'; - window.location.href = '../'; + document.cookie = "element_mobile_redirect_to_guide=false;path=/;max-age=14400"; + window.location.href = "../"; } // NEVER pass user-controlled content to this function! Hardcoded strings only please. function renderConfigError(message: string): void { - const contactMsg = "If this is unexpected, please contact your system administrator " + - "or technical support representative."; + const contactMsg = + "If this is unexpected, please contact your system administrator " + "or technical support representative."; message = `

Error loading Element

${message}

${contactMsg}

`; const toHide = document.getElementsByClassName("mx_HomePage_container"); @@ -22,46 +22,46 @@ function renderConfigError(message: string): void { for (const e of toHide) { // We have to clear the content because .style.display='none'; doesn't work // due to an !important in the CSS. - e.innerHTML = ''; + e.innerHTML = ""; } for (const e of errorContainers) { - e.style.display = 'block'; + e.style.display = "block"; e.innerHTML = message; } } async function initPage(): Promise { - document.getElementById('back_to_element_button').onclick = onBackToElementClick; + document.getElementById("back_to_element_button").onclick = onBackToElementClick; - const config = await getVectorConfig('..'); + const config = await getVectorConfig(".."); // We manually parse the config similar to how validateServerConfig works because // calling that function pulls in roughly 4mb of JS we don't use. - const wkConfig = config['default_server_config']; // overwritten later under some conditions - const serverName = config['default_server_name']; - const defaultHsUrl = config['default_hs_url']; - const defaultIsUrl = config['default_is_url']; + const wkConfig = config["default_server_config"]; // overwritten later under some conditions + const serverName = config["default_server_name"]; + const defaultHsUrl = config["default_hs_url"]; + const defaultIsUrl = config["default_is_url"]; - const incompatibleOptions = [wkConfig, serverName, defaultHsUrl].filter(i => !!i); + const incompatibleOptions = [wkConfig, serverName, defaultHsUrl].filter((i) => !!i); if (incompatibleOptions.length > 1) { return renderConfigError( "Invalid configuration: can only specify one of default_server_config, default_server_name, " + - "or default_hs_url.", + "or default_hs_url.", ); } if (incompatibleOptions.length < 1) { return renderConfigError("Invalid configuration: no default server specified."); } - let hsUrl = ''; - let isUrl = ''; + let hsUrl = ""; + let isUrl = ""; - if (wkConfig && wkConfig['m.homeserver']) { - hsUrl = wkConfig['m.homeserver']['base_url']; + if (wkConfig && wkConfig["m.homeserver"]) { + hsUrl = wkConfig["m.homeserver"]["base_url"]; - if (wkConfig['m.identity_server']) { - isUrl = wkConfig['m.identity_server']['base_url']; + if (wkConfig["m.identity_server"]) { + isUrl = wkConfig["m.identity_server"]["base_url"]; } } @@ -70,11 +70,11 @@ async function initPage(): Promise { try { const result = await fetch(`https://${serverName}/.well-known/matrix/client`); const wkConfig = await result.json(); - if (wkConfig && wkConfig['m.homeserver']) { - hsUrl = wkConfig['m.homeserver']['base_url']; + if (wkConfig && wkConfig["m.homeserver"]) { + hsUrl = wkConfig["m.homeserver"]["base_url"]; - if (wkConfig['m.identity_server']) { - isUrl = wkConfig['m.identity_server']['base_url']; + if (wkConfig["m.identity_server"]) { + isUrl = wkConfig["m.identity_server"]["base_url"]; } } } catch (e) { @@ -92,21 +92,20 @@ async function initPage(): Promise { return renderConfigError("Unable to locate homeserver"); } - if (hsUrl && !hsUrl.endsWith('/')) hsUrl += '/'; - if (isUrl && !isUrl.endsWith('/')) isUrl += '/'; + if (hsUrl && !hsUrl.endsWith("/")) hsUrl += "/"; + if (isUrl && !isUrl.endsWith("/")) isUrl += "/"; - if (hsUrl !== 'https://matrix.org/') { - (document.getElementById('configure_element_button') as HTMLAnchorElement).href = - "https://mobile.element.io?hs_url=" + encodeURIComponent(hsUrl) + - "&is_url=" + encodeURIComponent(isUrl); - document.getElementById('step1_heading').innerHTML= '1: Install the app'; - document.getElementById('step2_container').style.display = 'block'; - document.getElementById('hs_url').innerText = hsUrl; + if (hsUrl !== "https://matrix.org/") { + (document.getElementById("configure_element_button") as HTMLAnchorElement).href = + "https://mobile.element.io?hs_url=" + encodeURIComponent(hsUrl) + "&is_url=" + encodeURIComponent(isUrl); + document.getElementById("step1_heading").innerHTML = "1: Install the app"; + document.getElementById("step2_container").style.display = "block"; + document.getElementById("hs_url").innerText = hsUrl; if (isUrl) { - document.getElementById('custom_is').style.display = 'block'; - document.getElementById('is_url').style.display = 'block'; - document.getElementById('is_url').innerText = isUrl; + document.getElementById("custom_is").style.display = "block"; + document.getElementById("is_url").style.display = "block"; + document.getElementById("is_url").innerText = isUrl; } } } diff --git a/src/vector/modernizr.js b/src/vector/modernizr.js index 0790020f34..78e4f39c3f 100644 --- a/src/vector/modernizr.js +++ b/src/vector/modernizr.js @@ -1,3 +1,423 @@ /*! modernizr 3.12.0 (Custom Build) | MIT * * https://modernizr.com/download/?-cors-cryptography-cssanimations-cssfilters-displaytable-es5date-es5function-es5object-es5undefined-es6array-es6collections-es6string-fetch-flexbox-json-localstorage-objectfit-promises-resizeobserver-sandbox-svg-svgasimg-svgfilters-urlparser-urlsearchparams !*/ -!function(e,t,n,r){function o(e,t){return typeof e===t}function i(e,t){return!!~(""+e).indexOf(t)}function s(){return"function"!=typeof n.createElement?n.createElement(arguments[0]):O?n.createElementNS.call(n,"http://www.w3.org/2000/svg",arguments[0]):n.createElement.apply(n,arguments)}function a(){var e=n.body;return e||(e=s(O?"svg":"body"),e.fake=!0),e}function l(e,t,r,o){var i,l,f,d,u="modernizr",c=s("div"),p=a();if(parseInt(r,10))for(;r--;)f=s("div"),f.id=o?o[r]:u+(r+1),c.appendChild(f);return i=s("style"),i.type="text/css",i.id="s"+u,(p.fake?p:c).appendChild(i),p.appendChild(c),i.styleSheet?i.styleSheet.cssText=e:i.appendChild(n.createTextNode(e)),c.id=u,p.fake&&(p.style.background="",p.style.overflow="hidden",d=x.style.overflow,x.style.overflow="hidden",x.appendChild(p)),l=t(c,e),p.fake&&p.parentNode?(p.parentNode.removeChild(p),x.style.overflow=d,x.offsetHeight):c.parentNode.removeChild(c),!!l}function f(e){return e.replace(/([A-Z])/g,function(e,t){return"-"+t.toLowerCase()}).replace(/^ms-/,"-ms-")}function d(e,n,r){var o;if("getComputedStyle"in t){o=getComputedStyle.call(t,e,n);var i=t.console;if(null!==o)r&&(o=o.getPropertyValue(r));else if(i){var s=i.error?"error":"log";i[s].call(i,"getComputedStyle returning null, its possible modernizr test results are inaccurate")}}else o=!n&&e.currentStyle&&e.currentStyle[r];return o}function u(e,n){var o=e.length;if("CSS"in t&&"supports"in t.CSS){for(;o--;)if(t.CSS.supports(f(e[o]),n))return!0;return!1}if("CSSSupportsRule"in t){for(var i=[];o--;)i.push("("+f(e[o])+":"+n+")");return i=i.join(" or "),l("@supports ("+i+") { #modernizr { position: absolute; } }",function(e){return"absolute"===d(e,null,"position")})}return r}function c(e){return e.replace(/([a-z])-([a-z])/g,function(e,t,n){return t+n.toUpperCase()}).replace(/^-/,"")}function p(e,t,n,a){function l(){d&&(delete P.style,delete P.modElem)}if(a=!o(a,"undefined")&&a,!o(n,"undefined")){var f=u(e,n);if(!o(f,"undefined"))return f}for(var d,p,y,m,v,g=["modernizr","tspan","samp"];!P.style&&g.length;)d=!0,P.modElem=s(g.shift()),P.style=P.modElem.style;for(y=e.length,p=0;p0&&(t+=" "+n+e.join(" "+n)),O?x.className.baseVal=t:x.className=t)}function S(e,t){if("object"==typeof e)for(var n in e)k(e,n)&&S(n,e[n]);else{e=e.toLowerCase();var r=e.split("."),o=Modernizr[r[0]];if(2===r.length&&(o=o[r[1]]),void 0!==o)return Modernizr;t="function"==typeof t?t():t,1===r.length?Modernizr[r[0]]=t:(!Modernizr[r[0]]||Modernizr[r[0]]instanceof Boolean||(Modernizr[r[0]]=new Boolean(Modernizr[r[0]])),Modernizr[r[0]][r[1]]=t),h([(t&&!1!==t?"":"no-")+r.join("-")]),Modernizr._trigger(e,t)}return Modernizr}var b=[],w={_version:"3.12.0",_config:{classPrefix:"",enableClasses:!1,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(e,t){var n=this;setTimeout(function(){t(n[e])},0)},addTest:function(e,t,n){b.push({name:e,fn:t,options:n})},addAsyncTest:function(e){b.push({name:null,fn:e})}},Modernizr=function(){};Modernizr.prototype=w,Modernizr=new Modernizr;var T=[],C="Moz O ms Webkit",_=w._config.usePrefixes?C.split(" "):[];w._cssomPrefixes=_;var x=n.documentElement,O="svg"===x.nodeName.toLowerCase(),j={elem:s("modernizr")};Modernizr._q.push(function(){delete j.elem});var P={style:j.elem.style};Modernizr._q.unshift(function(){delete P.style});var E=w._config.usePrefixes?C.toLowerCase().split(" "):[];w._domPrefixes=E,w.testAllProps=v,w.testAllProps=g,Modernizr.addTest("cssanimations",g("animationName","a",!0)),(w.testStyles=l)("#modernizr{display: table; direction: ltr}#modernizr div{display: table-cell; padding: 10px}",function(e){var t,n=e.childNodes;t=n[0].offsetLeft9)}),Modernizr.addTest("flexbox",g("flexBasis","1px",!0));var R=function(e){var n,o=z.length,i=t.CSSRule;if(void 0===i)return r;if(!e)return!1;if(e=e.replace(/^@/,""),(n=e.replace(/-/g,"_").toUpperCase()+"_RULE")in i)return"@"+e;for(var s=0;s 0 && (t += " " + n + e.join(" " + n)), O ? (x.className.baseVal = t) : (x.className = t)); + } + function S(e, t) { + if ("object" == typeof e) for (var n in e) k(e, n) && S(n, e[n]); + else { + e = e.toLowerCase(); + var r = e.split("."), + o = Modernizr[r[0]]; + if ((2 === r.length && (o = o[r[1]]), void 0 !== o)) return Modernizr; + (t = "function" == typeof t ? t() : t), + 1 === r.length + ? (Modernizr[r[0]] = t) + : (!Modernizr[r[0]] || + Modernizr[r[0]] instanceof Boolean || + (Modernizr[r[0]] = new Boolean(Modernizr[r[0]])), + (Modernizr[r[0]][r[1]] = t)), + h([(t && !1 !== t ? "" : "no-") + r.join("-")]), + Modernizr._trigger(e, t); + } + return Modernizr; + } + var b = [], + w = { + _version: "3.12.0", + _config: { classPrefix: "", enableClasses: !1, enableJSClass: !0, usePrefixes: !0 }, + _q: [], + on: function (e, t) { + var n = this; + setTimeout(function () { + t(n[e]); + }, 0); + }, + addTest: function (e, t, n) { + b.push({ name: e, fn: t, options: n }); + }, + addAsyncTest: function (e) { + b.push({ name: null, fn: e }); + }, + }, + Modernizr = function () {}; + (Modernizr.prototype = w), (Modernizr = new Modernizr()); + var T = [], + C = "Moz O ms Webkit", + _ = w._config.usePrefixes ? C.split(" ") : []; + w._cssomPrefixes = _; + var x = n.documentElement, + O = "svg" === x.nodeName.toLowerCase(), + j = { elem: s("modernizr") }; + Modernizr._q.push(function () { + delete j.elem; + }); + var P = { style: j.elem.style }; + Modernizr._q.unshift(function () { + delete P.style; + }); + var E = w._config.usePrefixes ? C.toLowerCase().split(" ") : []; + (w._domPrefixes = E), + (w.testAllProps = v), + (w.testAllProps = g), + Modernizr.addTest("cssanimations", g("animationName", "a", !0)), + (w.testStyles = l)( + "#modernizr{display: table; direction: ltr}#modernizr div{display: table-cell; padding: 10px}", + function (e) { + var t, + n = e.childNodes; + (t = n[0].offsetLeft < n[1].offsetLeft), + Modernizr.addTest("displaytable", t, { aliases: ["display-table"] }); + }, + 2, + ); + var z = w._config.usePrefixes ? " -webkit- -moz- -o- -ms- ".split(" ") : ["", ""]; + w._prefixes = z; + var A = "CSS" in t && "supports" in t.CSS, + N = "supportsCSS" in t; + Modernizr.addTest("supports", A || N), + Modernizr.addTest("cssfilters", function () { + if (Modernizr.supports) return g("filter", "blur(2px)"); + var e = s("a"); + return ( + (e.style.cssText = z.join("filter:blur(2px); ")), + !!e.style.length && (n.documentMode === r || n.documentMode > 9) + ); + }), + Modernizr.addTest("flexbox", g("flexBasis", "1px", !0)); + var R = function (e) { + var n, + o = z.length, + i = t.CSSRule; + if (void 0 === i) return r; + if (!e) return !1; + if (((e = e.replace(/^@/, "")), (n = e.replace(/-/g, "_").toUpperCase() + "_RULE") in i)) return "@" + e; + for (var s = 0; s < o; s++) { + var a = z[s]; + if (a.toUpperCase() + "_" + n in i) return "@-" + a.toLowerCase() + "-" + e; + } + return !1; + }; + w.atRule = R; + var L = (w.prefixed = function (e, t, n) { + return 0 === e.indexOf("@") ? R(e) : (-1 !== e.indexOf("-") && (e = c(e)), t ? v(e, t, n) : v(e, "pfx")); + }); + Modernizr.addTest("objectfit", !!L("objectFit"), { aliases: ["object-fit"] }), + Modernizr.addTest("es5date", function () { + var e = !1; + try { + e = !!Date.parse("2013-04-12T06:06:37.307Z"); + } catch (e) {} + return !!(Date.now && Date.prototype && Date.prototype.toISOString && Date.prototype.toJSON && e); + }), + Modernizr.addTest("es5function", function () { + return !(!Function.prototype || !Function.prototype.bind); + }), + Modernizr.addTest("es5object", function () { + return !!( + Object.keys && + Object.create && + Object.getPrototypeOf && + Object.getOwnPropertyNames && + Object.isSealed && + Object.isFrozen && + Object.isExtensible && + Object.getOwnPropertyDescriptor && + Object.defineProperty && + Object.defineProperties && + Object.seal && + Object.freeze && + Object.preventExtensions + ); + }), + Modernizr.addTest("es5undefined", function () { + var e, n; + try { + (n = t.undefined), (t.undefined = 12345), (e = void 0 === t.undefined), (t.undefined = n); + } catch (e) { + return !1; + } + return e; + }), + Modernizr.addTest( + "es6array", + !!( + Array.prototype && + Array.prototype.copyWithin && + Array.prototype.fill && + Array.prototype.find && + Array.prototype.findIndex && + Array.prototype.keys && + Array.prototype.entries && + Array.prototype.values && + Array.from && + Array.of + ), + ), + Modernizr.addTest("es6collections", !!(t.Map && t.Set && t.WeakMap && t.WeakSet)), + Modernizr.addTest("promises", function () { + return ( + "Promise" in t && + "resolve" in t.Promise && + "reject" in t.Promise && + "all" in t.Promise && + "race" in t.Promise && + (function () { + var e; + return ( + new t.Promise(function (t) { + e = t; + }), + "function" == typeof e + ); + })() + ); + }), + Modernizr.addTest( + "es6string", + !!( + String.fromCodePoint && + String.raw && + String.prototype.codePointAt && + String.prototype.repeat && + String.prototype.startsWith && + String.prototype.endsWith && + String.prototype.includes + ), + ), + Modernizr.addTest( + "svg", + !!n.createElementNS && !!n.createElementNS("http://www.w3.org/2000/svg", "svg").createSVGRect, + ); + var k; + !(function () { + var e = {}.hasOwnProperty; + k = + o(e, "undefined") || o(e.call, "undefined") + ? function (e, t) { + return t in e && o(e.constructor.prototype[t], "undefined"); + } + : function (t, n) { + return e.call(t, n); + }; + })(), + (w._l = {}), + (w.on = function (e, t) { + this._l[e] || (this._l[e] = []), + this._l[e].push(t), + Modernizr.hasOwnProperty(e) && + setTimeout(function () { + Modernizr._trigger(e, Modernizr[e]); + }, 0); + }), + (w._trigger = function (e, t) { + if (this._l[e]) { + var n = this._l[e]; + setTimeout(function () { + var e; + for (e = 0; e < n.length; e++) (0, n[e])(t); + }, 0), + delete this._l[e]; + } + }), + Modernizr._q.push(function () { + w.addTest = S; + }), + Modernizr.addTest("svgasimg", n.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image", "1.1")), + Modernizr.addTest("svgfilters", function () { + var e = !1; + try { + e = "SVGFEColorMatrixElement" in t && 2 === SVGFEColorMatrixElement.SVG_FECOLORMATRIX_TYPE_SATURATE; + } catch (e) {} + return e; + }), + Modernizr.addTest("urlparser", function () { + var e; + try { + return (e = new URL("http://modernizr.com/")), "http://modernizr.com/" === e.href; + } catch (e) { + return !1; + } + }), + Modernizr.addTest("urlsearchparams", "URLSearchParams" in t), + Modernizr.addTest("cors", "XMLHttpRequest" in t && "withCredentials" in new XMLHttpRequest()); + var M = L("crypto", t); + Modernizr.addTest("crypto", !!L("subtle", M)), + Modernizr.addTest("sandbox", "sandbox" in s("iframe")), + Modernizr.addTest("json", "JSON" in t && "parse" in JSON && "stringify" in JSON), + Modernizr.addTest("fetch", "fetch" in t), + Modernizr.addTest("localstorage", function () { + var e = "modernizr"; + try { + return localStorage.setItem(e, e), localStorage.removeItem(e), !0; + } catch (e) { + return !1; + } + }), + Modernizr.addTest("resizeobserver", "ResizeObserver" in t), + (function () { + var e, t, n, r, i, s, a; + for (var l in b) + if (b.hasOwnProperty(l)) { + if ( + ((e = []), + (t = b[l]), + t.name && + (e.push(t.name.toLowerCase()), t.options && t.options.aliases && t.options.aliases.length)) + ) + for (n = 0; n < t.options.aliases.length; n++) e.push(t.options.aliases[n].toLowerCase()); + for (r = o(t.fn, "function") ? t.fn() : t.fn, i = 0; i < e.length; i++) + (s = e[i]), + (a = s.split(".")), + 1 === a.length + ? (Modernizr[a[0]] = r) + : ((Modernizr[a[0]] && (!Modernizr[a[0]] || Modernizr[a[0]] instanceof Boolean)) || + (Modernizr[a[0]] = new Boolean(Modernizr[a[0]])), + (Modernizr[a[0]][a[1]] = r)), + T.push((r ? "" : "no-") + a.join("-")); + } + })(), + delete w.addTest, + delete w.addAsyncTest; + for (var V = 0; V < Modernizr._q.length; V++) Modernizr._q[V](); + e.Modernizr = Modernizr; +})(window, window, document); diff --git a/src/vector/platform/ElectronPlatform.tsx b/src/vector/platform/ElectronPlatform.tsx index 4b1a71e36e..36c3137ab6 100644 --- a/src/vector/platform/ElectronPlatform.tsx +++ b/src/vector/platform/ElectronPlatform.tsx @@ -19,12 +19,12 @@ limitations under the License. */ import { UpdateCheckStatus, UpdateStatus } from "matrix-react-sdk/src/BasePlatform"; -import BaseEventIndexManager from 'matrix-react-sdk/src/indexing/BaseEventIndexManager'; -import dis from 'matrix-react-sdk/src/dispatcher/dispatcher'; -import { _t } from 'matrix-react-sdk/src/languageHandler'; -import SdkConfig from 'matrix-react-sdk/src/SdkConfig'; +import BaseEventIndexManager from "matrix-react-sdk/src/indexing/BaseEventIndexManager"; +import dis from "matrix-react-sdk/src/dispatcher/dispatcher"; +import { _t } from "matrix-react-sdk/src/languageHandler"; +import SdkConfig from "matrix-react-sdk/src/SdkConfig"; import { IConfigOptions } from "matrix-react-sdk/src/IConfigOptions"; -import * as rageshake from 'matrix-react-sdk/src/rageshake/rageshake'; +import * as rageshake from "matrix-react-sdk/src/rageshake/rageshake"; import { MatrixClient } from "matrix-js-sdk/src/client"; import { Room } from "matrix-js-sdk/src/models/room"; import Modal from "matrix-react-sdk/src/Modal"; @@ -41,35 +41,35 @@ import GenericExpiringToast from "matrix-react-sdk/src/components/views/toasts/G import { logger } from "matrix-js-sdk/src/logger"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; -import VectorBasePlatform from './VectorBasePlatform'; +import VectorBasePlatform from "./VectorBasePlatform"; import { SeshatIndexManager } from "./SeshatIndexManager"; import { IPCManager } from "./IPCManager"; -const isMac = navigator.platform.toUpperCase().includes('MAC'); +const isMac = navigator.platform.toUpperCase().includes("MAC"); function platformFriendlyName(): string { // used to use window.process but the same info is available here - if (navigator.userAgent.includes('Macintosh')) { - return 'macOS'; - } else if (navigator.userAgent.includes('FreeBSD')) { - return 'FreeBSD'; - } else if (navigator.userAgent.includes('OpenBSD')) { - return 'OpenBSD'; - } else if (navigator.userAgent.includes('SunOS')) { - return 'SunOS'; - } else if (navigator.userAgent.includes('Windows')) { - return 'Windows'; - } else if (navigator.userAgent.includes('Linux')) { - return 'Linux'; + if (navigator.userAgent.includes("Macintosh")) { + return "macOS"; + } else if (navigator.userAgent.includes("FreeBSD")) { + return "FreeBSD"; + } else if (navigator.userAgent.includes("OpenBSD")) { + return "OpenBSD"; + } else if (navigator.userAgent.includes("SunOS")) { + return "SunOS"; + } else if (navigator.userAgent.includes("Windows")) { + return "Windows"; + } else if (navigator.userAgent.includes("Linux")) { + return "Linux"; } else { - return 'Unknown'; + return "Unknown"; } } function onAction(payload: ActionPayload): void { // Whitelist payload actions, no point sending most across - if (['call_state'].includes(payload.action)) { - window.electron.send('app_onAction', payload); + if (["call_state"].includes(payload.action)) { + window.electron.send("app_onAction", payload); } } @@ -102,7 +102,7 @@ export default class ElectronPlatform extends VectorBasePlatform { false if there is not or the error if one is encountered */ - window.electron.on('check_updates', (event, status) => { + window.electron.on("check_updates", (event, status) => { dis.dispatch({ action: Action.CheckUpdates, ...getUpdateCheckStatus(status), @@ -110,27 +110,27 @@ export default class ElectronPlatform extends VectorBasePlatform { }); // try to flush the rageshake logs to indexeddb before quit. - window.electron.on('before-quit', function() { - logger.log('element-desktop closing'); + window.electron.on("before-quit", function () { + logger.log("element-desktop closing"); rageshake.flush(); }); - window.electron.on('update-downloaded', this.onUpdateDownloaded); + window.electron.on("update-downloaded", this.onUpdateDownloaded); - window.electron.on('preferences', () => { + window.electron.on("preferences", () => { dis.fire(Action.ViewUserSettings); }); - window.electron.on('userDownloadCompleted', (ev, { id, name }) => { + window.electron.on("userDownloadCompleted", (ev, { id, name }) => { const key = `DOWNLOAD_TOAST_${id}`; const onAccept = (): void => { - window.electron.send('userDownloadAction', { id, open: true }); + window.electron.send("userDownloadAction", { id, open: true }); ToastStore.sharedInstance().dismissToast(key); }; const onDismiss = (): void => { - window.electron.send('userDownloadAction', { id }); + window.electron.send("userDownloadAction", { id }); }; ToastStore.sharedInstance().addOrReplaceToast({ @@ -153,7 +153,7 @@ export default class ElectronPlatform extends VectorBasePlatform { } public async getConfig(): Promise { - return this.ipc.call('getConfig'); + return this.ipc.call("getConfig"); } private onUpdateDownloaded = async (ev, { releaseNotes, releaseName }): Promise => { @@ -167,7 +167,7 @@ export default class ElectronPlatform extends VectorBasePlatform { }; public getHumanReadableName(): string { - return 'Electron Platform'; // no translation required: only used for analytics + return "Electron Platform"; // no translation required: only used for analytics } /** @@ -186,7 +186,7 @@ export default class ElectronPlatform extends VectorBasePlatform { if (this.notificationCount === count) return; super.setNotificationCount(count); - window.electron.send('setBadgeCount', count); + window.electron.send("setBadgeCount", count); } public supportsNotifications(): boolean { @@ -210,29 +210,23 @@ export default class ElectronPlatform extends VectorBasePlatform { // maybe we should pass basic styling (italics, bold, underline) through from MD // we only have to strip out < and > as the spec doesn't include anything about things like & // so we shouldn't assume that all implementations will treat those properly. Very basic tag parsing is done. - if (navigator.userAgent.includes('Linux')) { - msg = msg.replace(//g, '>'); + if (navigator.userAgent.includes("Linux")) { + msg = msg.replace(//g, ">"); } - const notification = super.displayNotification( - title, - msg, - avatarUrl, - room, - ev, - ); + const notification = super.displayNotification(title, msg, avatarUrl, room, ev); const handler = notification.onclick as Function; notification.onclick = (): void => { handler?.(); - this.ipc.call('focusWindow'); + this.ipc.call("focusWindow"); }; return notification; } public loudNotification(ev: MatrixEvent, room: Room): void { - window.electron.send('loudNotification'); + window.electron.send("loudNotification"); } public needsUrlTooltips(): boolean { @@ -240,7 +234,7 @@ export default class ElectronPlatform extends VectorBasePlatform { } public async getAppVersion(): Promise { - return this.ipc.call('getAppVersion'); + return this.ipc.call("getAppVersion"); } public supportsSetting(settingName?: string): boolean { @@ -262,32 +256,32 @@ export default class ElectronPlatform extends VectorBasePlatform { } public async canSelfUpdate(): Promise { - const feedUrl = await this.ipc.call('getUpdateFeedUrl'); + const feedUrl = await this.ipc.call("getUpdateFeedUrl"); return Boolean(feedUrl); } public startUpdateCheck(): void { super.startUpdateCheck(); - window.electron.send('check_updates'); + window.electron.send("check_updates"); } public installUpdate(): void { // IPC to the main process to install the update, since quitAndInstall // doesn't fire the before-quit event so the main process needs to know // it should exit. - window.electron.send('install_update'); + window.electron.send("install_update"); } public getDefaultDeviceDisplayName(): string { const brand = SdkConfig.get().brand; - return _t('%(brand)s Desktop: %(platformName)s', { + return _t("%(brand)s Desktop: %(platformName)s", { brand, platformName: platformFriendlyName(), }); } public requestNotificationPermission(): Promise { - return Promise.resolve('granted'); + return Promise.resolve("granted"); } public reload(): void { @@ -299,33 +293,33 @@ export default class ElectronPlatform extends VectorBasePlatform { } public async setLanguage(preferredLangs: string[]): Promise { - return this.ipc.call('setLanguage', preferredLangs); + return this.ipc.call("setLanguage", preferredLangs); } public setSpellCheckEnabled(enabled: boolean): void { - this.ipc.call('setSpellCheckEnabled', enabled).catch(error => { + this.ipc.call("setSpellCheckEnabled", enabled).catch((error) => { logger.log("Failed to send setSpellCheckEnabled IPC to Electron"); logger.error(error); }); } public async getSpellCheckEnabled(): Promise { - return this.ipc.call('getSpellCheckEnabled'); + return this.ipc.call("getSpellCheckEnabled"); } public setSpellCheckLanguages(preferredLangs: string[]): void { - this.ipc.call('setSpellCheckLanguages', preferredLangs).catch(error => { + this.ipc.call("setSpellCheckLanguages", preferredLangs).catch((error) => { logger.log("Failed to send setSpellCheckLanguages IPC to Electron"); logger.error(error); }); } public async getSpellCheckLanguages(): Promise { - return this.ipc.call('getSpellCheckLanguages'); + return this.ipc.call("getSpellCheckLanguages"); } public async getDesktopCapturerSources(options: GetSourcesOptions): Promise> { - return this.ipc.call('getDesktopCapturerSources', options); + return this.ipc.call("getDesktopCapturerSources", options); } public supportsDesktopCapturer(): boolean { @@ -338,7 +332,7 @@ export default class ElectronPlatform extends VectorBasePlatform { } public async getAvailableSpellCheckLanguages(): Promise { - return this.ipc.call('getAvailableSpellCheckLanguages'); + return this.ipc.call("getAvailableSpellCheckLanguages"); } public getSSOCallbackUrl(fragmentAfterLogin: string): URL { @@ -372,7 +366,7 @@ export default class ElectronPlatform extends VectorBasePlatform { public async getPickleKey(userId: string, deviceId: string): Promise { try { - return await this.ipc.call('getPickleKey', userId, deviceId); + return await this.ipc.call("getPickleKey", userId, deviceId); } catch (e) { // if we can't connect to the password storage, assume there's no // pickle key @@ -382,7 +376,7 @@ export default class ElectronPlatform extends VectorBasePlatform { public async createPickleKey(userId: string, deviceId: string): Promise { try { - return await this.ipc.call('createPickleKey', userId, deviceId); + return await this.ipc.call("createPickleKey", userId, deviceId); } catch (e) { // if we can't connect to the password storage, assume there's no // pickle key @@ -392,7 +386,7 @@ export default class ElectronPlatform extends VectorBasePlatform { public async destroyPickleKey(userId: string, deviceId: string): Promise { try { - await this.ipc.call('destroyPickleKey', userId, deviceId); + await this.ipc.call("destroyPickleKey", userId, deviceId); } catch (e) {} } } diff --git a/src/vector/platform/IPCManager.ts b/src/vector/platform/IPCManager.ts index 6e9abf16f2..868f528d3e 100644 --- a/src/vector/platform/IPCManager.ts +++ b/src/vector/platform/IPCManager.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { defer, IDeferred } from 'matrix-js-sdk/src/utils'; +import { defer, IDeferred } from "matrix-js-sdk/src/utils"; import { logger } from "matrix-js-sdk/src/logger"; import { ElectronChannel } from "../../@types/global"; diff --git a/src/vector/platform/PWAPlatform.ts b/src/vector/platform/PWAPlatform.ts index ea0c9cf168..3cd73d1e6c 100644 --- a/src/vector/platform/PWAPlatform.ts +++ b/src/vector/platform/PWAPlatform.ts @@ -24,7 +24,7 @@ export default class PWAPlatform extends WebPlatform { if (this.notificationCount === count) return; this.notificationCount = count; - navigator.setAppBadge(count).catch(e => { + navigator.setAppBadge(count).catch((e) => { logger.error("Failed to update PWA app badge", e); }); } diff --git a/src/vector/platform/SeshatIndexManager.ts b/src/vector/platform/SeshatIndexManager.ts index 2f08f49296..3526bcc464 100644 --- a/src/vector/platform/SeshatIndexManager.ts +++ b/src/vector/platform/SeshatIndexManager.ts @@ -19,7 +19,7 @@ import BaseEventIndexManager, { IEventAndProfile, IIndexStats, ISearchArgs, -} from 'matrix-react-sdk/src/indexing/BaseEventIndexManager'; +} from "matrix-react-sdk/src/indexing/BaseEventIndexManager"; import { IMatrixProfile, IEventWithRoomId as IMatrixEvent, IResultRoomEvents } from "matrix-js-sdk/src/@types/search"; import { IPCManager } from "./IPCManager"; @@ -28,35 +28,35 @@ export class SeshatIndexManager extends BaseEventIndexManager { private readonly ipc = new IPCManager("seshat", "seshatReply"); public async supportsEventIndexing(): Promise { - return this.ipc.call('supportsEventIndexing'); + return this.ipc.call("supportsEventIndexing"); } public async initEventIndex(userId: string, deviceId: string): Promise { - return this.ipc.call('initEventIndex', userId, deviceId); + return this.ipc.call("initEventIndex", userId, deviceId); } public async addEventToIndex(ev: IMatrixEvent, profile: IMatrixProfile): Promise { - return this.ipc.call('addEventToIndex', ev, profile); + return this.ipc.call("addEventToIndex", ev, profile); } public async deleteEvent(eventId: string): Promise { - return this.ipc.call('deleteEvent', eventId); + return this.ipc.call("deleteEvent", eventId); } public async isEventIndexEmpty(): Promise { - return this.ipc.call('isEventIndexEmpty'); + return this.ipc.call("isEventIndexEmpty"); } public async isRoomIndexed(roomId: string): Promise { - return this.ipc.call('isRoomIndexed', roomId); + return this.ipc.call("isRoomIndexed", roomId); } public async commitLiveEvents(): Promise { - return this.ipc.call('commitLiveEvents'); + return this.ipc.call("commitLiveEvents"); } public async searchEventIndex(searchConfig: ISearchArgs): Promise { - return this.ipc.call('searchEventIndex', searchConfig); + return this.ipc.call("searchEventIndex", searchConfig); } public async addHistoricEvents( @@ -64,42 +64,42 @@ export class SeshatIndexManager extends BaseEventIndexManager { checkpoint: ICrawlerCheckpoint | null, oldCheckpoint: ICrawlerCheckpoint | null, ): Promise { - return this.ipc.call('addHistoricEvents', events, checkpoint, oldCheckpoint); + return this.ipc.call("addHistoricEvents", events, checkpoint, oldCheckpoint); } public async addCrawlerCheckpoint(checkpoint: ICrawlerCheckpoint): Promise { - return this.ipc.call('addCrawlerCheckpoint', checkpoint); + return this.ipc.call("addCrawlerCheckpoint", checkpoint); } public async removeCrawlerCheckpoint(checkpoint: ICrawlerCheckpoint): Promise { - return this.ipc.call('removeCrawlerCheckpoint', checkpoint); + return this.ipc.call("removeCrawlerCheckpoint", checkpoint); } public async loadFileEvents(args): Promise { - return this.ipc.call('loadFileEvents', args); + return this.ipc.call("loadFileEvents", args); } public async loadCheckpoints(): Promise { - return this.ipc.call('loadCheckpoints'); + return this.ipc.call("loadCheckpoints"); } public async closeEventIndex(): Promise { - return this.ipc.call('closeEventIndex'); + return this.ipc.call("closeEventIndex"); } public async getStats(): Promise { - return this.ipc.call('getStats'); + return this.ipc.call("getStats"); } public async getUserVersion(): Promise { - return this.ipc.call('getUserVersion'); + return this.ipc.call("getUserVersion"); } public async setUserVersion(version: number): Promise { - return this.ipc.call('setUserVersion', version); + return this.ipc.call("setUserVersion", version); } public async deleteEventIndex(): Promise { - return this.ipc.call('deleteEventIndex'); + return this.ipc.call("deleteEventIndex"); } } diff --git a/src/vector/platform/VectorBasePlatform.ts b/src/vector/platform/VectorBasePlatform.ts index 516c76e3ab..cbb3b9eeb5 100644 --- a/src/vector/platform/VectorBasePlatform.ts +++ b/src/vector/platform/VectorBasePlatform.ts @@ -17,8 +17,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import BasePlatform from 'matrix-react-sdk/src/BasePlatform'; -import { _t } from 'matrix-react-sdk/src/languageHandler'; +import BasePlatform from "matrix-react-sdk/src/BasePlatform"; +import { _t } from "matrix-react-sdk/src/languageHandler"; import type { IConfigOptions } from "matrix-react-sdk/src/IConfigOptions"; import { getVectorConfig } from "../getconfig"; @@ -35,7 +35,7 @@ export default abstract class VectorBasePlatform extends BasePlatform { } public getHumanReadableName(): string { - return 'Vector Base Platform'; // no translation required: only used for analytics + return "Vector Base Platform"; // no translation required: only used for analytics } /** @@ -78,8 +78,7 @@ export default abstract class VectorBasePlatform extends BasePlatform { /** * Begin update polling, if applicable */ - public startUpdater(): void { - } + public startUpdater(): void {} /** * Get a sensible default display name for the diff --git a/src/vector/platform/WebPlatform.ts b/src/vector/platform/WebPlatform.ts index be5842ec46..938fc8fd88 100644 --- a/src/vector/platform/WebPlatform.ts +++ b/src/vector/platform/WebPlatform.ts @@ -17,15 +17,15 @@ limitations under the License. */ import { UpdateCheckStatus, UpdateStatus } from "matrix-react-sdk/src/BasePlatform"; -import dis from 'matrix-react-sdk/src/dispatcher/dispatcher'; -import { _t } from 'matrix-react-sdk/src/languageHandler'; +import dis from "matrix-react-sdk/src/dispatcher/dispatcher"; +import { _t } from "matrix-react-sdk/src/languageHandler"; import { hideToast as hideUpdateToast, showToast as showUpdateToast } from "matrix-react-sdk/src/toasts/UpdateToast"; import { Action } from "matrix-react-sdk/src/dispatcher/actions"; -import { CheckUpdatesPayload } from 'matrix-react-sdk/src/dispatcher/payloads/CheckUpdatesPayload'; -import UAParser from 'ua-parser-js'; +import { CheckUpdatesPayload } from "matrix-react-sdk/src/dispatcher/payloads/CheckUpdatesPayload"; +import UAParser from "ua-parser-js"; import { logger } from "matrix-js-sdk/src/logger"; -import VectorBasePlatform from './VectorBasePlatform'; +import VectorBasePlatform from "./VectorBasePlatform"; import { parseQs } from "../url_utils"; const POKE_RATE_MS = 10 * 60 * 1000; // 10 min @@ -43,13 +43,13 @@ export default class WebPlatform extends VectorBasePlatform { public constructor() { super(); // Register service worker if available on this platform - if ('serviceWorker' in navigator) { - navigator.serviceWorker.register('sw.js'); + if ("serviceWorker" in navigator) { + navigator.serviceWorker.register("sw.js"); } } public getHumanReadableName(): string { - return 'Web Platform'; // no translation required: only used for analytics + return "Web Platform"; // no translation required: only used for analytics } /** @@ -65,7 +65,7 @@ export default class WebPlatform extends VectorBasePlatform { * to display notifications. Otherwise false. */ public maySendNotifications(): boolean { - return window.Notification.permission === 'granted'; + return window.Notification.permission === "granted"; } /** @@ -79,7 +79,7 @@ export default class WebPlatform extends VectorBasePlatform { // annoyingly, the latest spec says this returns a // promise, but this is only supported in Chrome 46 // and Firefox 47, so adapt the callback API. - return new Promise(function(resolve) { + return new Promise(function (resolve) { window.Notification.requestPermission((result) => { resolve(result); }); @@ -142,30 +142,33 @@ export default class WebPlatform extends VectorBasePlatform { showUpdate: (currentVersion: string, mostRecentVersion: string) => void, showNoUpdate?: () => void, ): Promise => { - return this.getMostRecentVersion().then((mostRecentVersion) => { - const currentVersion = getNormalizedAppVersion(process.env.VERSION); + return this.getMostRecentVersion().then( + (mostRecentVersion) => { + const currentVersion = getNormalizedAppVersion(process.env.VERSION); - if (currentVersion !== mostRecentVersion) { - if (this.shouldShowUpdate(mostRecentVersion)) { - console.log("Update available to " + mostRecentVersion + ", will notify user"); - showUpdate(currentVersion, mostRecentVersion); + if (currentVersion !== mostRecentVersion) { + if (this.shouldShowUpdate(mostRecentVersion)) { + console.log("Update available to " + mostRecentVersion + ", will notify user"); + showUpdate(currentVersion, mostRecentVersion); + } else { + console.log("Update available to " + mostRecentVersion + " but won't be shown"); + } + return { status: UpdateCheckStatus.Ready }; } else { - console.log("Update available to " + mostRecentVersion + " but won't be shown"); + console.log("No update available, already on " + mostRecentVersion); + showNoUpdate?.(); } - return { status: UpdateCheckStatus.Ready }; - } else { - console.log("No update available, already on " + mostRecentVersion); - showNoUpdate?.(); - } - return { status: UpdateCheckStatus.NotAvailable }; - }, (err) => { - logger.error("Failed to poll for update", err); - return { - status: UpdateCheckStatus.Error, - detail: err.message || err.status ? err.status.toString() : 'Unknown Error', - }; - }); + return { status: UpdateCheckStatus.NotAvailable }; + }, + (err) => { + logger.error("Failed to poll for update", err); + return { + status: UpdateCheckStatus.Error, + detail: err.message || err.status ? err.status.toString() : "Unknown Error", + }; + }, + ); }; public startUpdateCheck(): void { @@ -197,7 +200,7 @@ export default class WebPlatform extends VectorBasePlatform { let osName = ua.getOS().name || "unknown OS"; // Stylise the value from the parser to match Apple's current branding. if (osName === "Mac OS") osName = "macOS"; - return _t('%(appName)s: %(browserName)s on %(osName)s', { + return _t("%(appName)s: %(browserName)s on %(osName)s", { appName, browserName, osName, diff --git a/src/vector/rageshakesetup.ts b/src/vector/rageshakesetup.ts index ce98b423f6..a5d2df4464 100644 --- a/src/vector/rageshakesetup.ts +++ b/src/vector/rageshakesetup.ts @@ -33,22 +33,26 @@ import { logger } from "matrix-js-sdk/src/logger"; export function initRageshake(): Promise { // we manually check persistence for rageshakes ourselves - const prom = rageshake.init(/*setUpPersistence=*/false); - prom.then(() => { - logger.log("Initialised rageshake."); - logger.log("To fix line numbers in Chrome: " + - "Meatball menu → Settings → Ignore list → Add /rageshake\\.js$"); + const prom = rageshake.init(/*setUpPersistence=*/ false); + prom.then( + () => { + logger.log("Initialised rageshake."); + logger.log( + "To fix line numbers in Chrome: " + "Meatball menu → Settings → Ignore list → Add /rageshake\\.js$", + ); - window.addEventListener('beforeunload', () => { - logger.log('element-web closing'); - // try to flush the logs to indexeddb - rageshake.flush(); - }); + window.addEventListener("beforeunload", () => { + logger.log("element-web closing"); + // try to flush the logs to indexeddb + rageshake.flush(); + }); - rageshake.cleanup(); - }, (err) => { - logger.error("Failed to initialise rageshake: " + err); - }); + rageshake.cleanup(); + }, + (err) => { + logger.error("Failed to initialise rageshake: " + err); + }, + ); return prom; } @@ -56,7 +60,7 @@ export function initRageshakeStore(): Promise { return rageshake.tryInitStorage(); } -window.mxSendRageshake = function(text: string, withLogs?: boolean): void { +window.mxSendRageshake = function (text: string, withLogs?: boolean): void { const url = SdkConfig.get().bug_report_endpoint_url; if (!url) { logger.error("Cannot send a rageshake - no bug_report_endpoint_url configured"); @@ -72,9 +76,12 @@ window.mxSendRageshake = function(text: string, withLogs?: boolean): void { userText: text, sendLogs: withLogs, progressCallback: logger.log.bind(console), - }).then(() => { - logger.log("Bug report sent!"); - }, (err) => { - logger.error(err); - }); + }).then( + () => { + logger.log("Bug report sent!"); + }, + (err) => { + logger.error(err); + }, + ); }; diff --git a/src/vector/routing.ts b/src/vector/routing.ts index 3388e9d76a..92b2c7a59b 100644 --- a/src/vector/routing.ts +++ b/src/vector/routing.ts @@ -24,7 +24,7 @@ import { parseQsFromFragment } from "./url_utils"; let lastLocationHashSet: string = null; -export function getScreenFromLocation(location: Location): { screen: string, params: QueryDict } { +export function getScreenFromLocation(location: Location): { screen: string; params: QueryDict } { const fragparts = parseQsFromFragment(location); return { screen: fragparts.location.substring(1), @@ -54,11 +54,12 @@ function onHashChange(): void { // so a web page can update the URL bar appropriately. export function onNewScreen(screen: string, replaceLast = false): void { logger.log("newscreen " + screen); - const hash = '#/' + screen; + const hash = "#/" + screen; lastLocationHashSet = hash; // if the new hash is a substring of the old one then we are stripping fields e.g `via` so replace history - if (screen.startsWith("room/") && + if ( + screen.startsWith("room/") && window.location.hash.includes("/$") === hash.includes("/$") && // only if both did or didn't contain event link window.location.hash.startsWith(hash) ) { @@ -73,5 +74,5 @@ export function onNewScreen(screen: string, replaceLast = false): void { } export function init(): void { - window.addEventListener('hashchange', onHashChange); + window.addEventListener("hashchange", onHashChange); } diff --git a/src/vector/static/incompatible-browser.html b/src/vector/static/incompatible-browser.html index 7fc193465f..38d3ee18af 100644 --- a/src/vector/static/incompatible-browser.html +++ b/src/vector/static/incompatible-browser.html @@ -1,10 +1,10 @@ - + - + - -
-
- -

Unsupported browser

-
-
-
-
-

Your browser can't run Element

-

Element uses many advanced browser features, some of which are not available or experimental in your current browser.

-

Please install Chrome, Firefox, - or Safari for the best experience.

+
+
+ +

Unsupported browser

+
+
+
+
+

Your browser can't run Element

+

+ Element uses many advanced browser features, some of which are not available or experimental in + your current browser. +

+

+ Please install Chrome, + Firefox, or Safari for + the best experience. +

+
-
-
-
-
-

Use Element on mobile

-

iOS (iPhone or iPad)

- - - Download on the App Store. - - - - - - - - - - - +
+
+
+

Use Element on mobile

+

iOS (iPhone or iPad)

+
+ + Download on the App Store. + + + + + + + + + + + + + + + + + d="M8.30227,10.13965 L3.56887,10.13965 L2.43215,13.4961 L0.42727,13.4961 L4.91067,1.0781 L6.99367,1.0781 L11.47707,13.4961 L9.438,13.4961 L8.30227,10.13965 Z M4.0591,8.59082 L7.8111,8.59082 L5.96149,3.14355 L5.90973,3.14355 L4.0591,8.59082 Z" + id="Shape" + > + d="M21.15969,8.96973 C21.15969,11.78321 19.65383,13.59082 17.38137,13.59082 C16.2067553,13.6522646 15.1003324,13.0370307 14.53274,12.00682 L14.48974,12.00682 L14.48974,16.4912 L12.63134,16.4912 L12.63134,4.44238 L14.4302,4.44238 L14.4302,5.94824 L14.46438,5.94824 C15.0571883,4.92448792 16.1648043,4.30951884 17.34719,4.34765 C19.645,4.34766 21.15969,6.16406 21.15969,8.96973 Z M19.24953,8.96973 C19.24953,7.13673 18.30226,5.93164 16.85695,5.93164 C15.43703,5.93164 14.48195,7.16211 14.48195,8.96973 C14.48195,10.79395 15.43703,12.01563 16.85695,12.01563 C18.30227,12.01563 19.24953,10.81934 19.24953,8.96973 Z" + id="Shape" + > + + + + + + - + + + + d="M2.82619,0.731 C3.61513656,0.674384181 4.38789226,0.974547428 4.93176129,1.54887182 C5.47563032,2.12319621 5.73328587,2.91114479 5.63381,3.69584 C5.63381,5.60209 4.60354,6.69784 2.82619,6.69784 L0.67092,6.69784 L0.67092,0.731 L2.82619,0.731 Z M1.59767,5.854 L2.72267,5.854 C3.28649194,5.88770201 3.83548652,5.66555901 4.21721851,5.24925179 C4.5989505,4.83294457 4.77277961,4.26679442 4.69044,3.708 C4.76678901,3.15152453 4.59045658,2.59004622 4.20967108,2.17713398 C3.82888557,1.76422175 3.28349567,1.5430863 2.72267,1.57421 L1.59767,1.57421 L1.59767,5.854 Z" + id="Shape" + > + d="M6.68068,4.44434 C6.60346713,3.63722505 6.99048366,2.85611193 7.67941593,2.42859737 C8.36834819,2.0010828 9.24008181,2.0010828 9.92901407,2.42859737 C10.6179463,2.85611193 11.0049629,3.63722505 10.92775,4.44434 C11.0064319,5.25232226 10.6197687,6.03495038 9.93022554,6.4633853 C9.24068243,6.89182022 8.36774757,6.89182022 7.67820446,6.4633853 C6.98866134,6.03495038 6.60199806,5.25232226 6.68068,4.44434 Z M10.01368,4.44434 C10.01368,3.46827 9.5752,2.89747 8.80568,2.89747 C8.03322,2.89747 7.59868,3.46827 7.59868,4.44435 C7.59868,5.42824 8.03325,5.99464 8.80568,5.99464 C9.57522,5.99463 10.01369,5.42432 10.01369,4.44434 L10.01368,4.44434 Z" + id="Shape" + > + + d="M18.85354,2.19482 L19.709,2.19482 L19.709,2.91015 L19.77541,2.91015 C20.0063449,2.38347794 20.5459729,2.06130747 21.11916,2.1079 C21.5629768,2.07453177 21.9978859,2.2447899 22.3010908,2.57060207 C22.6042957,2.89641424 22.7428931,3.34242376 22.67775,3.7827 L22.67775,6.6977 L21.78908,6.6977 L21.78908,4.00586 C21.78908,3.28223 21.47463,2.92236 20.8174,2.92236 C20.5163658,2.90833912 20.2242202,3.02648257 20.0175546,3.24581678 C19.810889,3.465151 19.710315,3.76380101 19.7422,4.06347 L19.7422,6.69775 L18.85353,6.69775 L18.85354,2.19482 Z" + id="Shape" + > + + d="M26.21779,4.44434 C26.1406364,3.63717225 26.527714,2.85603936 27.2166974,2.4285182 C27.9056808,2.00099704 28.7774592,2.00099704 29.4664426,2.4285182 C30.155426,2.85603936 30.5425036,3.63717225 30.46535,4.44434 C30.543932,5.25235119 30.1571979,6.03495719 29.4676205,6.46337324 C28.7780432,6.89178929 27.9050968,6.89178929 27.2155195,6.46337324 C26.5259421,6.03495719 26.139208,5.25235119 26.21779,4.44434 Z M29.55079,4.44434 C29.55079,3.46827 29.11231,2.89747 28.34279,2.89747 C27.57033,2.89747 27.13579,3.46827 27.13579,4.44435 C27.13579,5.42824 27.57036,5.99464 28.34279,5.99464 C29.11232,5.99463 29.5508,5.42432 29.5508,4.44434 L29.55079,4.44434 Z" + id="Shape" + > + d="M31.4009,5.42432 C31.4009,4.61377 32.00442,4.14649 33.0757,4.08008 L34.29543,4.00977 L34.29543,3.6211 C34.29543,3.14551 33.98098,2.87696 33.37356,2.87696 C32.87747,2.87696 32.53372,3.05909 32.43508,3.37745 L31.57473,3.37745 C31.66555,2.60401 32.39309,2.10792 33.41457,2.10792 C34.54348,2.10792 35.1802,2.66992 35.1802,3.6211 L35.1802,6.69776 L34.32473,6.69776 L34.32473,6.06495 L34.25442,6.06495 C33.9638686,6.52707633 33.4471736,6.79716323 32.90188,6.77195 C32.5196161,6.81171181 32.1383711,6.68791066 31.8523958,6.43115244 C31.5664205,6.17439423 31.4024061,5.80864331 31.4009,5.42432 Z M34.29543,5.03955 L34.29543,4.66309 L33.19582,4.7334 C32.5757,4.7749 32.29445,4.98584 32.29445,5.38281 C32.29445,5.78808 32.64601,6.02392 33.12945,6.02392 C33.4156361,6.05288986 33.7013264,5.96447505 33.9211204,5.77891559 C34.1409144,5.59335613 34.2759916,5.32654106 34.29543,5.03955 Z" + id="Shape" + > + d="M36.34816,4.44434 C36.34816,3.02149 37.07961,2.12012 38.2173,2.12012 C38.7917768,2.09365013 39.3298275,2.40147287 39.59816,2.91012 L39.66457,2.91012 L39.66457,0.437 L40.55324,0.437 L40.55324,6.69774 L39.70168,6.69774 L39.70168,5.98631 L39.63137,5.98631 C39.3427542,6.49073698 38.7980745,6.79335648 38.21731,6.77195 C37.0718,6.772 36.34816,5.87061 36.34816,4.44434 Z M37.26616,4.44434 C37.26616,5.39942 37.71636,5.97413 38.46929,5.97413 C39.21829,5.97413 39.6812,5.39113 39.6812,4.44825 C39.6812,3.50977 39.21343,2.91846 38.46929,2.91846 C37.72121,2.91846 37.26613,3.49707 37.26613,4.44434 L37.26616,4.44434 Z" + id="Shape" + > + d="M44.23,4.44434 C44.1527871,3.63722505 44.5398037,2.85611193 45.2287359,2.42859737 C45.9176682,2.0010828 46.7894018,2.0010828 47.4783341,2.42859737 C48.1672663,2.85611193 48.5542829,3.63722505 48.47707,4.44434 C48.5557519,5.25232226 48.1690887,6.03495038 47.4795455,6.4633853 C46.7900024,6.89182022 45.9170676,6.89182022 45.2275245,6.4633853 C44.5379813,6.03495038 44.1513181,5.25232226 44.23,4.44434 Z M47.563,4.44434 C47.563,3.46827 47.12452,2.89747 46.355,2.89747 C45.58254,2.89747 45.148,3.46827 45.148,4.44435 C45.148,5.42824 45.58257,5.99464 46.355,5.99464 C47.12453,5.99463 47.563,5.42432 47.563,4.44434 Z" + id="Shape" + > + d="M49.66945,2.19482 L50.52492,2.19482 L50.52492,2.91015 L50.59133,2.91015 C50.8222649,2.38347794 51.3618929,2.06130747 51.93508,2.1079 C52.3788968,2.07453177 52.8138059,2.2447899 53.1170108,2.57060207 C53.4202157,2.89641424 53.5588131,3.34242376 53.49367,3.7827 L53.49367,6.6977 L52.605,6.6977 L52.605,4.00586 C52.605,3.28223 52.29055,2.92236 51.63332,2.92236 C51.3322858,2.90833912 51.0401402,3.02648257 50.8334746,3.24581678 C50.626809,3.465151 50.526235,3.76380101 50.55812,4.06347 L50.55812,6.69775 L49.66945,6.69775 L49.66945,2.19482 Z" + id="Shape" + > + + + - - + + + +

Android

+ + + + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Get it on F-Droid. + + + + + + + + + + + d="M5.68003571,6.29863415 L5.68003571,4.42449878 L4.13843589,4.42449878 L4.13843589,3.64866951 L6.61432589,3.64866951 L6.61432589,6.64455976 C6.24995506,6.90316951 5.84820327,7.09946423 5.40907054,7.2334439 C4.96995649,7.36430642 4.50125542,7.42973768 4.00296732,7.42973768 C2.91295744,7.42973768 2.0596344,7.1119263 1.44299821,6.47630354 C0.829483333,5.83757671 0.522725893,4.94958037 0.522725893,3.81231451 C0.522725893,2.67194459 0.829483333,1.78394825 1.44299821,1.14832549 C2.0596344,0.509598659 2.91295744,0.190235244 4.00296732,0.190235244 C4.45765054,0.190235244 4.88898,0.24631878 5.29695571,0.358485854 C5.70805274,0.470652927 6.08644143,0.635785447 6.43212179,0.853883415 L6.43212179,1.85873098 C6.08332012,1.56274154 5.71271601,1.33995943 5.32030946,1.19038463 C4.92790292,1.04082854 4.51525458,0.966050488 4.08236446,0.966050488 C3.22903208,0.966050488 2.58748149,1.20440902 2.15771268,1.6811261 C1.73104649,2.15784317 1.51771339,2.86824398 1.51771339,3.81232854 C1.51771339,4.75329033 1.73104649,5.46212976 2.15771268,5.93884683 C2.58748149,6.4155639 3.22903208,6.65392244 4.08236446,6.65392244 C4.41559696,6.65392244 4.71301851,6.6258802 4.97462911,6.56979573 C5.2362397,6.51059598 5.47136589,6.4202387 5.68000768,6.2987239 L5.68003571,6.29863415 Z" + id="Shape" + > + + + + - + d="M37.8398036,0.956463415 C37.1546481,0.956463415 36.6096432,1.21195976 36.2047887,1.72295244 C35.803037,2.23394512 35.6021611,2.93032154 35.6021611,3.81208171 C35.6021611,4.69071911 35.803037,5.38553415 36.2047887,5.89652683 C36.6096432,6.40751951 37.1546481,6.66301585 37.8398036,6.66301585 C38.524959,6.66301585 39.066852,6.40751951 39.4654825,5.89652683 C39.8672343,5.38553415 40.0681102,4.69071911 40.0681102,3.81208171 C40.0681102,2.93032154 39.8672343,2.23394512 39.4654825,1.72295244 C39.0673754,1.21209065 38.5253515,0.956659756 37.8394111,0.956659756 L37.8398036,0.956463415 Z M37.8398036,0.18997439 C38.817708,0.18997439 39.5993998,0.518687398 40.1848789,1.17611341 C40.7703768,1.83043537 41.0631257,2.70908211 41.0631257,3.81205366 C41.0631257,4.91192114 40.7703768,5.79056789 40.1848789,6.4479939 C39.5993811,7.10231585 38.8176893,7.42947683 37.8398036,7.42947683 C36.8587965,7.42947683 36.0739835,7.10231585 35.4853643,6.4479939 C34.8998664,5.79369065 34.6071175,4.9150439 34.6071175,3.81205366 C34.6071175,2.70906341 34.8998664,1.83041667 35.4853643,1.17611341 C36.0739648,0.518687398 36.8587779,0.18997439 37.8398036,0.18997439 Z" + id="Shape" + > + - + d="M14.4888571,14.6357134 L14.4888571,19.7020244 L13.8851641,19.7020244 C13.7044646,18.9049341 13.5319795,18.3009504 13.3677089,17.8900732 C13.2034365,17.4791959 12.981674,17.1176285 12.7024214,16.8053707 C11.9303365,15.9260976 10.3984651,15.486461 8.10680714,15.486461 C7.46615369,15.486461 7.03493637,15.5686346 6.81315518,15.7329817 C6.5995978,15.8891143 6.49281911,16.201372 6.49281911,16.6697549 L6.49281911,22.5369988 C7.74949196,22.5369988 8.64887768,22.269937 9.19097625,21.7358134 C9.73307482,21.2016898 10.1191173,20.2114744 10.3491036,18.7651671 L10.97744,18.7651671 L10.97744,27.3071427 L10.3491036,27.3071427 C10.1684059,25.8444362 9.80290431,24.8336516 9.25259875,24.274789 C8.70229506,23.7077923 7.78236851,23.4119618 6.49281911,23.3872976 L6.49281911,29.2668829 C6.49281911,29.9653724 6.66530417,30.4296545 7.01027429,30.6597293 C7.3552444,30.8815951 8.10678845,31.0171829 9.26490643,31.0664927 L9.26490643,31.695122 L0.184699286,31.695122 L0.184699286,31.0664927 C1.12105476,30.9678844 1.72475714,30.8076417 1.99580643,30.5857646 C2.26685571,30.3638988 2.40238036,29.9242622 2.40238036,29.2668549 L2.40238036,17.051611 C2.40238036,16.4024313 2.25042679,15.9545858 1.94651964,15.7080744 C1.6426125,15.4615443 1.05533905,15.3136337 0.184699286,15.2643427 L0.184699286,14.6357134 L14.4888571,14.6357134 Z" + id="Shape" + > + + d="M25.1956964,29.4035366 L25.1956964,16.9540854 C25.1956964,16.378861 25.0478548,15.9720976 24.7521714,15.7337951 C24.4564881,15.4954927 23.9061751,15.3393638 23.1012323,15.2654085 L23.1012323,14.6367793 L30.7891859,14.6367793 C33.7050871,14.6367793 35.9926145,15.3804553 37.651768,16.8678073 C39.3191267,18.3469504 40.1528061,20.3848813 40.1528061,22.9816 C40.1528061,25.5948114 39.3191267,27.7025837 37.651768,29.3049171 C35.9844093,30.8990976 33.7913623,31.6961878 31.072627,31.6961878 L23.1012323,31.6961878 L23.1012323,31.0675585 C23.8322355,31.0429074 24.3620171,30.8991013 24.690577,30.6361402 C25.027342,30.3731923 25.1957245,29.9623244 25.1957245,29.4035366 L25.1956964,29.4035366 Z M29.2737714,16.374878 L29.2737714,29.3541707 C29.2737714,29.9211675 29.3764382,30.3073898 29.5817718,30.5128378 C29.7871054,30.7100581 30.1731478,30.8086683 30.7398991,30.8086683 C32.3497659,30.8086683 33.585954,30.2293394 34.4484634,29.0706817 C34.8591492,28.503685 35.179476,27.7107085 35.4094436,26.6917524 C35.647635,25.6727963 35.7667307,24.5634671 35.7667307,23.3637646 C35.7667307,21.194285 35.4217606,19.4275858 34.7318204,18.0636671 C33.8776095,16.3462215 32.5552583,15.4874988 30.7647668,15.4874988 C29.7709194,15.4874988 29.2739957,15.7833293 29.2739957,16.3749902 L29.2737714,16.374878 Z" + id="Shape" + > + d="M46.4748036,20.0576829 L46.4748036,22.1284683 C47.0661889,21.2820683 47.6247164,20.6739801 48.1503861,20.3042037 C48.6842608,19.9344272 49.2633198,19.749539 49.887563,19.749539 C50.4789484,19.749539 50.9676576,19.9385411 51.3536907,20.3165451 C51.7397238,20.6863215 51.9327404,21.1547175 51.9327404,21.7217329 C51.9327404,22.2147744 51.7848987,22.6215378 51.4892154,22.9420232 C51.193532,23.2542809 50.8116015,23.4104098 50.3434237,23.4104098 C50.0148826,23.4104098 49.7397307,23.3446709 49.5179682,23.2131932 C49.2962057,23.0817136 49.0662287,22.8475156 48.8280373,22.5105993 C48.556988,22.1161586 48.3228992,21.9189383 48.1257707,21.9189383 C47.7068798,21.9189383 47.3454807,22.1572407 47.0415736,22.6338456 C46.7376664,23.1022415 46.5857129,23.6651338 46.5857129,24.3225224 L46.5857129,29.0927785 C46.5857129,29.8734322 46.6965941,30.3911285 46.9183566,30.6458676 C47.1483429,30.8923976 47.6493692,31.0403082 48.4214354,31.0895993 L48.4214354,31.6935737 L41.706879,31.6935737 L41.7068818,31.0895993 C42.2654093,30.990991 42.6309109,30.8430804 42.8033866,30.6458676 C42.9840843,30.4486472 43.0744331,30.0911936 43.0744331,29.5735066 L43.0744331,22.1532017 C43.0744331,21.6437237 42.9799808,21.28627 42.7910761,21.0808407 C42.6103766,20.8754115 42.2489776,20.7357192 41.706879,20.6617639 L41.706879,20.0577895 L46.4749129,20.0577895 L46.4748036,20.0576829 Z" + id="Shape" + > + d="M58.2021429,19.7519512 C59.8284386,19.7519512 61.1795731,20.3394984 62.2555464,21.5145927 C63.3397436,22.6814593 63.8818421,24.1482236 63.8818421,25.9148854 C63.8818421,27.673413 63.3397436,29.1361569 62.2555464,30.3031171 C61.1795731,31.4699837 59.8284386,32.0534171 58.2021429,32.0534171 C56.5512131,32.0534171 55.1877429,31.4699837 54.1117321,30.3031171 C53.0357588,29.1362504 52.4977721,27.6489171 52.4977721,25.8411171 C52.4977721,24.1236715 53.0480852,22.6814967 54.1487113,21.5145927 C55.2493373,20.3394984 56.6004718,19.7519512 58.2021148,19.7519512 L58.2021429,19.7519512 Z M58.2267862,20.5408232 C57.6929115,20.5408232 57.282235,20.7174931 56.9947568,21.0708329 C56.7155024,21.4159638 56.5142714,21.9952927 56.3910637,22.8088195 C56.2678599,23.6223463 56.2062579,24.7851553 56.2062579,26.2972463 C56.2062579,28.1461659 56.349997,29.4363163 56.6374752,30.1676976 C56.9331586,30.8990415 57.4547257,31.2647134 58.2021765,31.2647134 C58.9331797,31.2647134 59.4424204,30.8826049 59.7298986,30.1183878 C60.025582,29.3541707 60.1734236,28.0024065 60.1734236,26.0630951 C60.1734236,23.9923472 60.0296845,22.5542862 59.7422063,21.7489122 C59.4629519,20.943613 58.9578138,20.5409634 58.2267919,20.5409634 L58.2267862,20.5408232 Z" + id="Shape" + > + d="M69.7332321,20.0576829 L69.7332321,29.6472805 C69.7332321,30.1238854 69.8194747,30.4690163 69.9919597,30.6826732 C70.1644448,30.8881024 70.4724545,31.0236902 70.9159888,31.0894366 L70.9159888,31.693411 L64.891383,31.693411 L64.8913942,31.0894366 C65.4663506,30.9497406 65.8318522,30.7936117 65.987899,30.62105 C66.1439589,30.4402663 66.2219888,30.0910215 66.2219888,29.5733159 L66.2219888,22.153011 C66.2219888,21.6353053 66.1398526,21.2860606 65.9755801,21.1052768 C65.8113077,20.9244931 65.4499086,20.7765825 64.891383,20.6615451 L64.891383,20.0575707 L69.7334312,20.0575707 L69.7332321,20.0576829 Z M66.5792143,14.8313537 C66.9652474,14.4451407 67.4293132,14.2520341 67.9714118,14.2520341 C68.5135104,14.2520341 68.9693617,14.4451407 69.3389659,14.8313537 C69.7167939,15.2175667 69.9057079,15.6818488 69.9057079,16.2242 C69.9057079,16.7747602 69.7167939,17.2349378 69.3389659,17.6047329 C68.9693617,17.9745093 68.5052959,18.1593976 67.9467684,18.1593976 C67.3882409,18.1593976 66.9241751,17.9745093 66.5545709,17.6047329 C66.1849667,17.2349565 66.0001646,16.7665606 66.0001646,16.1995451 C66.0001646,15.6654215 66.1931812,15.2093577 66.5792143,14.8313537 Z" + id="Shape" + > - - - + d="M80.0335536,32.0289024 L80.0335536,30.3895354 C79.4421682,31.0140508 78.9041815,31.4495736 78.4195936,31.6961037 C77.9349869,31.9344061 77.3559279,32.0535573 76.6824166,32.0535573 C75.360028,32.0535573 74.2676632,31.4947695 73.405322,30.3771939 C72.542906,29.2514093 72.111698,27.8298037 72.111698,26.1123768 C72.111698,24.2881028 72.5716613,22.7719728 73.4915879,21.5639866 C74.4115144,20.3560191 75.5655579,19.7520354 76.9537182,19.7520354 C77.5286746,19.7520354 78.0379154,19.8670821 78.4814404,20.0971756 C78.9249654,20.3272691 79.4177771,20.7340325 79.9598757,21.3174659 L79.9598757,16.6953073 C79.9598757,16.1200829 79.8489945,15.7502972 79.627232,15.58595 C79.4136746,15.421601 78.883893,15.3065542 78.0378873,15.2408098 L78.0378873,14.6368354 L83.4712087,14.6368354 L83.4712087,29.1447866 C83.4712087,29.6789102 83.5738755,30.0486959 83.7792091,30.2541439 C83.9927665,30.4595732 84.3870234,30.5787244 84.9619798,30.6115976 L84.9619798,31.1909171 L83.3972786,31.3881337 C82.1734636,31.5196133 81.0523154,31.7332702 80.0338339,32.0291044 L80.0335536,32.0289024 Z M79.9596318,28.8734146 L79.9596318,22.734939 C79.7789323,22.2583341 79.507883,21.8803301 79.1464839,21.6009268 C78.7933087,21.3133146 78.4072663,21.1695085 77.9883566,21.1695085 C77.2573534,21.1695085 76.7152548,21.5557309 76.3620609,22.3281756 C76.0170908,23.0923927 75.8446057,24.2880187 75.8446057,25.9150537 C75.8446057,27.5174431 76.0047738,28.7048415 76.3251098,29.4772488 C76.6454459,30.2496935 77.1423696,30.6359159 77.8158809,30.6359159 C78.3744084,30.6359159 78.90419,30.3483037 79.4052256,29.7730793 C79.553071,29.6087302 79.6803811,29.4361648 79.7871561,29.2553829 C79.9021474,29.0746011 79.959643,28.9472315 79.959643,28.8732744 L79.9596318,28.8734146 Z" + id="Shape" + > + + + + + + + + + + + + + + + + + + + + + + + - - - -

Android

- - - - Created with Sketch. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Get it on F-Droid. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +
+
- -
- diff --git a/src/vector/static/unable-to-load.html b/src/vector/static/unable-to-load.html index 7e6af16c5b..5cb007a1a7 100644 --- a/src/vector/static/unable-to-load.html +++ b/src/vector/static/unable-to-load.html @@ -1,11 +1,10 @@ - - + - - + - -
- -
- -
-
- -

Unable to load

+
+
-
-
-
-

Element can't load

-

Something went wrong and Element was unable to load.

+ +
+
+ +

Unable to load

+
+
+
+
+

Element can't load

+

Something went wrong and Element was unable to load.

+
+
- -
- diff --git a/src/vector/url_utils.ts b/src/vector/url_utils.ts index 718fc15273..f4b09eed41 100644 --- a/src/vector/url_utils.ts +++ b/src/vector/url_utils.ts @@ -19,7 +19,7 @@ import { QueryDict, decodeParams } from "matrix-js-sdk/src/utils"; // We want to support some name / value pairs in the fragment // so we're re-using query string like format // -export function parseQsFromFragment(location: Location): { location: string, params: QueryDict } { +export function parseQsFromFragment(location: Location): { location: string; params: QueryDict } { // if we have a fragment, it will start with '#', which we need to drop. // (if we don't, this will return ''). const fragment = location.hash.substring(1); @@ -27,7 +27,7 @@ export function parseQsFromFragment(location: Location): { location: string, par // our fragment may contain a query-param-like section. we need to fish // this out *before* URI-decoding because the params may contain ? and & // characters which are only URI-encoded once. - const hashparts = fragment.split('?'); + const hashparts = fragment.split("?"); const result = { location: decodeURIComponent(hashparts[0]), diff --git a/test/app-tests/loading-test.tsx b/test/app-tests/loading-test.tsx index 59a5a84cb4..be705f79ad 100644 --- a/test/app-tests/loading-test.tsx +++ b/test/app-tests/loading-test.tsx @@ -18,27 +18,27 @@ limitations under the License. /* loading.js: test the myriad paths we have for loading the application */ import "fake-indexeddb/auto"; -import React from 'react'; +import React from "react"; import { render, screen, fireEvent, waitFor, RenderResult, waitForElementToBeRemoved } from "@testing-library/react"; -import PlatformPeg from 'matrix-react-sdk/src/PlatformPeg'; -import { MatrixClientPeg } from 'matrix-react-sdk/src/MatrixClientPeg'; -import MatrixChat from 'matrix-react-sdk/src/components/structures/MatrixChat'; -import dis from 'matrix-react-sdk/src/dispatcher/dispatcher'; -import MockHttpBackend from 'matrix-mock-request'; +import PlatformPeg from "matrix-react-sdk/src/PlatformPeg"; +import { MatrixClientPeg } from "matrix-react-sdk/src/MatrixClientPeg"; +import MatrixChat from "matrix-react-sdk/src/components/structures/MatrixChat"; +import dis from "matrix-react-sdk/src/dispatcher/dispatcher"; +import MockHttpBackend from "matrix-mock-request"; import { makeType } from "matrix-react-sdk/src/utils/TypeUtils"; -import { ValidatedServerConfig } from 'matrix-react-sdk/src/utils/ValidatedServerConfig'; +import { ValidatedServerConfig } from "matrix-react-sdk/src/utils/ValidatedServerConfig"; import { IndexedDBCryptoStore } from "matrix-js-sdk/src/crypto/store/indexeddb-crypto-store"; import { QueryDict, sleep } from "matrix-js-sdk/src/utils"; import "../jest-mocks"; -import WebPlatform from '../../src/vector/platform/WebPlatform'; -import { parseQs, parseQsFromFragment } from '../../src/vector/url_utils'; +import WebPlatform from "../../src/vector/platform/WebPlatform"; +import { parseQs, parseQsFromFragment } from "../../src/vector/url_utils"; import { cleanLocalstorage, deleteIndexedDB } from "../test-utils"; -const DEFAULT_HS_URL = 'http://my_server'; -const DEFAULT_IS_URL = 'http://my_is'; +const DEFAULT_HS_URL = "http://my_server"; +const DEFAULT_IS_URL = "http://my_is"; -describe('loading:', function() { +describe("loading:", function () { let parentDiv; let httpBackend; @@ -51,10 +51,10 @@ describe('loading:', function() { // a promise which resolves when the MatrixChat calls onTokenLoginCompleted let tokenLoginCompletePromise; - beforeEach(function() { + beforeEach(function () { httpBackend = new MockHttpBackend(); window.fetch = httpBackend.fetchFn; - parentDiv = document.createElement('div'); + parentDiv = document.createElement("div"); // uncomment this to actually add the div to the UI, to help with // debugging (but slow things down) @@ -64,17 +64,14 @@ describe('loading:', function() { matrixChat = null; }); - afterEach(async function() { + afterEach(async function () { console.log(`${Date.now()}: loading: afterEach`); matrixChat?.unmount(); // unmounting should have cleared the MatrixClientPeg expect(MatrixClientPeg.get()).toBe(null); // clear the indexeddbs so we can start from a clean slate next time. - await Promise.all([ - deleteIndexedDB('matrix-js-sdk:crypto'), - deleteIndexedDB('matrix-js-sdk:riot-web-sync'), - ]); + await Promise.all([deleteIndexedDB("matrix-js-sdk:crypto"), deleteIndexedDB("matrix-js-sdk:riot-web-sync")]); cleanLocalstorage(); console.log(`${Date.now()}: loading: afterEach complete`); }); @@ -92,19 +89,21 @@ describe('loading:', function() { windowLocation = { search: queryString, hash: uriFragment, - toString: function(): string { return this.search + this.hash; }, + toString: function (): string { + return this.search + this.hash; + }, }; function onNewScreen(screen): void { - console.log(Date.now() + " newscreen "+screen); - const hash = '#/' + screen; + console.log(Date.now() + " newscreen " + screen); + const hash = "#/" + screen; windowLocation.hash = hash; - console.log(Date.now() + " browser URI now "+ windowLocation); + console.log(Date.now() + " browser URI now " + windowLocation); } // Parse the given window.location and return parameters that can be used when calling // MatrixChat.showScreen(screen, params) - function getScreenFromLocation(location): { screen: string, params: QueryDict } { + function getScreenFromLocation(location): { screen: string; params: QueryDict } { const fragparts = parseQsFromFragment(location); return { screen: fragparts.location.substring(1), @@ -114,25 +113,28 @@ describe('loading:', function() { const fragParts = parseQsFromFragment(windowLocation); - const config = Object.assign({ - default_hs_url: DEFAULT_HS_URL, - default_is_url: DEFAULT_IS_URL, - validated_server_config: makeType(ValidatedServerConfig, { - hsUrl: DEFAULT_HS_URL, - hsName: "TEST_ENVIRONMENT", - hsNameIsDifferent: false, // yes, we lie - isUrl: DEFAULT_IS_URL, - }), - embeddedPages: { - homeUrl: 'data:text/html;charset=utf-8;base64,PGh0bWw+PC9odG1sPg==', + const config = Object.assign( + { + default_hs_url: DEFAULT_HS_URL, + default_is_url: DEFAULT_IS_URL, + validated_server_config: makeType(ValidatedServerConfig, { + hsUrl: DEFAULT_HS_URL, + hsName: "TEST_ENVIRONMENT", + hsNameIsDifferent: false, // yes, we lie + isUrl: DEFAULT_IS_URL, + }), + embeddedPages: { + homeUrl: "data:text/html;charset=utf-8;base64,PGh0bWw+PC9odG1sPg==", + }, }, - }, opts.config || {}); + opts.config || {}, + ); PlatformPeg.set(new WebPlatform()); const params = parseQs(windowLocation); - tokenLoginCompletePromise = new Promise(resolve => { + tokenLoginCompletePromise = new Promise((resolve) => { matrixChat = render( {throw new Error('Not implemented');}} - />, parentDiv, + makeRegistrationUrl={(): string => { + throw new Error("Not implemented"); + }} + />, + parentDiv, ); }); } @@ -155,21 +160,23 @@ describe('loading:', function() { // returns a promise resolving to the received request async function expectAndAwaitSync(opts?): Promise { let syncRequest = null; - httpBackend.when('GET', '/_matrix/client/versions') - .respond(200, { - "versions": ["r0.3.0"], - "unstable_features": { - "m.lazy_load_members": true, - }, - }); + httpBackend.when("GET", "/_matrix/client/versions").respond(200, { + versions: ["r0.3.0"], + unstable_features: { + "m.lazy_load_members": true, + }, + }); const isGuest = opts && opts.isGuest; if (!isGuest) { // the call to create the LL filter - httpBackend.when('POST', '/filter').respond(200, { filter_id: 'llfid' }); - httpBackend.when('GET', '/pushrules').respond(200, {}); + httpBackend.when("POST", "/filter").respond(200, { filter_id: "llfid" }); + httpBackend.when("GET", "/pushrules").respond(200, {}); } - httpBackend.when('GET', '/sync') - .check((r) => {syncRequest = r;}) + httpBackend + .when("GET", "/sync") + .check((r) => { + syncRequest = r; + }) .respond(200, {}); for (let attempts = 10; attempts > 0; attempts--) { @@ -182,29 +189,35 @@ describe('loading:', function() { throw new Error("Gave up waiting for /sync"); } - describe("Clean load with no stored credentials:", function() { - it('gives a welcome page by default', function() { + describe("Clean load with no stored credentials:", function () { + it("gives a welcome page by default", function () { loadApp(); - return sleep(1).then(async () => { - // at this point, we're trying to do a guest registration; - // we expect a spinner - await assertAtLoadingSpinner(); + return sleep(1) + .then(async () => { + // at this point, we're trying to do a guest registration; + // we expect a spinner + await assertAtLoadingSpinner(); - httpBackend.when('POST', '/register').check(function(req) { - expect(req.queryParams.kind).toEqual('guest'); - }).respond(403, "Guest access is disabled"); + httpBackend + .when("POST", "/register") + .check(function (req) { + expect(req.queryParams.kind).toEqual("guest"); + }) + .respond(403, "Guest access is disabled"); - return httpBackend.flush(); - }).then(() => { - // Wait for another trip around the event loop for the UI to update - return awaitWelcomeComponent(matrixChat); - }).then(() => { - return waitFor(() => expect(windowLocation.hash).toEqual("#/welcome")); - }); + return httpBackend.flush(); + }) + .then(() => { + // Wait for another trip around the event loop for the UI to update + return awaitWelcomeComponent(matrixChat); + }) + .then(() => { + return waitFor(() => expect(windowLocation.hash).toEqual("#/welcome")); + }); }); - it('should follow the original link after successful login', function() { + it("should follow the original link after successful login", function () { loadApp({ uriFragment: "#/room/!room:id", }); @@ -213,39 +226,48 @@ describe('loading:', function() { httpBackend.when("GET", "/versions").respond(200, { versions: ["r0.4.0"] }); httpBackend.when("GET", "/api/v1").respond(200, {}); - return sleep(1).then(async () => { - // at this point, we're trying to do a guest registration; - // we expect a spinner - await assertAtLoadingSpinner(); + return sleep(1) + .then(async () => { + // at this point, we're trying to do a guest registration; + // we expect a spinner + await assertAtLoadingSpinner(); - httpBackend.when('POST', '/register').check(function(req) { - expect(req.queryParams.kind).toEqual('guest'); - }).respond(403, "Guest access is disabled"); + httpBackend + .when("POST", "/register") + .check(function (req) { + expect(req.queryParams.kind).toEqual("guest"); + }) + .respond(403, "Guest access is disabled"); - return httpBackend.flush(); - }).then(() => { - // Wait for another trip around the event loop for the UI to update - return sleep(10); - }).then(() => { - return moveFromWelcomeToLogin(matrixChat); - }).then(() => { - return completeLogin(matrixChat); - }).then(() => { - // once the sync completes, we should have a room view - return awaitRoomView(matrixChat); - }).then(() => { - httpBackend.verifyNoOutstandingExpectation(); - expect(windowLocation.hash).toEqual("#/room/!room:id"); + return httpBackend.flush(); + }) + .then(() => { + // Wait for another trip around the event loop for the UI to update + return sleep(10); + }) + .then(() => { + return moveFromWelcomeToLogin(matrixChat); + }) + .then(() => { + return completeLogin(matrixChat); + }) + .then(() => { + // once the sync completes, we should have a room view + return awaitRoomView(matrixChat); + }) + .then(() => { + httpBackend.verifyNoOutstandingExpectation(); + expect(windowLocation.hash).toEqual("#/room/!room:id"); - // and the localstorage should have been updated - expect(localStorage.getItem('mx_user_id')).toEqual('@user:id'); - expect(localStorage.getItem('mx_access_token')).toEqual('access_token'); - expect(localStorage.getItem('mx_hs_url')).toEqual(DEFAULT_HS_URL); - expect(localStorage.getItem('mx_is_url')).toEqual(DEFAULT_IS_URL); - }); + // and the localstorage should have been updated + expect(localStorage.getItem("mx_user_id")).toEqual("@user:id"); + expect(localStorage.getItem("mx_access_token")).toEqual("access_token"); + expect(localStorage.getItem("mx_hs_url")).toEqual(DEFAULT_HS_URL); + expect(localStorage.getItem("mx_is_url")).toEqual(DEFAULT_IS_URL); + }); }); - it.skip('should not register as a guest when using a #/login link', function() { + it.skip("should not register as a guest when using a #/login link", function () { loadApp({ uriFragment: "#/login", }); @@ -254,37 +276,35 @@ describe('loading:', function() { httpBackend.when("GET", "/versions").respond(200, { versions: ["r0.4.0"] }); httpBackend.when("GET", "/api/v1").respond(200, {}); - return awaitLoginComponent(matrixChat).then(async () => { - await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading...")); - // we expect a single component - await screen.findByRole("main"); - screen.getAllByText("Sign in"); + return awaitLoginComponent(matrixChat) + .then(async () => { + await waitForElementToBeRemoved(() => screen.queryAllByLabelText("Loading...")); + // we expect a single component + await screen.findByRole("main"); + screen.getAllByText("Sign in"); - // the only outstanding request should be a GET /login - // (in particular there should be no /register request for - // guest registration). - const allowedRequests = [ - "/_matrix/client/r0/login", - "/versions", - "/api/v1", - ]; - for (const req of httpBackend.requests) { - if (req.method === 'GET' && allowedRequests.find(p => req.path.endsWith(p))) { - continue; + // the only outstanding request should be a GET /login + // (in particular there should be no /register request for + // guest registration). + const allowedRequests = ["/_matrix/client/r0/login", "/versions", "/api/v1"]; + for (const req of httpBackend.requests) { + if (req.method === "GET" && allowedRequests.find((p) => req.path.endsWith(p))) { + continue; + } + + throw new Error(`Unexpected HTTP request to ${req}`); } - - throw new Error(`Unexpected HTTP request to ${req}`); - } - return completeLogin(matrixChat); - }).then(() => { - expect(matrixChat.container.querySelector(".mx_HomePage")).toBeTruthy(); - expect(windowLocation.hash).toEqual("#/home"); - }); + return completeLogin(matrixChat); + }) + .then(() => { + expect(matrixChat.container.querySelector(".mx_HomePage")).toBeTruthy(); + expect(windowLocation.hash).toEqual("#/home"); + }); }); }); - describe("MatrixClient rehydrated from stored credentials:", function() { - beforeEach(async function() { + describe("MatrixClient rehydrated from stored credentials:", function () { + beforeEach(async function () { localStorage.setItem("mx_hs_url", "http://localhost"); localStorage.setItem("mx_is_url", "http://localhost"); localStorage.setItem("mx_access_token", "access_token"); @@ -292,63 +312,68 @@ describe('loading:', function() { localStorage.setItem("mx_last_room_id", "!last_room:id"); // Create a crypto store as well to satisfy storage consistency checks - const cryptoStore = new IndexedDBCryptoStore( - indexedDB, - "matrix-js-sdk:crypto", - ); + const cryptoStore = new IndexedDBCryptoStore(indexedDB, "matrix-js-sdk:crypto"); await cryptoStore.startup(); }); - it('shows the last known room by default', function() { + it("shows the last known room by default", function () { loadApp(); - return awaitLoggedIn(matrixChat).then(() => { - // we are logged in - let the sync complete - return expectAndAwaitSync(); - }).then(() => { - // once the sync completes, we should have a room view - return awaitRoomView(matrixChat); - }).then(() => { - httpBackend.verifyNoOutstandingExpectation(); - expect(windowLocation.hash).toEqual("#/room/!last_room:id"); - }); + return awaitLoggedIn(matrixChat) + .then(() => { + // we are logged in - let the sync complete + return expectAndAwaitSync(); + }) + .then(() => { + // once the sync completes, we should have a room view + return awaitRoomView(matrixChat); + }) + .then(() => { + httpBackend.verifyNoOutstandingExpectation(); + expect(windowLocation.hash).toEqual("#/room/!last_room:id"); + }); }); - it('shows a home page by default if we have no joined rooms', function() { + it("shows a home page by default if we have no joined rooms", function () { localStorage.removeItem("mx_last_room_id"); loadApp(); - return awaitLoggedIn(matrixChat).then(() => { - // we are logged in - let the sync complete - return expectAndAwaitSync(); - }).then(() => { - // once the sync completes, we should have a home page - httpBackend.verifyNoOutstandingExpectation(); - expect(matrixChat.container.querySelector(".mx_HomePage")).toBeTruthy(); - expect(windowLocation.hash).toEqual("#/home"); - }); + return awaitLoggedIn(matrixChat) + .then(() => { + // we are logged in - let the sync complete + return expectAndAwaitSync(); + }) + .then(() => { + // once the sync completes, we should have a home page + httpBackend.verifyNoOutstandingExpectation(); + expect(matrixChat.container.querySelector(".mx_HomePage")).toBeTruthy(); + expect(windowLocation.hash).toEqual("#/home"); + }); }); - it('shows a room view if we followed a room link', function() { + it("shows a room view if we followed a room link", function () { loadApp({ uriFragment: "#/room/!room:id", }); - return awaitLoggedIn(matrixChat).then(() => { - // we are logged in - let the sync complete - return expectAndAwaitSync(); - }).then(() => { - // once the sync completes, we should have a room view - return awaitRoomView(matrixChat); - }).then(() => { - httpBackend.verifyNoOutstandingExpectation(); - expect(windowLocation.hash).toEqual("#/room/!room:id"); - }); + return awaitLoggedIn(matrixChat) + .then(() => { + // we are logged in - let the sync complete + return expectAndAwaitSync(); + }) + .then(() => { + // once the sync completes, we should have a room view + return awaitRoomView(matrixChat); + }) + .then(() => { + httpBackend.verifyNoOutstandingExpectation(); + expect(windowLocation.hash).toEqual("#/room/!room:id"); + }); }); - describe('/#/login link:', function() { - beforeEach(function() { + describe("/#/login link:", function () { + beforeEach(function () { loadApp({ uriFragment: "#/login", }); @@ -357,7 +382,7 @@ describe('loading:', function() { return expectAndAwaitSync(); }); - it('does not show a login view', async function() { + it("does not show a login view", async function () { await awaitRoomView(matrixChat); await screen.findByLabelText("Spaces"); @@ -366,136 +391,165 @@ describe('loading:', function() { }); }); - describe('Guest auto-registration:', function() { - it('shows a welcome page by default', function() { + describe("Guest auto-registration:", function () { + it("shows a welcome page by default", function () { loadApp(); - return sleep(1).then(async () => { - // at this point, we're trying to do a guest registration; - // we expect a spinner - await assertAtLoadingSpinner(); + return sleep(1) + .then(async () => { + // at this point, we're trying to do a guest registration; + // we expect a spinner + await assertAtLoadingSpinner(); - httpBackend.when('POST', '/register').check(function(req) { - expect(req.queryParams.kind).toEqual('guest'); - }).respond(200, { - user_id: "@guest:localhost", - access_token: "secret_token", + httpBackend + .when("POST", "/register") + .check(function (req) { + expect(req.queryParams.kind).toEqual("guest"); + }) + .respond(200, { + user_id: "@guest:localhost", + access_token: "secret_token", + }); + + return httpBackend.flush(); + }) + .then(() => { + return awaitLoggedIn(matrixChat); + }) + .then(() => { + // we are logged in - let the sync complete + return expectAndAwaitSync({ isGuest: true }); + }) + .then(() => { + // once the sync completes, we should have a welcome page + httpBackend.verifyNoOutstandingExpectation(); + expect(matrixChat.container.querySelector(".mx_Welcome")).toBeTruthy(); + expect(windowLocation.hash).toEqual("#/welcome"); }); - - return httpBackend.flush(); - }).then(() => { - return awaitLoggedIn(matrixChat); - }).then(() => { - // we are logged in - let the sync complete - return expectAndAwaitSync({ isGuest: true }); - }).then(() => { - // once the sync completes, we should have a welcome page - httpBackend.verifyNoOutstandingExpectation(); - expect(matrixChat.container.querySelector(".mx_Welcome")).toBeTruthy(); - expect(windowLocation.hash).toEqual("#/welcome"); - }); }); - it('uses the default homeserver to register with', function() { + it("uses the default homeserver to register with", function () { loadApp(); - return sleep(1).then(async () => { - // at this point, we're trying to do a guest registration; - // we expect a spinner - await assertAtLoadingSpinner(); + return sleep(1) + .then(async () => { + // at this point, we're trying to do a guest registration; + // we expect a spinner + await assertAtLoadingSpinner(); - httpBackend.when('POST', '/register').check(function(req) { + httpBackend + .when("POST", "/register") + .check(function (req) { + expect(req.path.startsWith(DEFAULT_HS_URL)).toBe(true); + expect(req.queryParams.kind).toEqual("guest"); + }) + .respond(200, { + user_id: "@guest:localhost", + access_token: "secret_token", + }); + + return httpBackend.flush(); + }) + .then(() => { + return awaitLoggedIn(matrixChat); + }) + .then(() => { + return expectAndAwaitSync({ isGuest: true }); + }) + .then((req) => { expect(req.path.startsWith(DEFAULT_HS_URL)).toBe(true); - expect(req.queryParams.kind).toEqual('guest'); - }).respond(200, { - user_id: "@guest:localhost", - access_token: "secret_token", + + // once the sync completes, we should have a welcome page + httpBackend.verifyNoOutstandingExpectation(); + expect(matrixChat.container.querySelector(".mx_Welcome")).toBeTruthy(); + expect(windowLocation.hash).toEqual("#/welcome"); + expect(MatrixClientPeg.get().baseUrl).toEqual(DEFAULT_HS_URL); + expect(MatrixClientPeg.get().idBaseUrl).toEqual(DEFAULT_IS_URL); }); - - return httpBackend.flush(); - }).then(() => { - return awaitLoggedIn(matrixChat); - }).then(() => { - return expectAndAwaitSync({ isGuest: true }); - }).then((req) => { - expect(req.path.startsWith(DEFAULT_HS_URL)).toBe(true); - - // once the sync completes, we should have a welcome page - httpBackend.verifyNoOutstandingExpectation(); - expect(matrixChat.container.querySelector(".mx_Welcome")).toBeTruthy(); - expect(windowLocation.hash).toEqual("#/welcome"); - expect(MatrixClientPeg.get().baseUrl).toEqual(DEFAULT_HS_URL); - expect(MatrixClientPeg.get().idBaseUrl).toEqual(DEFAULT_IS_URL); - }); }); - it('shows a room view if we followed a room link', function() { + it("shows a room view if we followed a room link", function () { loadApp({ uriFragment: "#/room/!room:id", }); - return sleep(1).then(async () => { - // at this point, we're trying to do a guest registration; - // we expect a spinner - await assertAtLoadingSpinner(); + return sleep(1) + .then(async () => { + // at this point, we're trying to do a guest registration; + // we expect a spinner + await assertAtLoadingSpinner(); - httpBackend.when('POST', '/register').check(function(req) { - expect(req.queryParams.kind).toEqual('guest'); - }).respond(200, { - user_id: "@guest:localhost", - access_token: "secret_token", + httpBackend + .when("POST", "/register") + .check(function (req) { + expect(req.queryParams.kind).toEqual("guest"); + }) + .respond(200, { + user_id: "@guest:localhost", + access_token: "secret_token", + }); + + return httpBackend.flush(); + }) + .then(() => { + return awaitLoggedIn(matrixChat); + }) + .then(() => { + return expectAndAwaitSync({ isGuest: true }); + }) + .then(() => { + // once the sync completes, we should have a room view + return awaitRoomView(matrixChat); + }) + .then(() => { + httpBackend.verifyNoOutstandingExpectation(); + expect(windowLocation.hash).toEqual("#/room/!room:id"); }); - - return httpBackend.flush(); - }).then(() => { - return awaitLoggedIn(matrixChat); - }).then(() => { - return expectAndAwaitSync({ isGuest: true }); - }).then(() => { - // once the sync completes, we should have a room view - return awaitRoomView(matrixChat); - }).then(() => { - httpBackend.verifyNoOutstandingExpectation(); - expect(windowLocation.hash).toEqual("#/room/!room:id"); - }); }); - describe('Login as user', function() { - beforeEach(function() { + describe("Login as user", function () { + beforeEach(function () { // first we have to load the homepage loadApp(); - httpBackend.when('POST', '/register').check(function(req) { - expect(req.queryParams.kind).toEqual('guest'); - }).respond(200, { - user_id: "@guest:localhost", - access_token: "secret_token", - }); + httpBackend + .when("POST", "/register") + .check(function (req) { + expect(req.queryParams.kind).toEqual("guest"); + }) + .respond(200, { + user_id: "@guest:localhost", + access_token: "secret_token", + }); - return httpBackend.flush().then(() => { - return awaitLoggedIn(matrixChat); - }).then(() => { - // we got a sync spinner - let the sync complete - return expectAndAwaitSync(); - }).then(async () => { - // once the sync completes, we should have a home page - await waitFor(() => matrixChat.container.querySelector(".mx_HomePage")); + return httpBackend + .flush() + .then(() => { + return awaitLoggedIn(matrixChat); + }) + .then(() => { + // we got a sync spinner - let the sync complete + return expectAndAwaitSync(); + }) + .then(async () => { + // once the sync completes, we should have a home page + await waitFor(() => matrixChat.container.querySelector(".mx_HomePage")); - // we simulate a click on the 'login' button by firing off - // the relevant dispatch. - // - // XXX: is it an anti-pattern to access the react-sdk's - // dispatcher in this way? Is it better to find the login - // button and simulate a click? (we might have to arrange - // for it to be shown - it's not always, due to the - // collapsing left panel + // we simulate a click on the 'login' button by firing off + // the relevant dispatch. + // + // XXX: is it an anti-pattern to access the react-sdk's + // dispatcher in this way? Is it better to find the login + // button and simulate a click? (we might have to arrange + // for it to be shown - it's not always, due to the + // collapsing left panel - dis.dispatch({ action: 'start_login' }); + dis.dispatch({ action: "start_login" }); - return awaitLoginComponent(matrixChat); - }); + return awaitLoginComponent(matrixChat); + }); }); - it('should give us a login page', async function() { + it("should give us a login page", async function () { // we expect a single component await screen.findByRole("main"); screen.getAllByText("Sign in"); @@ -505,44 +559,50 @@ describe('loading:', function() { }); }); - describe('Token login:', function() { - it('logs in successfully', function() { + describe("Token login:", function () { + it("logs in successfully", function () { localStorage.setItem("mx_sso_hs_url", "https://homeserver"); localStorage.setItem("mx_sso_is_url", "https://idserver"); loadApp({ queryString: "?loginToken=secretToken", }); - return sleep(1).then(async () => { - // we expect a spinner while we're logging in - await assertAtLoadingSpinner(); + return sleep(1) + .then(async () => { + // we expect a spinner while we're logging in + await assertAtLoadingSpinner(); - httpBackend.when('POST', '/login').check(function(req) { - expect(req.path).toMatch(new RegExp("^https://homeserver/")); - expect(req.data.type).toEqual("m.login.token"); - expect(req.data.token).toEqual("secretToken"); - }).respond(200, { - user_id: "@user:localhost", - device_id: 'DEVICE_ID', - access_token: "access_token", + httpBackend + .when("POST", "/login") + .check(function (req) { + expect(req.path).toMatch(new RegExp("^https://homeserver/")); + expect(req.data.type).toEqual("m.login.token"); + expect(req.data.token).toEqual("secretToken"); + }) + .respond(200, { + user_id: "@user:localhost", + device_id: "DEVICE_ID", + access_token: "access_token", + }); + + return httpBackend.flush(); + }) + .then(() => { + // at this point, MatrixChat should fire onTokenLoginCompleted, which + // makes index.js reload the app. We're not going to attempt to + // simulate the reload - just check that things are left in the + // right state for the reloaded app. + + return tokenLoginCompletePromise; + }) + .then(() => { + // check that the localstorage has been set up in such a way that + // the reloaded app can pick up where we leave off. + expect(localStorage.getItem("mx_user_id")).toEqual("@user:localhost"); + expect(localStorage.getItem("mx_access_token")).toEqual("access_token"); + expect(localStorage.getItem("mx_hs_url")).toEqual("https://homeserver"); + expect(localStorage.getItem("mx_is_url")).toEqual("https://idserver"); }); - - return httpBackend.flush(); - }).then(() => { - // at this point, MatrixChat should fire onTokenLoginCompleted, which - // makes index.js reload the app. We're not going to attempt to - // simulate the reload - just check that things are left in the - // right state for the reloaded app. - - return tokenLoginCompletePromise; - }).then(() => { - // check that the localstorage has been set up in such a way that - // the reloaded app can pick up where we leave off. - expect(localStorage.getItem('mx_user_id')).toEqual('@user:localhost'); - expect(localStorage.getItem('mx_access_token')).toEqual('access_token'); - expect(localStorage.getItem('mx_hs_url')).toEqual('https://homeserver'); - expect(localStorage.getItem('mx_is_url')).toEqual('https://idserver'); - }); }); }); @@ -551,38 +611,44 @@ describe('loading:', function() { async function completeLogin(matrixChat: RenderResult): Promise { // When we switch to the login component, it'll hit the login endpoint // for proof of life and to get flows. We'll only give it one option. - httpBackend.when('GET', '/login') - .respond(200, { flows: [{ type: "m.login.password" }] }); + httpBackend.when("GET", "/login").respond(200, { flows: [{ type: "m.login.password" }] }); httpBackend.flush(); // We already would have tried the GET /login request // Give the component some time to finish processing the login flows before // continuing. await sleep(100); - httpBackend.when('POST', '/login').check(function(req) { - expect(req.data.type).toEqual('m.login.password'); - expect(req.data.identifier.type).toEqual('m.id.user'); - expect(req.data.identifier.user).toEqual('user'); - expect(req.data.password).toEqual('pass'); - }).respond(200, { - user_id: '@user:id', - device_id: 'DEVICE_ID', - access_token: 'access_token', - }); + httpBackend + .when("POST", "/login") + .check(function (req) { + expect(req.data.type).toEqual("m.login.password"); + expect(req.data.identifier.type).toEqual("m.id.user"); + expect(req.data.identifier.user).toEqual("user"); + expect(req.data.password).toEqual("pass"); + }) + .respond(200, { + user_id: "@user:id", + device_id: "DEVICE_ID", + access_token: "access_token", + }); fireEvent.change(matrixChat.container.querySelector("#mx_LoginForm_username"), { target: { value: "user" } }); fireEvent.change(matrixChat.container.querySelector("#mx_LoginForm_password"), { target: { value: "pass" } }); fireEvent.click(screen.getByText("Sign in", { selector: ".mx_Login_submit" })); - return httpBackend.flush().then(() => { - // Wait for another trip around the event loop for the UI to update - return sleep(1); - }).then(() => { - return expectAndAwaitSync().catch((e) => { - throw new Error("Never got /sync after login: did the client start?"); + return httpBackend + .flush() + .then(() => { + // Wait for another trip around the event loop for the UI to update + return sleep(1); + }) + .then(() => { + return expectAndAwaitSync().catch((e) => { + throw new Error("Never got /sync after login: did the client start?"); + }); + }) + .then(() => { + httpBackend.verifyNoOutstandingExpectation(); }); - }).then(() => { - httpBackend.verifyNoOutstandingExpectation(); - }); } }); @@ -594,7 +660,7 @@ async function assertAtLoadingSpinner(): Promise { async function awaitLoggedIn(matrixChat: RenderResult): Promise { if (matrixChat.container.querySelector(".mx_MatrixChat_wrapper")) return; // already logged in - return new Promise(resolve => { + return new Promise((resolve) => { const onAction = ({ action }): void => { if (action !== "on_logged_in") { return; @@ -621,6 +687,6 @@ async function awaitWelcomeComponent(matrixChat: RenderResult): Promise { } function moveFromWelcomeToLogin(matrixChat: RenderResult): Promise { - dis.dispatch({ action: 'start_login' }); + dis.dispatch({ action: "start_login" }); return awaitLoginComponent(matrixChat); } diff --git a/test/jest-mocks.ts b/test/jest-mocks.ts index 7a5503667a..3302ea9ff9 100644 --- a/test/jest-mocks.ts +++ b/test/jest-mocks.ts @@ -15,9 +15,9 @@ limitations under the License. */ // https://jestjs.io/docs/en/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom -Object.defineProperty(window, 'matchMedia', { +Object.defineProperty(window, "matchMedia", { writable: true, - value: jest.fn().mockImplementation(query => ({ + value: jest.fn().mockImplementation((query) => ({ matches: false, media: query, onchange: null, diff --git a/test/test-utils.ts b/test/test-utils.ts index db10cd5a2b..84234ef34e 100644 --- a/test/test-utils.ts +++ b/test/test-utils.ts @@ -34,14 +34,12 @@ export function deleteIndexedDB(dbName: string): Promise { }; req.onerror = (ev): void => { - reject(new Error( - `${Date.now()}: unable to delete indexeddb ${dbName}: ${req.error}`, - )); + reject(new Error(`${Date.now()}: unable to delete indexeddb ${dbName}: ${req.error}`)); }; req.onsuccess = (): void => { const now = Date.now(); - console.log(`${now}: Removed indexeddb instance: ${dbName} in ${now-startTime} ms`); + console.log(`${now}: Removed indexeddb instance: ${dbName} in ${now - startTime} ms`); resolve(); }; }).catch((e) => { diff --git a/test/unit-tests/vector/getconfig-test.ts b/test/unit-tests/vector/getconfig-test.ts index 3e205fe83e..aac830457e 100644 --- a/test/unit-tests/vector/getconfig-test.ts +++ b/test/unit-tests/vector/getconfig-test.ts @@ -20,88 +20,88 @@ import { getVectorConfig } from "../../../src/vector/getconfig"; fetchMock.config.overwriteRoutes = true; -describe('getVectorConfig()', () => { +describe("getVectorConfig()", () => { const prevDocumentDomain = document.domain; - const elementDomain = 'app.element.io'; + const elementDomain = "app.element.io"; const now = 1234567890; const specificConfig = { - brand: 'specific', + brand: "specific", }; const generalConfig = { - brand: 'general', + brand: "general", }; beforeEach(() => { document.domain = elementDomain; // stable value for cachebuster - jest.spyOn(Date, 'now').mockReturnValue(now); + jest.spyOn(Date, "now").mockReturnValue(now); jest.clearAllMocks(); fetchMock.mockClear(); }); afterAll(() => { document.domain = prevDocumentDomain; - jest.spyOn(Date, 'now').mockRestore(); + jest.spyOn(Date, "now").mockRestore(); }); - it('requests specific config for document domain', async () => { + it("requests specific config for document domain", async () => { fetchMock.getOnce("express:/config.app.element.io.json", specificConfig); fetchMock.getOnce("express:/config.json", generalConfig); await expect(getVectorConfig()).resolves.toEqual(specificConfig); }); - it('adds trailing slash to relativeLocation when not an empty string', async () => { + it("adds trailing slash to relativeLocation when not an empty string", async () => { fetchMock.getOnce("express:../config.app.element.io.json", specificConfig); fetchMock.getOnce("express:../config.json", generalConfig); await expect(getVectorConfig("..")).resolves.toEqual(specificConfig); }); - it('returns general config when specific config succeeds but is empty', async () => { + it("returns general config when specific config succeeds but is empty", async () => { fetchMock.getOnce("express:/config.app.element.io.json", {}); fetchMock.getOnce("express:/config.json", generalConfig); await expect(getVectorConfig()).resolves.toEqual(generalConfig); }); - it('returns general config when specific config 404s', async () => { + it("returns general config when specific config 404s", async () => { fetchMock.getOnce("express:/config.app.element.io.json", { status: 404 }); fetchMock.getOnce("express:/config.json", generalConfig); await expect(getVectorConfig()).resolves.toEqual(generalConfig); }); - it('returns general config when specific config is fetched from a file and is empty', async () => { + it("returns general config when specific config is fetched from a file and is empty", async () => { fetchMock.getOnce("express:/config.app.element.io.json", 0); fetchMock.getOnce("express:/config.json", generalConfig); await expect(getVectorConfig()).resolves.toEqual(generalConfig); }); - it('returns general config when specific config returns a non-200 status', async () => { + it("returns general config when specific config returns a non-200 status", async () => { fetchMock.getOnce("express:/config.app.element.io.json", { status: 401 }); fetchMock.getOnce("express:/config.json", generalConfig); await expect(getVectorConfig()).resolves.toEqual(generalConfig); }); - it('returns general config when specific config returns an error', async () => { + it("returns general config when specific config returns an error", async () => { fetchMock.getOnce("express:/config.app.element.io.json", { throws: "err1" }); fetchMock.getOnce("express:/config.json", generalConfig); await expect(getVectorConfig()).resolves.toEqual(generalConfig); }); - it('rejects with an error when general config rejects', async () => { + it("rejects with an error when general config rejects", async () => { fetchMock.getOnce("express:/config.app.element.io.json", { throws: "err-specific" }); fetchMock.getOnce("express:/config.json", { throws: "err-general" }); await expect(getVectorConfig()).rejects.toBe("err-general"); }); - it('rejects with an error when config is invalid JSON', async () => { + it("rejects with an error when config is invalid JSON", async () => { fetchMock.getOnce("express:/config.app.element.io.json", { throws: "err-specific" }); fetchMock.getOnce("express:/config.json", '{"invalid": "json",}'); diff --git a/test/unit-tests/vector/platform/ElectronPlatform-test.ts b/test/unit-tests/vector/platform/ElectronPlatform-test.ts index f96506d0a8..c1a4e4ce5c 100644 --- a/test/unit-tests/vector/platform/ElectronPlatform-test.ts +++ b/test/unit-tests/vector/platform/ElectronPlatform-test.ts @@ -14,33 +14,34 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { logger } from 'matrix-js-sdk/src/logger'; -import { MatrixEvent, Room } from 'matrix-js-sdk/src/matrix'; -import { UpdateCheckStatus } from 'matrix-react-sdk/src/BasePlatform'; -import { Action } from 'matrix-react-sdk/src/dispatcher/actions'; -import dispatcher from 'matrix-react-sdk/src/dispatcher/dispatcher'; -import * as rageshake from 'matrix-react-sdk/src/rageshake/rageshake'; +import { logger } from "matrix-js-sdk/src/logger"; +import { MatrixEvent, Room } from "matrix-js-sdk/src/matrix"; +import { UpdateCheckStatus } from "matrix-react-sdk/src/BasePlatform"; +import { Action } from "matrix-react-sdk/src/dispatcher/actions"; +import dispatcher from "matrix-react-sdk/src/dispatcher/dispatcher"; +import * as rageshake from "matrix-react-sdk/src/rageshake/rageshake"; -import ElectronPlatform from '../../../../src/vector/platform/ElectronPlatform'; +import ElectronPlatform from "../../../../src/vector/platform/ElectronPlatform"; -jest.mock('matrix-react-sdk/src/rageshake/rageshake', () => ({ +jest.mock("matrix-react-sdk/src/rageshake/rageshake", () => ({ flush: jest.fn(), })); -describe('ElectronPlatform', () => { - const defaultUserAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 ' + - '(KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36'; +describe("ElectronPlatform", () => { + const defaultUserAgent = + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 " + + "(KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"; const mockElectron = { on: jest.fn(), send: jest.fn(), }; - const dispatchSpy = jest.spyOn(dispatcher, 'dispatch'); - const dispatchFireSpy = jest.spyOn(dispatcher, 'fire'); - const logSpy = jest.spyOn(logger, 'log').mockImplementation(() => {}); + const dispatchSpy = jest.spyOn(dispatcher, "dispatch"); + const dispatchFireSpy = jest.spyOn(dispatcher, "fire"); + const logSpy = jest.spyOn(logger, "log").mockImplementation(() => {}); - const userId = '@alice:server.org'; - const deviceId = 'device-id'; + const userId = "@alice:server.org"; + const deviceId = "device-id"; window.electron = mockElectron; beforeEach(() => { @@ -53,9 +54,9 @@ describe('ElectronPlatform', () => { const getElectronEventHandlerCall = (eventType: string): [type: string, handler: Function] | undefined => mockElectron.on.mock.calls.find(([type]) => type === eventType); - it('flushes rageshake before quitting', () => { + it("flushes rageshake before quitting", () => { new ElectronPlatform(); - const [event, handler] = getElectronEventHandlerCall('before-quit'); + const [event, handler] = getElectronEventHandlerCall("before-quit"); // correct event bound expect(event).toBeTruthy(); @@ -65,9 +66,9 @@ describe('ElectronPlatform', () => { expect(rageshake.flush).toHaveBeenCalled(); }); - it('dispatches view settings action on preferences event', () => { + it("dispatches view settings action on preferences event", () => { new ElectronPlatform(); - const [event, handler] = getElectronEventHandlerCall('preferences'); + const [event, handler] = getElectronEventHandlerCall("preferences"); // correct event bound expect(event).toBeTruthy(); @@ -76,10 +77,10 @@ describe('ElectronPlatform', () => { expect(dispatchFireSpy).toHaveBeenCalledWith(Action.ViewUserSettings); }); - describe('updates', () => { - it('dispatches on check updates action', () => { + describe("updates", () => { + it("dispatches on check updates action", () => { new ElectronPlatform(); - const [event, handler] = getElectronEventHandlerCall('check_updates'); + const [event, handler] = getElectronEventHandlerCall("check_updates"); // correct event bound expect(event).toBeTruthy(); @@ -90,9 +91,9 @@ describe('ElectronPlatform', () => { }); }); - it('dispatches on check updates action when update not available', () => { + it("dispatches on check updates action when update not available", () => { new ElectronPlatform(); - const [, handler] = getElectronEventHandlerCall('check_updates'); + const [, handler] = getElectronEventHandlerCall("check_updates"); handler({}, false); expect(dispatchSpy).toHaveBeenCalledWith({ @@ -101,55 +102,42 @@ describe('ElectronPlatform', () => { }); }); - it('starts update check', () => { + it("starts update check", () => { const platform = new ElectronPlatform(); platform.startUpdateCheck(); - expect(mockElectron.send).toHaveBeenCalledWith('check_updates'); + expect(mockElectron.send).toHaveBeenCalledWith("check_updates"); }); - it('installs update', () => { + it("installs update", () => { const platform = new ElectronPlatform(); platform.installUpdate(); - expect(mockElectron.send).toHaveBeenCalledWith('install_update'); + expect(mockElectron.send).toHaveBeenCalledWith("install_update"); }); }); - it('returns human readable name', () => { + it("returns human readable name", () => { const platform = new ElectronPlatform(); - expect(platform.getHumanReadableName()).toEqual('Electron Platform'); + expect(platform.getHumanReadableName()).toEqual("Electron Platform"); }); describe("getDefaultDeviceDisplayName", () => { - it.each([[ - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 " + - "(KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", - "Element Desktop: macOS", - ], - [ - "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) " + - "electron/1.0.0 Chrome/53.0.2785.113 Electron/1.4.3 Safari/537.36", - "Element Desktop: Windows", - ], - [ - "Mozilla/5.0 (X11; Linux i686; rv:21.0) Gecko/20100101 Firefox/21.0", - "Element Desktop: Linux", - ], - [ - "Mozilla/5.0 (X11; FreeBSD i686; rv:21.0) Gecko/20100101 Firefox/21.0", - "Element Desktop: FreeBSD", - ], - [ - "Mozilla/5.0 (X11; OpenBSD i686; rv:21.0) Gecko/20100101 Firefox/21.0", - "Element Desktop: OpenBSD", - ], - [ - "Mozilla/5.0 (X11; SunOS i686; rv:21.0) Gecko/20100101 Firefox/21.0", - "Element Desktop: SunOS", - ], - [ - "custom user agent", - "Element Desktop: Unknown", - ]])("%s = %s", (userAgent, result) => { + it.each([ + [ + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 " + + "(KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36", + "Element Desktop: macOS", + ], + [ + "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) " + + "electron/1.0.0 Chrome/53.0.2785.113 Electron/1.4.3 Safari/537.36", + "Element Desktop: Windows", + ], + ["Mozilla/5.0 (X11; Linux i686; rv:21.0) Gecko/20100101 Firefox/21.0", "Element Desktop: Linux"], + ["Mozilla/5.0 (X11; FreeBSD i686; rv:21.0) Gecko/20100101 Firefox/21.0", "Element Desktop: FreeBSD"], + ["Mozilla/5.0 (X11; OpenBSD i686; rv:21.0) Gecko/20100101 Firefox/21.0", "Element Desktop: OpenBSD"], + ["Mozilla/5.0 (X11; SunOS i686; rv:21.0) Gecko/20100101 Firefox/21.0", "Element Desktop: SunOS"], + ["custom user agent", "Element Desktop: Unknown"], + ])("%s = %s", (userAgent, result) => { delete window.navigator; window.navigator = { userAgent } as unknown as Navigator; const platform = new ElectronPlatform(); @@ -157,119 +145,119 @@ describe('ElectronPlatform', () => { }); }); - it('returns true for needsUrlTooltips', () => { + it("returns true for needsUrlTooltips", () => { const platform = new ElectronPlatform(); expect(platform.needsUrlTooltips()).toBe(true); }); - it('should override browser shortcuts', () => { + it("should override browser shortcuts", () => { const platform = new ElectronPlatform(); expect(platform.overrideBrowserShortcuts()).toBe(true); }); - it('allows overriding native context menus', () => { + it("allows overriding native context menus", () => { const platform = new ElectronPlatform(); expect(platform.allowOverridingNativeContextMenus()).toBe(true); }); - it('indicates support for desktop capturer', () => { + it("indicates support for desktop capturer", () => { const platform = new ElectronPlatform(); expect(platform.supportsDesktopCapturer()).toBe(true); }); - it('indicates no support for jitsi screensharing', () => { + it("indicates no support for jitsi screensharing", () => { const platform = new ElectronPlatform(); expect(platform.supportsJitsiScreensharing()).toBe(false); }); - describe('notifications', () => { - it('indicates support for notifications', () => { + describe("notifications", () => { + it("indicates support for notifications", () => { const platform = new ElectronPlatform(); expect(platform.supportsNotifications()).toBe(true); }); - it('may send notifications', () => { + it("may send notifications", () => { const platform = new ElectronPlatform(); expect(platform.maySendNotifications()).toBe(true); }); - it('pretends to request notification permission', async () => { + it("pretends to request notification permission", async () => { const platform = new ElectronPlatform(); const result = await platform.requestNotificationPermission(); - expect(result).toEqual('granted'); + expect(result).toEqual("granted"); }); - it('creates a loud notification', async () => { + it("creates a loud notification", async () => { const platform = new ElectronPlatform(); - platform.loudNotification(new MatrixEvent(), new Room('!room:server', {} as any, userId)); - expect(mockElectron.send).toHaveBeenCalledWith('loudNotification'); + platform.loudNotification(new MatrixEvent(), new Room("!room:server", {} as any, userId)); + expect(mockElectron.send).toHaveBeenCalledWith("loudNotification"); }); - it('sets notification count when count is changing', async () => { + it("sets notification count when count is changing", async () => { const platform = new ElectronPlatform(); platform.setNotificationCount(0); // not called because matches internal notificaiton count - expect(mockElectron.send).not.toHaveBeenCalledWith('setBadgeCount', 0); + expect(mockElectron.send).not.toHaveBeenCalledWith("setBadgeCount", 0); platform.setNotificationCount(1); - expect(mockElectron.send).toHaveBeenCalledWith('setBadgeCount', 1); + expect(mockElectron.send).toHaveBeenCalledWith("setBadgeCount", 1); }); }); - describe('spellcheck', () => { - it('indicates support for spellcheck settings', () => { + describe("spellcheck", () => { + it("indicates support for spellcheck settings", () => { const platform = new ElectronPlatform(); expect(platform.supportsSpellCheckSettings()).toBe(true); }); - it('gets available spellcheck languages', () => { + it("gets available spellcheck languages", () => { const platform = new ElectronPlatform(); mockElectron.send.mockClear(); platform.getAvailableSpellCheckLanguages(); const [channel, { name }] = mockElectron.send.mock.calls[0]; expect(channel).toEqual("ipcCall"); - expect(name).toEqual('getAvailableSpellCheckLanguages'); + expect(name).toEqual("getAvailableSpellCheckLanguages"); }); }); - describe('pickle key', () => { - it('makes correct ipc call to get pickle key', () => { + describe("pickle key", () => { + it("makes correct ipc call to get pickle key", () => { const platform = new ElectronPlatform(); mockElectron.send.mockClear(); platform.getPickleKey(userId, deviceId); const [, { name, args }] = mockElectron.send.mock.calls[0]; - expect(name).toEqual('getPickleKey'); + expect(name).toEqual("getPickleKey"); expect(args).toEqual([userId, deviceId]); }); - it('makes correct ipc call to create pickle key', () => { + it("makes correct ipc call to create pickle key", () => { const platform = new ElectronPlatform(); mockElectron.send.mockClear(); platform.createPickleKey(userId, deviceId); const [, { name, args }] = mockElectron.send.mock.calls[0]; - expect(name).toEqual('createPickleKey'); + expect(name).toEqual("createPickleKey"); expect(args).toEqual([userId, deviceId]); }); - it('makes correct ipc call to destroy pickle key', () => { + it("makes correct ipc call to destroy pickle key", () => { const platform = new ElectronPlatform(); mockElectron.send.mockClear(); platform.destroyPickleKey(userId, deviceId); const [, { name, args }] = mockElectron.send.mock.calls[0]; - expect(name).toEqual('destroyPickleKey'); + expect(name).toEqual("destroyPickleKey"); expect(args).toEqual([userId, deviceId]); }); }); - describe('versions', () => { - it('calls install update', () => { + describe("versions", () => { + it("calls install update", () => { const platform = new ElectronPlatform(); platform.installUpdate(); - expect(mockElectron.send).toHaveBeenCalledWith('install_update'); + expect(mockElectron.send).toHaveBeenCalledWith("install_update"); }); }); }); diff --git a/test/unit-tests/vector/platform/PWAPlatform-test.ts b/test/unit-tests/vector/platform/PWAPlatform-test.ts index 4829fc04d3..59632d0bb6 100644 --- a/test/unit-tests/vector/platform/PWAPlatform-test.ts +++ b/test/unit-tests/vector/platform/PWAPlatform-test.ts @@ -21,7 +21,7 @@ import WebPlatform from "../../../../src/vector/platform/WebPlatform"; jest.mock("../../../../src/vector/platform/WebPlatform"); -describe('PWAPlatform', () => { +describe("PWAPlatform", () => { beforeEach(() => { jest.clearAllMocks(); }); @@ -54,7 +54,7 @@ describe('PWAPlatform', () => { }); it("should handle Navigator::setAppBadge rejecting gracefully", () => { - navigator.setAppBadge = jest.fn().mockRejectedValue(new Error); + navigator.setAppBadge = jest.fn().mockRejectedValue(new Error()); const platform = new PWAPlatform(); expect(() => platform.setNotificationCount(123)).not.toThrow(); }); diff --git a/test/unit-tests/vector/platform/WebPlatform-test.ts b/test/unit-tests/vector/platform/WebPlatform-test.ts index ad84cb0c06..6f7c332c2d 100644 --- a/test/unit-tests/vector/platform/WebPlatform-test.ts +++ b/test/unit-tests/vector/platform/WebPlatform-test.ts @@ -15,24 +15,24 @@ limitations under the License. */ import fetchMock from "fetch-mock-jest"; -import { UpdateCheckStatus } from 'matrix-react-sdk/src/BasePlatform'; -import { MatrixClientPeg } from 'matrix-react-sdk/src/MatrixClientPeg'; +import { UpdateCheckStatus } from "matrix-react-sdk/src/BasePlatform"; +import { MatrixClientPeg } from "matrix-react-sdk/src/MatrixClientPeg"; -import WebPlatform from '../../../../src/vector/platform/WebPlatform'; +import WebPlatform from "../../../../src/vector/platform/WebPlatform"; fetchMock.config.overwriteRoutes = true; -describe('WebPlatform', () => { +describe("WebPlatform", () => { beforeEach(() => { jest.clearAllMocks(); }); - it('returns human readable name', () => { + it("returns human readable name", () => { const platform = new WebPlatform(); - expect(platform.getHumanReadableName()).toEqual('Web Platform'); + expect(platform.getHumanReadableName()).toEqual("Web Platform"); }); - it('registers service worker', () => { + it("registers service worker", () => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - mocking readonly object navigator.serviceWorker = { register: jest.fn() }; @@ -65,12 +65,14 @@ describe('WebPlatform', () => { }); describe("getDefaultDeviceDisplayName", () => { - it.each([[ - "https://develop.element.io/#/room/!foo:bar", - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) " + - "Chrome/105.0.0.0 Safari/537.36", - "develop.element.io: Chrome on macOS", - ]])("%s & %s = %s", (url, userAgent, result) => { + it.each([ + [ + "https://develop.element.io/#/room/!foo:bar", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) " + + "Chrome/105.0.0.0 Safari/537.36", + "develop.element.io: Chrome on macOS", + ], + ])("%s & %s = %s", (url, userAgent, result) => { delete window.navigator; window.navigator = { userAgent } as unknown as Navigator; delete window.location; @@ -80,66 +82,66 @@ describe('WebPlatform', () => { }); }); - describe('notification support', () => { + describe("notification support", () => { const mockNotification = { requestPermission: jest.fn(), - permission: 'notGranted', + permission: "notGranted", }; beforeEach(() => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore window.Notification = mockNotification; - mockNotification.permission = 'notGranted'; + mockNotification.permission = "notGranted"; }); - it('supportsNotifications returns false when platform does not support notifications', () => { + it("supportsNotifications returns false when platform does not support notifications", () => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore window.Notification = undefined; expect(new WebPlatform().supportsNotifications()).toBe(false); }); - it('supportsNotifications returns true when platform supports notifications', () => { + it("supportsNotifications returns true when platform supports notifications", () => { expect(new WebPlatform().supportsNotifications()).toBe(true); }); - it('maySendNotifications returns true when notification permissions are not granted', () => { + it("maySendNotifications returns true when notification permissions are not granted", () => { expect(new WebPlatform().maySendNotifications()).toBe(false); }); - it('maySendNotifications returns true when notification permissions are granted', () => { - mockNotification.permission = 'granted'; + it("maySendNotifications returns true when notification permissions are granted", () => { + mockNotification.permission = "granted"; expect(new WebPlatform().maySendNotifications()).toBe(true); }); - it('requests notification permissions and returns result ', async () => { - mockNotification.requestPermission.mockImplementation(callback => callback('test')); + it("requests notification permissions and returns result ", async () => { + mockNotification.requestPermission.mockImplementation((callback) => callback("test")); const platform = new WebPlatform(); const result = await platform.requestNotificationPermission(); - expect(result).toEqual('test'); + expect(result).toEqual("test"); }); }); - describe('app version', () => { + describe("app version", () => { const envVersion = process.env.VERSION; - const prodVersion = '1.10.13'; + const prodVersion = "1.10.13"; beforeEach(() => { - jest.spyOn(MatrixClientPeg, 'userRegisteredWithinLastHours').mockReturnValue(false); + jest.spyOn(MatrixClientPeg, "userRegisteredWithinLastHours").mockReturnValue(false); }); afterAll(() => { process.env.VERSION = envVersion; }); - it('should return true from canSelfUpdate()', async () => { + it("should return true from canSelfUpdate()", async () => { const platform = new WebPlatform(); const result = await platform.canSelfUpdate(); expect(result).toBe(true); }); - it('getAppVersion returns normalized app version', async () => { + it("getAppVersion returns normalized app version", async () => { process.env.VERSION = prodVersion; const platform = new WebPlatform(); @@ -156,23 +158,26 @@ describe('WebPlatform', () => { expect(notSemverVersion).toEqual(`version not like semver`); }); - describe('pollForUpdate()', () => { - it('should return not available and call showNoUpdate when current version ' + - 'matches most recent version', async () => { - process.env.VERSION = prodVersion; - fetchMock.getOnce("/version", prodVersion); - const platform = new WebPlatform(); + describe("pollForUpdate()", () => { + it( + "should return not available and call showNoUpdate when current version " + + "matches most recent version", + async () => { + process.env.VERSION = prodVersion; + fetchMock.getOnce("/version", prodVersion); + const platform = new WebPlatform(); - const showUpdate = jest.fn(); - const showNoUpdate = jest.fn(); - const result = await platform.pollForUpdate(showUpdate, showNoUpdate); + const showUpdate = jest.fn(); + const showNoUpdate = jest.fn(); + const result = await platform.pollForUpdate(showUpdate, showNoUpdate); - expect(result).toEqual({ status: UpdateCheckStatus.NotAvailable }); - expect(showUpdate).not.toHaveBeenCalled(); - expect(showNoUpdate).toHaveBeenCalled(); - }); + expect(result).toEqual({ status: UpdateCheckStatus.NotAvailable }); + expect(showUpdate).not.toHaveBeenCalled(); + expect(showNoUpdate).toHaveBeenCalled(); + }, + ); - it('should strip v prefix from versions before comparing', async () => { + it("should strip v prefix from versions before comparing", async () => { process.env.VERSION = prodVersion; fetchMock.getOnce("/version", `v${prodVersion}`); const platform = new WebPlatform(); @@ -187,24 +192,26 @@ describe('WebPlatform', () => { expect(showNoUpdate).toHaveBeenCalled(); }); - it('should return ready and call showUpdate when current version ' + - 'differs from most recent version', async () => { - process.env.VERSION = '0.0.0'; // old version - fetchMock.getOnce("/version", prodVersion); - const platform = new WebPlatform(); + it( + "should return ready and call showUpdate when current version " + "differs from most recent version", + async () => { + process.env.VERSION = "0.0.0"; // old version + fetchMock.getOnce("/version", prodVersion); + const platform = new WebPlatform(); - const showUpdate = jest.fn(); - const showNoUpdate = jest.fn(); - const result = await platform.pollForUpdate(showUpdate, showNoUpdate); + const showUpdate = jest.fn(); + const showNoUpdate = jest.fn(); + const result = await platform.pollForUpdate(showUpdate, showNoUpdate); - expect(result).toEqual({ status: UpdateCheckStatus.Ready }); - expect(showUpdate).toHaveBeenCalledWith('0.0.0', prodVersion); - expect(showNoUpdate).not.toHaveBeenCalled(); - }); + expect(result).toEqual({ status: UpdateCheckStatus.Ready }); + expect(showUpdate).toHaveBeenCalledWith("0.0.0", prodVersion); + expect(showNoUpdate).not.toHaveBeenCalled(); + }, + ); - it('should return ready without showing update when user registered in last 24', async () => { - process.env.VERSION = '0.0.0'; // old version - jest.spyOn(MatrixClientPeg, 'userRegisteredWithinLastHours').mockReturnValue(true); + it("should return ready without showing update when user registered in last 24", async () => { + process.env.VERSION = "0.0.0"; // old version + jest.spyOn(MatrixClientPeg, "userRegisteredWithinLastHours").mockReturnValue(true); fetchMock.getOnce("/version", prodVersion); const platform = new WebPlatform(); @@ -217,7 +224,7 @@ describe('WebPlatform', () => { expect(showNoUpdate).not.toHaveBeenCalled(); }); - it('should return error when version check fails', async () => { + it("should return error when version check fails", async () => { fetchMock.getOnce("/version", { throws: "oups" }); const platform = new WebPlatform(); @@ -225,7 +232,7 @@ describe('WebPlatform', () => { const showNoUpdate = jest.fn(); const result = await platform.pollForUpdate(showUpdate, showNoUpdate); - expect(result).toEqual({ status: UpdateCheckStatus.Error, detail: 'Unknown Error' }); + expect(result).toEqual({ status: UpdateCheckStatus.Error, detail: "Unknown Error" }); expect(showUpdate).not.toHaveBeenCalled(); expect(showNoUpdate).not.toHaveBeenCalled(); }); diff --git a/test/unit-tests/vector/url_utils-test.ts b/test/unit-tests/vector/url_utils-test.ts index 663798a1ae..374b6c6d29 100644 --- a/test/unit-tests/vector/url_utils-test.ts +++ b/test/unit-tests/vector/url_utils-test.ts @@ -16,7 +16,7 @@ limitations under the License. import { parseQsFromFragment, parseQs } from "../../../src/vector/url_utils"; -describe("url_utils.ts", function() { +describe("url_utils.ts", function () { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const location: Location = { @@ -24,28 +24,28 @@ describe("url_utils.ts", function() { search: "", }; - it("parseQsFromFragment", function() { + it("parseQsFromFragment", function () { location.hash = "/home?foo=bar"; expect(parseQsFromFragment(location)).toEqual({ location: "home", params: { - "foo": "bar", + foo: "bar", }, }); }); - describe("parseQs", function() { + describe("parseQs", function () { location.search = "?foo=bar"; expect(parseQs(location)).toEqual({ - "foo": "bar", + foo: "bar", }); }); - describe("parseQs with arrays", function() { + describe("parseQs with arrays", function () { location.search = "?via=s1&via=s2&via=s2&foo=bar"; expect(parseQs(location)).toEqual({ - "via": ["s1", "s2", "s2"], - "foo": "bar", + via: ["s1", "s2", "s2"], + foo: "bar", }); }); }); diff --git a/tsconfig.json b/tsconfig.json index 691d3c3487..ddceda407c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,28 +1,19 @@ { - "compilerOptions": { - "experimentalDecorators": false, - "emitDecoratorMetadata": false, - "resolveJsonModule": true, - "esModuleInterop": true, - "module": "commonjs", - "moduleResolution": "node", - "target": "es2016", - "noImplicitAny": false, - "noUnusedLocals": true, - "sourceMap": false, - "outDir": "./lib", - "declaration": true, - "jsx": "react", - "lib": [ - "es2019", - "dom", - "dom.iterable" - ] - }, - "include": [ - "./src/**/*.ts", - "./src/**/*.tsx", - "./test/**/*.ts", - "./test/**/*.tsx" - ] + "compilerOptions": { + "experimentalDecorators": false, + "emitDecoratorMetadata": false, + "resolveJsonModule": true, + "esModuleInterop": true, + "module": "commonjs", + "moduleResolution": "node", + "target": "es2016", + "noImplicitAny": false, + "noUnusedLocals": true, + "sourceMap": false, + "outDir": "./lib", + "declaration": true, + "jsx": "react", + "lib": ["es2019", "dom", "dom.iterable"] + }, + "include": ["./src/**/*.ts", "./src/**/*.tsx", "./test/**/*.ts", "./test/**/*.tsx"] } diff --git a/tsconfig.module_system.json b/tsconfig.module_system.json index e5e8d22b52..e1776404f5 100644 --- a/tsconfig.module_system.json +++ b/tsconfig.module_system.json @@ -1,17 +1,11 @@ { - "extends": "./tsconfig.json", - "compilerOptions": { - "jsx": "preserve", - "declaration": false, - "outDir": "./lib/module_system", - "lib": [ - "es2019" - ], - "types": [ - "node" - ] - }, - "include": [ - "./module_system/**/*.ts" - ] + "extends": "./tsconfig.json", + "compilerOptions": { + "jsx": "preserve", + "declaration": false, + "outDir": "./lib/module_system", + "lib": ["es2019"], + "types": ["node"] + }, + "include": ["./module_system/**/*.ts"] } diff --git a/webpack.config.js b/webpack.config.js index 9ecb2ad5be..dce54b53f3 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,13 +1,13 @@ /* eslint-disable quote-props */ -const dotenv = require('dotenv'); -const path = require('path'); -const webpack = require('webpack'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const TerserPlugin = require('terser-webpack-plugin'); -const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); -const HtmlWebpackInjectPreload = require('@principalstudio/html-webpack-inject-preload'); +const dotenv = require("dotenv"); +const path = require("path"); +const webpack = require("webpack"); +const HtmlWebpackPlugin = require("html-webpack-plugin"); +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); +const TerserPlugin = require("terser-webpack-plugin"); +const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); +const HtmlWebpackInjectPreload = require("@principalstudio/html-webpack-inject-preload"); const SentryCliPlugin = require("@sentry/webpack-plugin"); // Environment variables @@ -17,7 +17,7 @@ const SentryCliPlugin = require("@sentry/webpack-plugin"); dotenv.config(); let ogImageUrl = process.env.RIOT_OG_IMAGE_URL; -if (!ogImageUrl) ogImageUrl = 'https://app.element.io/themes/element/img/logos/opengraph.png'; +if (!ogImageUrl) ogImageUrl = "https://app.element.io/themes/element/img/logos/opengraph.png"; if (!process.env.VERSION) { console.warn("Unset VERSION variable - this may affect build output"); @@ -38,14 +38,19 @@ const cssThemes = { function getActiveThemes() { // Default to `light` theme when the MATRIX_THEMES environment variable is not defined. - const theme = process.env.MATRIX_THEMES ?? 'light'; - return theme.split(',').map(x => x.trim()).filter(Boolean); + const theme = process.env.MATRIX_THEMES ?? "light"; + return theme + .split(",") + .map((x) => x.trim()) + .filter(Boolean); } // See docs/customisations.md -let fileOverrides = {/* {[file: string]: string} */}; +let fileOverrides = { + /* {[file: string]: string} */ +}; try { - fileOverrides = require('./customisations.json'); + fileOverrides = require("./customisations.json"); // stringify the output so it appears in logs correctly, as large files can sometimes get // represented as `` which is less than helpful. @@ -59,14 +64,14 @@ function parseOverridesToReplacements(overrides) { return new webpack.NormalModuleReplacementPlugin( // because the input is effectively defined by the person running the build, we don't // need to do anything special to protect against regex overrunning, etc. - new RegExp(oldPath.replace(/\//g, '[\\/\\\\]').replace(/\./g, '\\.')), + new RegExp(oldPath.replace(/\//g, "[\\/\\\\]").replace(/\./g, "\\.")), path.resolve(__dirname, newPath), ); }); } const moduleReplacementPlugins = [ - ...parseOverridesToReplacements(require('./components.json')), + ...parseOverridesToReplacements(require("./components.json")), // Allow customisations to override the default components too ...parseOverridesToReplacements(fileOverrides), @@ -82,42 +87,42 @@ module.exports = (env, argv) => { // process.env.CI_PACKAGE is set when yarn build is called from scripts/ci_package.sh // (called to build nightly and develop.element.io) const nodeEnv = argv.mode; - const devMode = nodeEnv !== 'production'; - const useHMR = process.env.CSS_HOT_RELOAD === '1' && devMode; + const devMode = nodeEnv !== "production"; + const useHMR = process.env.CSS_HOT_RELOAD === "1" && devMode; const enableMinification = !devMode && !process.env.CI_PACKAGE; const development = {}; if (devMode) { // High quality, embedded source maps for dev builds - development['devtool'] = "eval-source-map"; + development["devtool"] = "eval-source-map"; } else { if (process.env.CI_PACKAGE) { // High quality source maps in separate .map files which include the source. This doesn't bulk up the .js // payload file size, which is nice for performance but also necessary to get the bundle to a small enough // size that sentry will accept the upload. - development['devtool'] = 'source-map'; + development["devtool"] = "source-map"; } else { // High quality source maps in separate .map files which don't include the source - development['devtool'] = 'nosources-source-map'; + development["devtool"] = "nosources-source-map"; } } // Resolve the directories for the react-sdk and js-sdk for later use. We resolve these early, so we // don't have to call them over and over. We also resolve to the package.json instead of the src // directory, so we don't have to rely on an index.js or similar file existing. - const reactSdkSrcDir = path.resolve(require.resolve("matrix-react-sdk/package.json"), '..', 'src'); - const jsSdkSrcDir = path.resolve(require.resolve("matrix-js-sdk/package.json"), '..', 'src'); + const reactSdkSrcDir = path.resolve(require.resolve("matrix-react-sdk/package.json"), "..", "src"); + const jsSdkSrcDir = path.resolve(require.resolve("matrix-js-sdk/package.json"), "..", "src"); const ACTIVE_THEMES = getActiveThemes(); function getThemesImports() { const imports = ACTIVE_THEMES.map((t) => { - return cssThemes[`theme-${ t }`].replace('./node_modules/', ''); // theme import path + return cssThemes[`theme-${t}`].replace("./node_modules/", ""); // theme import path }); const s = JSON.stringify(ACTIVE_THEMES); return ` window.MX_insertedThemeStylesCounter = 0; - window.MX_DEV_ACTIVE_THEMES = (${ s }); - ${ imports.map(i => `import("${ i }")`).join('\n') }; + window.MX_DEV_ACTIVE_THEMES = (${s}); + ${imports.map((i) => `import("${i}")`).join("\n")}; `; } @@ -125,17 +130,17 @@ module.exports = (env, argv) => { ...development, node: { // Mock out the NodeFS module: The opus decoder imports this wrongly. - fs: 'empty', - net: 'empty', - tls: 'empty', - crypto: 'empty', + fs: "empty", + net: "empty", + tls: "empty", + crypto: "empty", }, entry: { - "bundle": "./src/vector/index.ts", - "mobileguide": "./src/vector/mobile_guide/index.ts", - "jitsi": "./src/vector/jitsi/index.ts", - "usercontent": "./node_modules/matrix-react-sdk/src/usercontent/index.ts", + bundle: "./src/vector/index.ts", + mobileguide: "./src/vector/mobile_guide/index.ts", + jitsi: "./src/vector/jitsi/index.ts", + usercontent: "./node_modules/matrix-react-sdk/src/usercontent/index.ts", ...(useHMR ? {} : cssThemes), }, @@ -146,7 +151,7 @@ module.exports = (env, argv) => { splitChunks: { cacheGroups: { styles: { - name: 'styles', + name: "styles", test: /\.css$/, enforce: true, // Do not add `chunks: 'all'` here because you'll break the app entry point. @@ -181,26 +186,26 @@ module.exports = (env, argv) => { // the package.json for the dependency. Instead, we rely on the package.json of each // layer to have our custom alternate fields to load things in the right order. These are // the defaults of webpack prepended with `matrix_src_`. - mainFields: ['matrix_src_browser', 'matrix_src_main', 'browser', 'main'], - aliasFields: ['matrix_src_browser', 'browser'], + mainFields: ["matrix_src_browser", "matrix_src_main", "browser", "main"], + aliasFields: ["matrix_src_browser", "browser"], // We need to specify that TS can be resolved without an extension - extensions: ['.js', '.json', '.ts', '.tsx'], + extensions: [".js", ".json", ".ts", ".tsx"], alias: { // alias any requires to the react module to the one in our path, // otherwise we tend to get the react source included twice when // using `npm link` / `yarn link`. - "react": path.resolve(__dirname, 'node_modules/react'), - "react-dom": path.resolve(__dirname, 'node_modules/react-dom'), + "react": path.resolve(__dirname, "node_modules/react"), + "react-dom": path.resolve(__dirname, "node_modules/react-dom"), // Same goes for js/react-sdk - we don't need two copies. - "matrix-js-sdk": path.resolve(__dirname, 'node_modules/matrix-js-sdk'), - "matrix-react-sdk": path.resolve(__dirname, 'node_modules/matrix-react-sdk'), + "matrix-js-sdk": path.resolve(__dirname, "node_modules/matrix-js-sdk"), + "matrix-react-sdk": path.resolve(__dirname, "node_modules/matrix-react-sdk"), // and sanitize-html - "sanitize-html": path.resolve(__dirname, 'node_modules/sanitize-html'), + "sanitize-html": path.resolve(__dirname, "node_modules/sanitize-html"), // Define a variable so the i18n stuff can load - "$webapp": path.resolve(__dirname, 'webapp'), + "$webapp": path.resolve(__dirname, "webapp"), }, }, @@ -222,7 +227,7 @@ module.exports = (env, argv) => { rules: [ useHMR && { test: /devcss\.ts$/, - loader: 'string-replace-loader', + loader: "string-replace-loader", options: { search: '"use theming";', replace: getThemesImports(), @@ -236,7 +241,7 @@ module.exports = (env, argv) => { test: /\.(ts|js)x?$/, include: (f) => { // our own source needs babel-ing - if (f.startsWith(path.resolve(__dirname, 'src'))) return true; + if (f.startsWith(path.resolve(__dirname, "src"))) return true; // we use the original source files of react-sdk and js-sdk, so we need to // run them through babel. Because the path tested is the resolved, absolute @@ -251,7 +256,7 @@ module.exports = (env, argv) => { // not necessary anyway). So, for anything else, don't babel. return false; }, - loader: 'babel-loader', + loader: "babel-loader", options: { cacheDirectory: true, }, @@ -261,18 +266,18 @@ module.exports = (env, argv) => { use: [ MiniCssExtractPlugin.loader, { - loader: 'css-loader', + loader: "css-loader", options: { importLoaders: 1, sourceMap: true, }, }, { - loader: 'postcss-loader', - ident: 'postcss', + loader: "postcss-loader", + ident: "postcss", options: { - sourceMap: true, - plugins: () => [ + "sourceMap": true, + "plugins": () => [ // Note that we use significantly fewer plugins on the plain // CSS parser. If we start to parse plain CSS, we end with all // kinds of nasty problems (like stylesheets not loading). @@ -296,9 +301,9 @@ module.exports = (env, argv) => { // It's important that this plugin is last otherwise we end // up with broken CSS. - require('postcss-preset-env')({ stage: 3, browsers: 'last 2 versions' }), + require("postcss-preset-env")({ stage: 3, browsers: "last 2 versions" }), ], - parser: "postcss-scss", + "parser": "postcss-scss", "local-plugins": true, }, }, @@ -312,50 +317,52 @@ module.exports = (env, argv) => { * of the JS/TS files. * Should be MUCH better with webpack 5, but we're stuck to this solution for now. */ - useHMR ? { - loader: 'style-loader', - /** - * If we refactor the `theme.js` in `matrix-react-sdk` a little bit, - * we could try using `lazyStyleTag` here to add and remove styles on demand, - * that would nicely resolve issues of race conditions for themes, - * at least for development purposes. - */ - options: { - - insert: function insertBeforeAt(element) { - const parent = document.querySelector('head'); - // We're in iframe - if (!window.MX_DEV_ACTIVE_THEMES) { - parent.appendChild(element); - return; - } - // Properly disable all other instances of themes - element.disabled = true; - element.onload = () => { - element.disabled = true; - }; - const theme = window.MX_DEV_ACTIVE_THEMES[window.MX_insertedThemeStylesCounter]; - element.setAttribute('data-mx-theme', theme); - window.MX_insertedThemeStylesCounter++; - parent.appendChild(element); - }, - }, - } : MiniCssExtractPlugin.loader, + useHMR + ? { + loader: "style-loader", + /** + * If we refactor the `theme.js` in `matrix-react-sdk` a little bit, + * we could try using `lazyStyleTag` here to add and remove styles on demand, + * that would nicely resolve issues of race conditions for themes, + * at least for development purposes. + */ + options: { + insert: function insertBeforeAt(element) { + const parent = document.querySelector("head"); + // We're in iframe + if (!window.MX_DEV_ACTIVE_THEMES) { + parent.appendChild(element); + return; + } + // Properly disable all other instances of themes + element.disabled = true; + element.onload = () => { + element.disabled = true; + }; + const theme = + window.MX_DEV_ACTIVE_THEMES[window.MX_insertedThemeStylesCounter]; + element.setAttribute("data-mx-theme", theme); + window.MX_insertedThemeStylesCounter++; + parent.appendChild(element); + }, + }, + } + : MiniCssExtractPlugin.loader, { - loader: 'css-loader', + loader: "css-loader", options: { importLoaders: 1, sourceMap: true, }, }, { - loader: 'postcss-loader', - ident: 'postcss', + loader: "postcss-loader", + ident: "postcss", options: { - sourceMap: true, - plugins: () => [ + "sourceMap": true, + "plugins": () => [ // Note that we use slightly different plugins for PostCSS. - require('postcss-import')(), + require("postcss-import")(), require("postcss-mixins")(), require("postcss-simple-vars")(), require("postcss-nested")(), @@ -364,9 +371,9 @@ module.exports = (env, argv) => { // It's important that this plugin is last otherwise we end // up with broken CSS. - require('postcss-preset-env')({ stage: 3, browsers: 'last 2 versions' }), + require("postcss-preset-env")({ stage: 3, browsers: "last 2 versions" }), ], - parser: "postcss-scss", + "parser": "postcss-scss", "local-plugins": true, }, }, @@ -382,8 +389,8 @@ module.exports = (env, argv) => { loader: "file-loader", type: "javascript/auto", options: { - name: '[name].[hash:7].[ext]', - outputPath: '.', + name: "[name].[hash:7].[ext]", + outputPath: ".", }, }, { @@ -394,8 +401,8 @@ module.exports = (env, argv) => { type: "javascript/auto", options: { // We deliberately override the name so it makes sense in debugging - name: 'opus-encoderWorker.min.[hash:7].[ext]', - outputPath: '.', + name: "opus-encoderWorker.min.[hash:7].[ext]", + outputPath: ".", }, }, { @@ -404,12 +411,13 @@ module.exports = (env, argv) => { // however it seems to work fine for our purposes. test: /RecorderWorklet\.ts$/, type: "javascript/auto", - use: [ // executed last -> first, for some reason. + use: [ + // executed last -> first, for some reason. { loader: "worklet-loader", options: { // Override name so we know what it is in the output. - name: 'recorder-worklet.[hash:7].js', + name: "recorder-worklet.[hash:7].js", }, }, { @@ -426,8 +434,8 @@ module.exports = (env, argv) => { type: "javascript/auto", // https://github.com/webpack/webpack/issues/6725 options: { // We deliberately override the name so it makes sense in debugging - name: 'opus-decoderWorker.min.[hash:7].[ext]', - outputPath: '.', + name: "opus-decoderWorker.min.[hash:7].[ext]", + outputPath: ".", }, }, { @@ -439,8 +447,8 @@ module.exports = (env, argv) => { options: { // We deliberately don't change the name because the decoderWorker has this // hardcoded. This is here to avoid the default wasm rule from adding a hash. - name: 'decoderWorker.min.wasm', - outputPath: '.', + name: "decoderWorker.min.wasm", + outputPath: ".", }, }, { @@ -451,8 +459,8 @@ module.exports = (env, argv) => { type: "javascript/auto", // https://github.com/webpack/webpack/issues/6725 options: { // We deliberately override the name so it makes sense in debugging - name: 'wave-encoderWorker.min.[hash:7].[ext]', - outputPath: '.', + name: "wave-encoderWorker.min.[hash:7].[ext]", + outputPath: ".", }, }, { @@ -460,9 +468,9 @@ module.exports = (env, argv) => { // element-web/webapp/i18n during build by copy-res.js test: /\.*languages.json$/, type: "javascript/auto", - loader: 'file-loader', + loader: "file-loader", options: { - name: 'i18n/[name].[hash:7].[ext]', + name: "i18n/[name].[hash:7].[ext]", }, }, { @@ -470,15 +478,15 @@ module.exports = (env, argv) => { issuer: /\.(js|ts|jsx|tsx|html)$/, use: [ { - loader: '@svgr/webpack', + loader: "@svgr/webpack", options: { - namedExport: 'Icon', + namedExport: "Icon", svgProps: { - role: 'presentation', - 'aria-hidden': true, + "role": "presentation", + "aria-hidden": true, }, // props set on the svg will override defaults - expandProps: 'end', + expandProps: "end", svgoConfig: { plugins: { // generates a viewbox if missing @@ -486,21 +494,21 @@ module.exports = (env, argv) => { }, }, esModule: false, - name: '[name].[hash:7].[ext]', + name: "[name].[hash:7].[ext]", outputPath: getAssetOutputPath, - publicPath: function(url, resourcePath) { + publicPath: function (url, resourcePath) { const outputPath = getAssetOutputPath(url, resourcePath); return toPublicPath(outputPath); }, }, }, { - loader: 'file-loader', + loader: "file-loader", options: { esModule: false, - name: '[name].[hash:7].[ext]', + name: "[name].[hash:7].[ext]", outputPath: getAssetOutputPath, - publicPath: function(url, resourcePath) { + publicPath: function (url, resourcePath) { const outputPath = getAssetOutputPath(url, resourcePath); return toPublicPath(outputPath); }, @@ -513,12 +521,12 @@ module.exports = (env, argv) => { issuer: /\.(pcss|scss|css)$/, use: [ { - loader: 'file-loader', + loader: "file-loader", options: { esModule: false, - name: '[name].[hash:7].[ext]', + name: "[name].[hash:7].[ext]", outputPath: getAssetOutputPath, - publicPath: function(url, resourcePath) { + publicPath: function (url, resourcePath) { // CSS image usages end up in the `bundles/[hash]` output // directory, so we adjust the final path to navigate up // twice. @@ -537,12 +545,12 @@ module.exports = (env, argv) => { { // Assets referenced in CSS files issuer: /\.(pcss|scss|css)$/, - loader: 'file-loader', + loader: "file-loader", options: { esModule: false, - name: '[name].[hash:7].[ext]', + name: "[name].[hash:7].[ext]", outputPath: getAssetOutputPath, - publicPath: function(url, resourcePath) { + publicPath: function (url, resourcePath) { // CSS image usages end up in the `bundles/[hash]` output // directory, so we adjust the final path to navigate up // twice. @@ -553,12 +561,12 @@ module.exports = (env, argv) => { }, { // Assets referenced in HTML and JS files - loader: 'file-loader', + loader: "file-loader", options: { esModule: false, - name: '[name].[hash:7].[ext]', + name: "[name].[hash:7].[ext]", outputPath: getAssetOutputPath, - publicPath: function(url, resourcePath) { + publicPath: function (url, resourcePath) { const outputPath = getAssetOutputPath(url, resourcePath); return toPublicPath(outputPath); }, @@ -581,13 +589,13 @@ module.exports = (env, argv) => { // This is the app's main entry point. new HtmlWebpackPlugin({ - template: './src/vector/index.html', + template: "./src/vector/index.html", // we inject the links ourselves via the template, because // HtmlWebpackPlugin will screw up our formatting like the names // of the themes and which chunks we actually care about. inject: false, - excludeChunks: ['mobileguide', 'usercontent', 'jitsi'], + excludeChunks: ["mobileguide", "usercontent", "jitsi"], minify: false, templateParameters: { og_image_url: ogImageUrl, @@ -597,40 +605,40 @@ module.exports = (env, argv) => { // This is the jitsi widget wrapper (embedded, so isolated stack) new HtmlWebpackPlugin({ - template: './src/vector/jitsi/index.html', - filename: 'jitsi.html', + template: "./src/vector/jitsi/index.html", + filename: "jitsi.html", minify: false, - chunks: ['jitsi'], + chunks: ["jitsi"], }), // This is the mobile guide's entry point (separate for faster mobile loading) new HtmlWebpackPlugin({ - template: './src/vector/mobile_guide/index.html', - filename: 'mobile_guide/index.html', + template: "./src/vector/mobile_guide/index.html", + filename: "mobile_guide/index.html", minify: false, - chunks: ['mobileguide'], + chunks: ["mobileguide"], }), // These are the static error pages for when the javascript env is *really unsupported* new HtmlWebpackPlugin({ - template: './src/vector/static/unable-to-load.html', - filename: 'static/unable-to-load.html', + template: "./src/vector/static/unable-to-load.html", + filename: "static/unable-to-load.html", minify: false, chunks: [], }), new HtmlWebpackPlugin({ - template: './src/vector/static/incompatible-browser.html', - filename: 'static/incompatible-browser.html', + template: "./src/vector/static/incompatible-browser.html", + filename: "static/incompatible-browser.html", minify: false, chunks: [], }), // This is the usercontent sandbox's entry point (separate for iframing) new HtmlWebpackPlugin({ - template: './node_modules/matrix-react-sdk/src/usercontent/index.html', - filename: 'usercontent/index.html', + template: "./node_modules/matrix-react-sdk/src/usercontent/index.html", + filename: "usercontent/index.html", minify: false, - chunks: ['usercontent'], + chunks: ["usercontent"], }), new HtmlWebpackInjectPreload({ @@ -643,11 +651,11 @@ module.exports = (env, argv) => { release: process.env.VERSION, include: "./webapp/bundles", errorHandler: (err, invokeErr, compilation) => { - compilation.warnings.push('Sentry CLI Plugin: ' + err.message); + compilation.warnings.push("Sentry CLI Plugin: " + err.message); console.log(`::warning title=Sentry error::${err.message}`); }, }), - new webpack.EnvironmentPlugin(['VERSION']), + new webpack.EnvironmentPlugin(["VERSION"]), ].filter(Boolean), output: { @@ -668,13 +676,11 @@ module.exports = (env, argv) => { // configuration for the webpack-dev-server devServer: { // serve unwebpacked assets from webapp. - contentBase: [ - './webapp', - ], + contentBase: ["./webapp"], // Only output errors, warnings, or new compilations. // This hides the massive list of modules. - stats: 'minimal', + stats: "minimal", hotOnly: true, inline: true, }, @@ -713,5 +719,5 @@ function getAssetOutputPath(url, resourcePath) { * @returns {string} converted path */ function toPublicPath(path) { - return path.replace(/\\/g, '/'); + return path.replace(/\\/g, "/"); }