diff --git a/.eslintrc.js b/.eslintrc.js
index bc2a142c2d..99695b7a03 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -22,6 +22,8 @@ module.exports = {
"files": ["src/**/*.{ts,tsx}"],
"extends": ["matrix-org/ts"],
"rules": {
+ // We're okay being explicit at the moment
+ "@typescript-eslint/no-empty-interface": "off",
// We disable this while we're transitioning
"@typescript-eslint/no-explicit-any": "off",
// We'd rather not do this but we do
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ef31ef886f..5d1595396b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,185 @@
+Changes in [3.13.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.13.0) (2021-02-03)
+=====================================================================================================
+[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.13.0-rc.1...v3.13.0)
+
+ * Upgrade to JS SDK 9.6.0
+ * [Release] Fix flair height after accent changes
+ [\#5612](https://github.com/matrix-org/matrix-react-sdk/pull/5612)
+ * [Release] Iterate Social Logins work around edge cases and branding
+ [\#5610](https://github.com/matrix-org/matrix-react-sdk/pull/5610)
+ * [Release] Lock widget room ID when added
+ [\#5608](https://github.com/matrix-org/matrix-react-sdk/pull/5608)
+ * [Release] Better errors for SSO failures
+ [\#5606](https://github.com/matrix-org/matrix-react-sdk/pull/5606)
+ * [Release] Fix RoomView re-mounting breaking peeking
+ [\#5603](https://github.com/matrix-org/matrix-react-sdk/pull/5603)
+
+Changes in [3.13.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.13.0-rc.1) (2021-01-29)
+===============================================================================================================
+[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.12.1...v3.13.0-rc.1)
+
+ * Upgrade to JS SDK 9.6.0-rc.1
+ * Translations update from Weblate
+ [\#5597](https://github.com/matrix-org/matrix-react-sdk/pull/5597)
+ * Support managed hybrid widgets from config
+ [\#5596](https://github.com/matrix-org/matrix-react-sdk/pull/5596)
+ * Add managed hybrid call widgets when supported
+ [\#5594](https://github.com/matrix-org/matrix-react-sdk/pull/5594)
+ * Tweak mobile guide toast copy
+ [\#5595](https://github.com/matrix-org/matrix-react-sdk/pull/5595)
+ * Improve SSO auth flow
+ [\#5578](https://github.com/matrix-org/matrix-react-sdk/pull/5578)
+ * Add optional mobile guide toast
+ [\#5586](https://github.com/matrix-org/matrix-react-sdk/pull/5586)
+ * Fix invisible text after logging out in the dark theme
+ [\#5588](https://github.com/matrix-org/matrix-react-sdk/pull/5588)
+ * Fix escape for cancelling replies
+ [\#5591](https://github.com/matrix-org/matrix-react-sdk/pull/5591)
+ * Update widget-api to beta.12
+ [\#5589](https://github.com/matrix-org/matrix-react-sdk/pull/5589)
+ * Add commands for DM conversion
+ [\#5540](https://github.com/matrix-org/matrix-react-sdk/pull/5540)
+ * Run a UI refresh over the OIDC Exchange confirmation dialog
+ [\#5580](https://github.com/matrix-org/matrix-react-sdk/pull/5580)
+ * Allow stickerpickers the legacy "visibility" capability
+ [\#5581](https://github.com/matrix-org/matrix-react-sdk/pull/5581)
+ * Hide local video if it is muted
+ [\#5529](https://github.com/matrix-org/matrix-react-sdk/pull/5529)
+ * Don't use name width in reply thread for IRC layout
+ [\#5518](https://github.com/matrix-org/matrix-react-sdk/pull/5518)
+ * Update code_style.md
+ [\#5554](https://github.com/matrix-org/matrix-react-sdk/pull/5554)
+ * Fix Czech capital letters like ŠČŘ...
+ [\#5569](https://github.com/matrix-org/matrix-react-sdk/pull/5569)
+ * Add optional search shortcut
+ [\#5548](https://github.com/matrix-org/matrix-react-sdk/pull/5548)
+ * Fix Sudden 'find a room' UI shows up when the only room moves to favourites
+ [\#5584](https://github.com/matrix-org/matrix-react-sdk/pull/5584)
+ * Increase PersistedElement's z-index
+ [\#5568](https://github.com/matrix-org/matrix-react-sdk/pull/5568)
+ * Remove check that prevents Jitsi widgets from being unpinned
+ [\#5582](https://github.com/matrix-org/matrix-react-sdk/pull/5582)
+ * Fix Jitsi widgets causing localized tile crashes
+ [\#5583](https://github.com/matrix-org/matrix-react-sdk/pull/5583)
+ * Log candidates for calls
+ [\#5573](https://github.com/matrix-org/matrix-react-sdk/pull/5573)
+ * Upgrade deps 2021-01
+ [\#5579](https://github.com/matrix-org/matrix-react-sdk/pull/5579)
+ * Fix "Continuing without email" dialog bug
+ [\#5566](https://github.com/matrix-org/matrix-react-sdk/pull/5566)
+ * Require registration for verification actions
+ [\#5574](https://github.com/matrix-org/matrix-react-sdk/pull/5574)
+ * Don't play the hangup sound when the call is answered from elsewhere
+ [\#5572](https://github.com/matrix-org/matrix-react-sdk/pull/5572)
+ * Move to newer base image for end-to-end tests
+ [\#5570](https://github.com/matrix-org/matrix-react-sdk/pull/5570)
+ * Update widgets in the room upon join
+ [\#5564](https://github.com/matrix-org/matrix-react-sdk/pull/5564)
+ * Update AuxPanel and related buttons when widgets change or on reload
+ [\#5563](https://github.com/matrix-org/matrix-react-sdk/pull/5563)
+ * Add VoIP user mapper
+ [\#5560](https://github.com/matrix-org/matrix-react-sdk/pull/5560)
+ * Improve styling of SSO Buttons for multiple IdPs
+ [\#5558](https://github.com/matrix-org/matrix-react-sdk/pull/5558)
+ * Fixes for the general tab in the room dialog
+ [\#5522](https://github.com/matrix-org/matrix-react-sdk/pull/5522)
+ * fix issue 16226 to allow switching back to default HS.
+ [\#5561](https://github.com/matrix-org/matrix-react-sdk/pull/5561)
+ * Support room-defined widget layouts
+ [\#5553](https://github.com/matrix-org/matrix-react-sdk/pull/5553)
+ * Change a bunch of strings from Recovery Key/Phrase to Security Key/Phrase
+ [\#5533](https://github.com/matrix-org/matrix-react-sdk/pull/5533)
+ * Give a bigger target area to AppsDrawer vertical resizer
+ [\#5557](https://github.com/matrix-org/matrix-react-sdk/pull/5557)
+ * Fix minimized left panel avatar alignment
+ [\#5493](https://github.com/matrix-org/matrix-react-sdk/pull/5493)
+ * Ensure component index has been written before renaming
+ [\#5556](https://github.com/matrix-org/matrix-react-sdk/pull/5556)
+ * Fixed continue button while selecting home-server
+ [\#5552](https://github.com/matrix-org/matrix-react-sdk/pull/5552)
+ * Wire up MSC2931 widget navigation
+ [\#5527](https://github.com/matrix-org/matrix-react-sdk/pull/5527)
+ * Various fixes for Bridge Info page (MSC2346)
+ [\#5454](https://github.com/matrix-org/matrix-react-sdk/pull/5454)
+ * Use room-specific listeners for message preview and community prototype
+ [\#5547](https://github.com/matrix-org/matrix-react-sdk/pull/5547)
+ * Fix some misc. React warnings when viewing timeline
+ [\#5546](https://github.com/matrix-org/matrix-react-sdk/pull/5546)
+ * Use device storage for allowed widgets if account data not supported
+ [\#5544](https://github.com/matrix-org/matrix-react-sdk/pull/5544)
+ * Fix incoming call box on dark theme
+ [\#5542](https://github.com/matrix-org/matrix-react-sdk/pull/5542)
+ * Convert DMRoomMap to typescript
+ [\#5541](https://github.com/matrix-org/matrix-react-sdk/pull/5541)
+ * Add in-call dialpad for DTMF sending
+ [\#5532](https://github.com/matrix-org/matrix-react-sdk/pull/5532)
+
+Changes in [3.12.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.12.1) (2021-01-26)
+=====================================================================================================
+[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.12.0...v3.12.1)
+
+ * Upgrade to JS SDK 9.5.1
+
+Changes in [3.12.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.12.0) (2021-01-18)
+=====================================================================================================
+[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.12.0-rc.1...v3.12.0)
+
+ * Upgrade to JS SDK 9.5.0
+ * Fix incoming call box on dark theme
+ [\#5543](https://github.com/matrix-org/matrix-react-sdk/pull/5543)
+
+Changes in [3.12.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.12.0-rc.1) (2021-01-13)
+===============================================================================================================
+[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.11.1...v3.12.0-rc.1)
+
+ * Upgrade to JS SDK 9.5.0-rc.1
+ * Fix soft crash on soft logout page
+ [\#5539](https://github.com/matrix-org/matrix-react-sdk/pull/5539)
+ * Translations update from Weblate
+ [\#5538](https://github.com/matrix-org/matrix-react-sdk/pull/5538)
+ * Run TypeScript tests
+ [\#5537](https://github.com/matrix-org/matrix-react-sdk/pull/5537)
+ * Add a basic widget explorer to devtools (per-room)
+ [\#5528](https://github.com/matrix-org/matrix-react-sdk/pull/5528)
+ * Add to security key field
+ [\#5534](https://github.com/matrix-org/matrix-react-sdk/pull/5534)
+ * Fix avatar upload prompt/tooltip floating wrong and permissions
+ [\#5526](https://github.com/matrix-org/matrix-react-sdk/pull/5526)
+ * Add a dialpad UI for PSTN lookup
+ [\#5523](https://github.com/matrix-org/matrix-react-sdk/pull/5523)
+ * Basic call transfer initiation support
+ [\#5494](https://github.com/matrix-org/matrix-react-sdk/pull/5494)
+ * Fix #15988
+ [\#5524](https://github.com/matrix-org/matrix-react-sdk/pull/5524)
+ * Bump node-notifier from 8.0.0 to 8.0.1
+ [\#5520](https://github.com/matrix-org/matrix-react-sdk/pull/5520)
+ * Use TypeScript source for development, swap to build during release
+ [\#5503](https://github.com/matrix-org/matrix-react-sdk/pull/5503)
+ * Look for emoji in the body that will be displayed
+ [\#5517](https://github.com/matrix-org/matrix-react-sdk/pull/5517)
+ * Bump ini from 1.3.5 to 1.3.7
+ [\#5486](https://github.com/matrix-org/matrix-react-sdk/pull/5486)
+ * Recognise `*.element.io` links as Element permalinks
+ [\#5514](https://github.com/matrix-org/matrix-react-sdk/pull/5514)
+ * Fixes for call UI
+ [\#5509](https://github.com/matrix-org/matrix-react-sdk/pull/5509)
+ * Add a snowfall chat effect (with /snowfall command)
+ [\#5511](https://github.com/matrix-org/matrix-react-sdk/pull/5511)
+ * fireworks effect
+ [\#5507](https://github.com/matrix-org/matrix-react-sdk/pull/5507)
+ * Don't play call end sound for calls that never started
+ [\#5506](https://github.com/matrix-org/matrix-react-sdk/pull/5506)
+ * Add /tableflip slash command
+ [\#5485](https://github.com/matrix-org/matrix-react-sdk/pull/5485)
+ * Import from src in IncomingCallBox.tsx
+ [\#5504](https://github.com/matrix-org/matrix-react-sdk/pull/5504)
+ * Social Login support both https and mxc icons
+ [\#5499](https://github.com/matrix-org/matrix-react-sdk/pull/5499)
+ * Fix padding in confirmation email registration prompt
+ [\#5501](https://github.com/matrix-org/matrix-react-sdk/pull/5501)
+ * Fix room list help prompt alignment
+ [\#5500](https://github.com/matrix-org/matrix-react-sdk/pull/5500)
+
Changes in [3.11.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.11.1) (2020-12-21)
=====================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.11.0...v3.11.1)
diff --git a/code_style.md b/code_style.md
index fe04d2cc3d..5747540a76 100644
--- a/code_style.md
+++ b/code_style.md
@@ -35,12 +35,6 @@ General Style
- lowerCamelCase for functions and variables.
- Single line ternary operators are fine.
- UPPER_SNAKE_CASE for constants
-- Single quotes for strings by default, for consistency with most JavaScript styles:
-
- ```javascript
- "bad" // Bad
- 'good' // Good
- ```
- Use parentheses or `` ` `` instead of `\` for line continuation where ever possible
- Open braces on the same line (consistent with Node):
@@ -162,7 +156,14 @@ ECMAScript
- Be careful mixing arrow functions and regular functions, eg. if one function in a promise chain is an
arrow function, they probably all should be.
- Apart from that, newer ES features should be used whenever the author deems them to be appropriate.
-- Flow annotations are welcome and encouraged.
+
+TypeScript
+----------
+- TypeScript is preferred over the use of JavaScript
+- It's desirable to convert existing JavaScript files to TypeScript. TypeScript conversions should be done in small
+ chunks without functional changes to ease the review process.
+- Use full type definitions for function parameters and return values.
+- Avoid `any` types and `any` casts
React
-----
@@ -201,6 +202,8 @@ React
this.state = { counter: 0 };
}
```
+- Prefer class components over function components and hooks (not a strict rule though)
+
- Think about whether your component really needs state: are you duplicating
information in component state that could be derived from the model?
diff --git a/docs/widget-layouts.md b/docs/widget-layouts.md
new file mode 100644
index 0000000000..e7f72e2001
--- /dev/null
+++ b/docs/widget-layouts.md
@@ -0,0 +1,60 @@
+# Widget layout support
+
+Rooms can have a default widget layout to auto-pin certain widgets, make the container different
+sizes, etc. These are defined through the `io.element.widgets.layout` state event (empty state key).
+
+Full example content:
+```json5
+{
+ "widgets": {
+ "first-widget-id": {
+ "container": "top",
+ "index": 0,
+ "width": 60,
+ "height": 40
+ },
+ "second-widget-id": {
+ "container": "right"
+ }
+ }
+}
+```
+
+As shown, there are two containers possible for widgets. These containers have different behaviour
+and interpret the other options differently.
+
+## `top` container
+
+This is the "App Drawer" or any pinned widgets in a room. This is by far the most versatile container
+though does introduce potential usability issues upon members of the room (widgets take up space and
+therefore fewer messages can be shown).
+
+The `index` for a widget determines which order the widgets show up in from left to right. Widgets
+without an `index` will show up as the rightmost widgets. Tiebreaks (same `index` or multiple defined
+without an `index`) are resolved by comparing widget IDs. A maximum of 3 widgets can be in the top
+container - any which exceed this will be ignored (placed into the `right` container). Smaller numbers
+represent leftmost widgets.
+
+The `width` is relative width within the container in percentage points. This will be clamped to a
+range of 0-100 (inclusive). The widgets will attempt to scale to relative proportions when more than
+100% space is allocated. For example, if 3 widgets are defined at 40% width each then the client will
+attempt to show them at 33% width each.
+
+Note that the client may impose minimum widths on the widgets, such as a 10% minimum to avoid pinning
+hidden widgets. In general, widgets defined in the 30-70% range each will be free of these restrictions.
+
+The `height` is not in fact applied per-widget but is recorded per-widget for potential future
+capabilities in future containers. The top container will take the tallest `height` and use that for
+the height of the whole container, and thus all widgets in that container. The `height` is relative
+to the container, like with `width`, meaning that 100% will consume as much space as the client is
+willing to sacrifice to the widget container. Like with `width`, the client may impose minimums to avoid
+the container being uselessly small. Heights in the 30-100% range are generally acceptable. The height
+is also clamped to be within 0-100, inclusive.
+
+## `right` container
+
+This is the default container and has no special configuration. Widgets which overflow from the top
+container will be put in this container instead. Putting a widget in the right container does not
+automatically show it - it only mentions that widgets should not be in another container.
+
+The behaviour of this container may change in the future.
diff --git a/package.json b/package.json
index 92b574da34..a5f626d09a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "matrix-react-sdk",
- "version": "3.11.1",
+ "version": "3.13.0",
"description": "SDK for matrix.org using React",
"author": "matrix.org",
"repository": {
@@ -54,48 +54,47 @@
"test:e2e": "./test/end-to-end-tests/run.sh --app-url http://localhost:8080"
},
"dependencies": {
- "@babel/runtime": "^7.10.5",
- "await-lock": "^2.0.1",
- "blueimp-canvas-to-blob": "^3.27.0",
+ "@babel/runtime": "^7.12.5",
+ "await-lock": "^2.1.0",
+ "blueimp-canvas-to-blob": "^3.28.0",
"browser-encrypt-attachment": "^0.3.0",
"browser-request": "^0.3.3",
- "cheerio": "^1.0.0-rc.3",
+ "cheerio": "^1.0.0-rc.5",
"classnames": "^2.2.6",
- "commonmark": "^0.29.1",
+ "commonmark": "^0.29.3",
"counterpart": "^0.18.6",
- "diff-dom": "^4.1.6",
+ "diff-dom": "^4.2.2",
"diff-match-patch": "^1.0.5",
- "emojibase-data": "^5.0.1",
- "emojibase-regex": "^4.0.1",
+ "emojibase-data": "^5.1.1",
+ "emojibase-regex": "^4.1.1",
"escape-html": "^1.0.3",
- "file-saver": "^1.3.8",
- "filesize": "3.6.1",
+ "file-saver": "^2.0.5",
+ "filesize": "6.1.0",
"flux": "2.1.1",
- "focus-visible": "^5.1.0",
- "fuse.js": "^2.7.4",
+ "focus-visible": "^5.2.0",
"gfm.css": "^1.1.2",
"glob-to-regexp": "^0.4.1",
- "highlight.js": "^10.1.2",
- "html-entities": "^1.3.1",
- "is-ip": "^2.0.0",
+ "highlight.js": "^10.5.0",
+ "html-entities": "^1.4.0",
+ "is-ip": "^3.1.0",
"katex": "^0.12.0",
"linkifyjs": "^2.1.9",
- "lodash": "^4.17.19",
+ "lodash": "^4.17.20",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
- "matrix-widget-api": "^0.1.0-beta.10",
+ "matrix-widget-api": "^0.1.0-beta.13",
"minimist": "^1.2.5",
- "pako": "^1.0.11",
- "parse5": "^5.1.1",
+ "pako": "^2.0.3",
+ "parse5": "^6.0.1",
"png-chunks-extract": "^1.0.0",
- "project-name-generator": "^2.1.7",
+ "project-name-generator": "^2.1.9",
"prop-types": "^15.7.2",
"qrcode": "^1.4.4",
- "qs": "^6.9.4",
- "re-resizable": "^6.5.4",
- "react": "^16.13.1",
+ "qs": "^6.9.6",
+ "re-resizable": "^6.9.0",
+ "react": "^16.14.0",
"react-beautiful-dnd": "^4.0.1",
- "react-dom": "^16.13.1",
- "react-focus-lock": "^2.4.1",
+ "react-dom": "^16.14.0",
+ "react-focus-lock": "^2.5.0",
"react-transition-group": "^4.4.1",
"resize-observer-polyfill": "^1.5.1",
"rfc4648": "^1.4.0",
@@ -108,68 +107,71 @@
"zxcvbn": "^4.4.2"
},
"devDependencies": {
- "@babel/cli": "^7.10.5",
- "@babel/core": "^7.10.5",
- "@babel/parser": "^7.11.0",
- "@babel/plugin-proposal-class-properties": "^7.10.4",
- "@babel/plugin-proposal-decorators": "^7.10.5",
- "@babel/plugin-proposal-export-default-from": "^7.10.4",
- "@babel/plugin-proposal-numeric-separator": "^7.10.4",
- "@babel/plugin-proposal-object-rest-spread": "^7.10.4",
- "@babel/plugin-transform-flow-comments": "^7.10.4",
- "@babel/plugin-transform-runtime": "^7.10.5",
- "@babel/preset-env": "^7.10.4",
- "@babel/preset-flow": "^7.10.4",
- "@babel/preset-react": "^7.10.4",
- "@babel/preset-typescript": "^7.10.4",
- "@babel/register": "^7.10.5",
- "@babel/traverse": "^7.11.0",
- "@peculiar/webcrypto": "^1.1.3",
- "@types/classnames": "^2.2.10",
+ "@babel/cli": "^7.12.10",
+ "@babel/core": "^7.12.10",
+ "@babel/parser": "^7.12.11",
+ "@babel/plugin-proposal-class-properties": "^7.12.1",
+ "@babel/plugin-proposal-decorators": "^7.12.12",
+ "@babel/plugin-proposal-export-default-from": "^7.12.1",
+ "@babel/plugin-proposal-numeric-separator": "^7.12.7",
+ "@babel/plugin-proposal-object-rest-spread": "^7.12.1",
+ "@babel/plugin-transform-flow-comments": "^7.12.1",
+ "@babel/plugin-transform-runtime": "^7.12.10",
+ "@babel/preset-env": "^7.12.11",
+ "@babel/preset-flow": "^7.12.1",
+ "@babel/preset-react": "^7.12.10",
+ "@babel/preset-typescript": "^7.12.7",
+ "@babel/register": "^7.12.10",
+ "@babel/traverse": "^7.12.12",
+ "@peculiar/webcrypto": "^1.1.4",
+ "@sinonjs/fake-timers": "^7.0.2",
+ "@types/classnames": "^2.2.11",
"@types/counterpart": "^0.18.1",
"@types/flux": "^3.1.9",
"@types/jest": "^26.0.20",
"@types/linkifyjs": "^2.1.3",
- "@types/lodash": "^4.14.158",
+ "@types/lodash": "^4.14.168",
"@types/modernizr": "^3.5.3",
- "@types/node": "^12.12.51",
+ "@types/node": "^14.14.22",
"@types/pako": "^1.0.1",
- "@types/qrcode": "^1.3.4",
+ "@types/qrcode": "^1.3.5",
"@types/react": "^16.9",
- "@types/react-dom": "^16.9.8",
+ "@types/react-dom": "^16.9.10",
"@types/react-transition-group": "^4.4.0",
- "@types/sanitize-html": "^1.23.3",
+ "@types/sanitize-html": "^1.27.0",
"@types/zxcvbn": "^4.4.0",
- "@typescript-eslint/eslint-plugin": "^3.7.0",
- "@typescript-eslint/parser": "^3.7.0",
+ "@typescript-eslint/eslint-plugin": "^4.14.0",
+ "@typescript-eslint/parser": "^4.14.0",
"babel-eslint": "^10.1.0",
- "babel-jest": "^24.9.0",
- "chokidar": "^3.4.1",
- "concurrently": "^4.1.2",
+ "babel-jest": "^26.6.3",
+ "chokidar": "^3.5.1",
+ "concurrently": "^5.3.0",
"enzyme": "^3.11.0",
- "enzyme-adapter-react-16": "^1.15.2",
- "eslint": "7.5.0",
- "eslint-config-matrix-org": "^0.1.2",
+ "enzyme-adapter-react-16": "^1.15.6",
+ "eslint": "7.18.0",
+ "eslint-config-matrix-org": "^0.2.0",
"eslint-plugin-babel": "^5.3.1",
- "eslint-plugin-flowtype": "^2.50.3",
- "eslint-plugin-react": "^7.20.3",
- "eslint-plugin-react-hooks": "^2.5.1",
- "glob": "^5.0.15",
- "jest": "^26.5.2",
+ "eslint-plugin-flowtype": "^5.2.0",
+ "eslint-plugin-react": "^7.22.0",
+ "eslint-plugin-react-hooks": "^4.2.0",
+ "glob": "^7.1.6",
+ "jest": "^26.6.3",
"jest-canvas-mock": "^2.3.0",
"jest-environment-jsdom-sixteen": "^1.0.3",
- "lolex": "^5.1.2",
"matrix-mock-request": "^1.2.3",
"matrix-react-test-utils": "^0.2.2",
"olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
- "react-test-renderer": "^16.13.1",
- "rimraf": "^2.7.1",
- "stylelint": "^9.10.1",
- "stylelint-config-standard": "^18.3.0",
+ "react-test-renderer": "^16.14.0",
+ "rimraf": "^3.0.2",
+ "stylelint": "^13.9.0",
+ "stylelint-config-standard": "^20.0.0",
"stylelint-scss": "^3.18.0",
- "typescript": "^3.9.7",
+ "typescript": "^4.1.3",
"walk": "^2.3.14"
},
+ "resolutions": {
+ "**/@types/react": "^16.14"
+ },
"jest": {
"testEnvironment": "./__test-utils__/environment.js",
"testMatch": [
diff --git a/res/css/structures/_LeftPanel.scss b/res/css/structures/_LeftPanel.scss
index 1424d9cda0..168590502d 100644
--- a/res/css/structures/_LeftPanel.scss
+++ b/res/css/structures/_LeftPanel.scss
@@ -180,6 +180,11 @@ $groupFilterPanelWidth: 56px; // only applies in this file, used for calculation
.mx_LeftPanel_roomListContainer {
width: 68px;
+ .mx_LeftPanel_userHeader {
+ flex-direction: row;
+ justify-content: center;
+ }
+
.mx_LeftPanel_filterContainer {
// Organize the flexbox into a centered column layout
flex-direction: column;
diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss
index 572c7166d2..36bf96359b 100644
--- a/res/css/structures/_RoomView.scss
+++ b/res/css/structures/_RoomView.scss
@@ -219,7 +219,7 @@ hr.mx_RoomView_myReadMarker {
position: relative;
top: -1px;
z-index: 1;
- transition: width 400ms easeInSine 1s, opacity 400ms easeInSine 1s;
+ transition: width 400ms easeinsine 1s, opacity 400ms easeinsine 1s;
width: 99%;
opacity: 1;
}
diff --git a/res/css/structures/_UserMenu.scss b/res/css/structures/_UserMenu.scss
index 014e7b4213..90292e812c 100644
--- a/res/css/structures/_UserMenu.scss
+++ b/res/css/structures/_UserMenu.scss
@@ -119,14 +119,10 @@ limitations under the License.
}
&.mx_UserMenu_minimized {
- .mx_UserMenu_userHeader {
- .mx_UserMenu_row {
- justify-content: center;
- }
+ padding-right: 0px;
- .mx_UserMenu_userAvatarContainer {
- margin-right: 0;
- }
+ .mx_UserMenu_userAvatarContainer {
+ margin-right: 0px;
}
}
}
diff --git a/res/css/views/auth/_AuthBody.scss b/res/css/views/auth/_AuthBody.scss
index 8f0c758e7a..90dca32e48 100644
--- a/res/css/views/auth/_AuthBody.scss
+++ b/res/css/views/auth/_AuthBody.scss
@@ -34,7 +34,7 @@ limitations under the License.
h3 {
font-size: $font-14px;
font-weight: 600;
- color: $authpage-primary-color;
+ color: $authpage-secondary-color;
}
h3.mx_AuthBody_centered {
diff --git a/res/css/views/auth/_AuthHeader.scss b/res/css/views/auth/_AuthHeader.scss
index b1372affee..13d5195160 100644
--- a/res/css/views/auth/_AuthHeader.scss
+++ b/res/css/views/auth/_AuthHeader.scss
@@ -18,7 +18,7 @@ limitations under the License.
display: flex;
flex-direction: column;
width: 206px;
- padding: 25px 40px;
+ padding: 25px 25px;
box-sizing: border-box;
}
diff --git a/res/css/views/auth/_AuthHeaderLogo.scss b/res/css/views/auth/_AuthHeaderLogo.scss
index 917dcabf67..86f0313b68 100644
--- a/res/css/views/auth/_AuthHeaderLogo.scss
+++ b/res/css/views/auth/_AuthHeaderLogo.scss
@@ -17,7 +17,7 @@ limitations under the License.
.mx_AuthHeaderLogo {
margin-top: 15px;
flex: 1;
- padding: 0 10px;
+ padding: 0 25px;
}
.mx_AuthHeaderLogo img {
diff --git a/res/css/views/auth/_InteractiveAuthEntryComponents.scss b/res/css/views/auth/_InteractiveAuthEntryComponents.scss
index 5df73b139f..ffaad3cd7a 100644
--- a/res/css/views/auth/_InteractiveAuthEntryComponents.scss
+++ b/res/css/views/auth/_InteractiveAuthEntryComponents.scss
@@ -83,7 +83,10 @@ limitations under the License.
}
.mx_InteractiveAuthEntryComponents_termsPolicy {
- display: block;
+ display: flex;
+ flex-direction: row;
+ justify-content: start;
+ align-items: center;
}
.mx_InteractiveAuthEntryComponents_passwordSection {
diff --git a/res/css/views/auth/_LanguageSelector.scss b/res/css/views/auth/_LanguageSelector.scss
index 781561f876..885ee7f30d 100644
--- a/res/css/views/auth/_LanguageSelector.scss
+++ b/res/css/views/auth/_LanguageSelector.scss
@@ -23,6 +23,7 @@ limitations under the License.
font-size: $font-14px;
font-weight: 600;
color: $authpage-lang-color;
+ width: auto;
}
.mx_AuthBody_language .mx_Dropdown_arrow {
diff --git a/res/css/views/auth/_Welcome.scss b/res/css/views/auth/_Welcome.scss
index f0e2b3de33..894174d6e2 100644
--- a/res/css/views/auth/_Welcome.scss
+++ b/res/css/views/auth/_Welcome.scss
@@ -18,7 +18,6 @@ limitations under the License.
display: flex;
flex-direction: column;
align-items: center;
-
&.mx_WelcomePage_registrationDisabled {
.mx_ButtonCreateAccount {
display: none;
@@ -27,6 +26,6 @@ limitations under the License.
}
.mx_Welcome .mx_AuthBody_language {
- width: 120px;
+ width: 160px;
margin-bottom: 10px;
}
diff --git a/res/css/views/dialogs/_RoomSettingsDialogBridges.scss b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss
index a1793cc75e..c97a3b69b7 100644
--- a/res/css/views/dialogs/_RoomSettingsDialogBridges.scss
+++ b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss
@@ -89,24 +89,18 @@ limitations under the License.
}
}
- .mx_showMore {
- display: block;
- text-align: left;
- margin-top: 10px;
- }
-
.metadata {
color: $muted-fg-color;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
margin-bottom: 0;
- }
-
- .metadata.visible {
overflow-y: visible;
text-overflow: ellipsis;
white-space: normal;
+ padding: 0;
+
+ > li {
+ padding: 0;
+ border: 0;
+ }
}
}
}
diff --git a/res/css/views/elements/_SSOButtons.scss b/res/css/views/elements/_SSOButtons.scss
index f762468c7f..e02816780f 100644
--- a/res/css/views/elements/_SSOButtons.scss
+++ b/res/css/views/elements/_SSOButtons.scss
@@ -16,13 +16,26 @@ limitations under the License.
.mx_SSOButtons {
display: flex;
+ flex-wrap: wrap;
justify-content: center;
+ .mx_SSOButtons_row {
+ & + .mx_SSOButtons_row {
+ margin-top: 16px;
+ }
+ }
+
.mx_SSOButton {
position: relative;
width: 100%;
- padding-left: 32px;
- padding-right: 32px;
+ padding: 7px 32px;
+ text-align: center;
+ border-radius: 8px;
+ display: inline-block;
+ font-size: $font-14px;
+ font-weight: $font-semi-bold;
+ border: 1px solid $input-border-color;
+ color: $primary-fg-color;
> img {
object-fit: contain;
@@ -32,10 +45,22 @@ limitations under the License.
}
}
+ .mx_SSOButton_default {
+ color: $button-primary-bg-color;
+ background-color: $button-secondary-bg-color;
+ border-color: $button-primary-bg-color;
+ }
+ .mx_SSOButton_default.mx_SSOButton_primary {
+ color: $button-primary-fg-color;
+ background-color: $button-primary-bg-color;
+ }
+
.mx_SSOButton_mini {
box-sizing: border-box;
width: 50px; // 48px + 1px border on all sides
height: 50px; // 48px + 1px border on all sides
+ min-width: 50px; // prevent crushing by the flexbox
+ padding: 12px;
> img {
left: 12px;
@@ -43,7 +68,7 @@ limitations under the License.
}
& + .mx_SSOButton_mini {
- margin-left: 24px;
+ margin-left: 16px;
}
}
}
diff --git a/res/css/views/elements/_ServerPicker.scss b/res/css/views/elements/_ServerPicker.scss
index ae1e445a9f..188eb5d655 100644
--- a/res/css/views/elements/_ServerPicker.scss
+++ b/res/css/views/elements/_ServerPicker.scss
@@ -59,7 +59,7 @@ limitations under the License.
}
.mx_ServerPicker_server {
- color: $primary-fg-color;
+ color: $authpage-primary-color;
grid-column: 1;
grid-row: 2;
margin-bottom: 16px;
diff --git a/res/css/views/rooms/_AppsDrawer.scss b/res/css/views/rooms/_AppsDrawer.scss
index 8731d22660..492ed95973 100644
--- a/res/css/views/rooms/_AppsDrawer.scss
+++ b/res/css/views/rooms/_AppsDrawer.scss
@@ -24,26 +24,45 @@ $MiniAppTileHeight: 200px;
flex-direction: column;
overflow: hidden;
+ .mx_AppsContainer_resizerHandleContainer {
+ width: 100%;
+ height: 10px;
+ margin-top: -3px; // move it up so the interactions are slightly more comfortable
+ display: block;
+ position: relative;
+ }
+
.mx_AppsContainer_resizerHandle {
cursor: ns-resize;
- border-radius: 3px;
- // Override styles from library
- width: unset !important;
- height: 4px !important;
+ // Override styles from library, making the whole area the target area
+ width: 100% !important;
+ height: 100% !important;
// This is positioned directly below frame
position: absolute;
- bottom: -8px !important; // override from library
+ bottom: 0 !important; // override from library
- // Together, these make the bar 64px wide
- // These are also overridden from the library
- left: calc(50% - 32px) !important;
- right: calc(50% - 32px) !important;
+ // We then render the pill handle in an ::after to keep it in the handle's
+ // area without being a massive line across the screen
+ &::after {
+ content: '';
+ position: absolute;
+ border-radius: 3px;
+
+ // The combination of these two should make the pill 4px high
+ top: 6px;
+ bottom: 0;
+
+ // Together, these make the bar 64px wide
+ // These are also overridden from the library
+ left: calc(50% - 32px);
+ right: calc(50% - 32px);
+ }
}
&:hover {
- .mx_AppsContainer_resizerHandle {
+ .mx_AppsContainer_resizerHandle::after {
opacity: 0.8;
background: $primary-fg-color;
}
diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss
index 429ac7ed4b..acf3049804 100644
--- a/res/css/views/rooms/_EventTile.scss
+++ b/res/css/views/rooms/_EventTile.scss
@@ -74,7 +74,6 @@ $left-gutter: 64px;
margin-left: 5px;
display: inline-block;
vertical-align: top;
- height: 16px;
overflow: hidden;
user-select: none;
diff --git a/res/css/views/rooms/_GroupLayout.scss b/res/css/views/rooms/_GroupLayout.scss
index 2b447be44a..50ef50110f 100644
--- a/res/css/views/rooms/_GroupLayout.scss
+++ b/res/css/views/rooms/_GroupLayout.scss
@@ -20,7 +20,7 @@ $left-gutter: 64px;
.mx_GroupLayout {
.mx_EventTile {
> .mx_SenderProfile {
- line-height: $font-17px;
+ line-height: $font-20px;
padding-left: $left-gutter;
}
@@ -38,7 +38,7 @@ $left-gutter: 64px;
}
.mx_EventTile_line, .mx_EventTile_reply {
- padding-top: 3px;
+ padding-top: 1px;
padding-bottom: 3px;
line-height: $font-22px;
}
diff --git a/res/css/views/rooms/_IRCLayout.scss b/res/css/views/rooms/_IRCLayout.scss
index ece547d02b..792c2f1f58 100644
--- a/res/css/views/rooms/_IRCLayout.scss
+++ b/res/css/views/rooms/_IRCLayout.scss
@@ -207,6 +207,17 @@ $irc-line-height: $font-18px;
width: unset;
max-width: var(--name-width);
}
+
+ .mx_SenderProfile_hover {
+ background: transparent;
+
+ > span {
+ > .mx_SenderProfile_name,
+ > .mx_SenderProfile_aux {
+ min-width: inherit;
+ }
+ }
+ }
}
.mx_ProfileResizer {
diff --git a/res/css/views/settings/_ProfileSettings.scss b/res/css/views/settings/_ProfileSettings.scss
index 732cbedf02..4cbcb8e708 100644
--- a/res/css/views/settings/_ProfileSettings.scss
+++ b/res/css/views/settings/_ProfileSettings.scss
@@ -14,6 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
+.mx_ProfileSettings_controls_topic {
+ & > textarea {
+ resize: vertical;
+ }
+}
+
.mx_ProfileSettings_profile {
display: flex;
}
diff --git a/res/img/element-icons/brands/apple.svg b/res/img/element-icons/brands/apple.svg
new file mode 100644
index 0000000000..308c3c5d5a
--- /dev/null
+++ b/res/img/element-icons/brands/apple.svg
@@ -0,0 +1,3 @@
+
diff --git a/res/img/element-icons/brands/facebook.svg b/res/img/element-icons/brands/facebook.svg
new file mode 100644
index 0000000000..2742785424
--- /dev/null
+++ b/res/img/element-icons/brands/facebook.svg
@@ -0,0 +1,9 @@
+
diff --git a/res/img/element-icons/brands/github.svg b/res/img/element-icons/brands/github.svg
new file mode 100644
index 0000000000..503719520b
--- /dev/null
+++ b/res/img/element-icons/brands/github.svg
@@ -0,0 +1,3 @@
+
diff --git a/res/img/element-icons/brands/gitlab.svg b/res/img/element-icons/brands/gitlab.svg
new file mode 100644
index 0000000000..df84c41e21
--- /dev/null
+++ b/res/img/element-icons/brands/gitlab.svg
@@ -0,0 +1,9 @@
+
diff --git a/res/img/element-icons/brands/google.svg b/res/img/element-icons/brands/google.svg
new file mode 100644
index 0000000000..1b0b19ae5b
--- /dev/null
+++ b/res/img/element-icons/brands/google.svg
@@ -0,0 +1,6 @@
+
diff --git a/res/img/element-icons/brands/twitter.svg b/res/img/element-icons/brands/twitter.svg
new file mode 100644
index 0000000000..43eb825a59
--- /dev/null
+++ b/res/img/element-icons/brands/twitter.svg
@@ -0,0 +1,3 @@
+
diff --git a/scripts/ci/Dockerfile b/scripts/ci/Dockerfile
index 5351291f29..3fdd0d7bf6 100644
--- a/scripts/ci/Dockerfile
+++ b/scripts/ci/Dockerfile
@@ -1,8 +1,7 @@
# Update on docker hub with the following commands in the directory of this file:
# docker build -t vectorim/element-web-ci-e2etests-env:latest .
-# docker log
# docker push vectorim/element-web-ci-e2etests-env:latest
-FROM node:10
+FROM node:14-buster
RUN apt-get update
RUN apt-get -y install build-essential python3-dev libffi-dev python-pip python-setuptools sqlite3 libssl-dev python-virtualenv libjpeg-dev libxslt1-dev uuid-runtime
# dependencies for chrome (installed by puppeteer)
diff --git a/scripts/reskindex.js b/scripts/reskindex.js
index 9fb0e1a7c0..12310b77c1 100755
--- a/scripts/reskindex.js
+++ b/scripts/reskindex.js
@@ -1,29 +1,30 @@
#!/usr/bin/env node
-var fs = require('fs');
-var path = require('path');
-var glob = require('glob');
-var args = require('minimist')(process.argv);
-var chokidar = require('chokidar');
+const fs = require('fs');
+const path = require('path');
+const glob = require('glob');
+const util = require('util');
+const args = require('minimist')(process.argv);
+const chokidar = require('chokidar');
-var componentIndex = path.join('src', 'component-index.js');
-var componentIndexTmp = componentIndex+".tmp";
-var componentsDir = path.join('src', 'components');
-var componentJsGlob = '**/*.js';
-var componentTsGlob = '**/*.tsx';
-var prevFiles = [];
+const componentIndex = path.join('src', 'component-index.js');
+const componentIndexTmp = componentIndex+".tmp";
+const componentsDir = path.join('src', 'components');
+const componentJsGlob = '**/*.js';
+const componentTsGlob = '**/*.tsx';
+let prevFiles = [];
-function reskindex() {
- var jsFiles = glob.sync(componentJsGlob, {cwd: componentsDir}).sort();
- var tsFiles = glob.sync(componentTsGlob, {cwd: componentsDir}).sort();
- var files = [...tsFiles, ...jsFiles];
+async function reskindex() {
+ const jsFiles = glob.sync(componentJsGlob, {cwd: componentsDir}).sort();
+ const tsFiles = glob.sync(componentTsGlob, {cwd: componentsDir}).sort();
+ const files = [...tsFiles, ...jsFiles];
if (!filesHaveChanged(files, prevFiles)) {
return;
}
prevFiles = files;
- var header = args.h || args.header;
+ const header = args.h || args.header;
- var strm = fs.createWriteStream(componentIndexTmp);
+ const strm = fs.createWriteStream(componentIndexTmp);
if (header) {
strm.write(fs.readFileSync(header));
@@ -38,11 +39,11 @@ function reskindex() {
strm.write(" */\n\n");
strm.write("let components = {};\n");
- for (var i = 0; i < files.length; ++i) {
- var file = files[i].replace('.js', '').replace('.tsx', '');
+ for (let i = 0; i < files.length; ++i) {
+ const file = files[i].replace('.js', '').replace('.tsx', '');
- var moduleName = (file.replace(/\//g, '.'));
- var importName = moduleName.replace(/\./g, "$");
+ const moduleName = (file.replace(/\//g, '.'));
+ const importName = moduleName.replace(/\./g, "$");
strm.write("import " + importName + " from './components/" + file + "';\n");
strm.write(importName + " && (components['"+moduleName+"'] = " + importName + ");");
@@ -51,9 +52,10 @@ function reskindex() {
}
strm.write("export {components};\n");
- strm.end();
+ // Ensure the file has been fully written to disk before proceeding
+ await util.promisify(strm.end);
fs.rename(componentIndexTmp, componentIndex, function(err) {
- if(err) {
+ if (err) {
console.error("Error moving new index into place: " + err);
} else {
console.log('Reskindex: completed');
@@ -67,7 +69,7 @@ function filesHaveChanged(files, prevFiles) {
return true;
}
// Check for name changes
- for (var i = 0; i < files.length; i++) {
+ for (let i = 0; i < files.length; i++) {
if (prevFiles[i] !== files[i]) {
return true;
}
@@ -81,7 +83,7 @@ if (!args.w) {
return;
}
-var watchDebouncer = null;
+let watchDebouncer = null;
chokidar.watch(path.join(componentsDir, componentJsGlob)).on('all', (event, path) => {
if (path === componentIndex) return;
if (watchDebouncer) clearTimeout(watchDebouncer);
diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts
index 741798761f..2a28c8e43f 100644
--- a/src/@types/global.d.ts
+++ b/src/@types/global.d.ts
@@ -36,6 +36,7 @@ import {Analytics} from "../Analytics";
import CountlyAnalytics from "../CountlyAnalytics";
import UserActivity from "../UserActivity";
import {ModalWidgetStore} from "../stores/ModalWidgetStore";
+import { WidgetLayoutStore } from "../stores/widgets/WidgetLayoutStore";
declare global {
interface Window {
@@ -59,6 +60,7 @@ declare global {
mxNotifier: typeof Notifier;
mxRightPanelStore: RightPanelStore;
mxWidgetStore: WidgetStore;
+ mxWidgetLayoutStore: WidgetLayoutStore;
mxCallHandler: CallHandler;
mxAnalytics: Analytics;
mxCountlyAnalytics: typeof CountlyAnalytics;
diff --git a/src/BasePlatform.ts b/src/BasePlatform.ts
index c301aa6a10..d0d5e60ce8 100644
--- a/src/BasePlatform.ts
+++ b/src/BasePlatform.ts
@@ -30,6 +30,7 @@ import {idbLoad, idbSave, idbDelete} from "./utils/StorageManager";
export const SSO_HOMESERVER_URL_KEY = "mx_sso_hs_url";
export const SSO_ID_SERVER_URL_KEY = "mx_sso_is_url";
+export const SSO_IDP_ID_KEY = "mx_sso_idp_id";
export enum UpdateCheckStatus {
Checking = "CHECKING",
@@ -56,7 +57,7 @@ export default abstract class BasePlatform {
this.startUpdateCheck = this.startUpdateCheck.bind(this);
}
- abstract async getConfig(): Promise<{}>;
+ abstract getConfig(): Promise<{}>;
abstract getDefaultDeviceDisplayName(): string;
@@ -258,6 +259,9 @@ export default abstract class BasePlatform {
if (mxClient.getIdentityServerUrl()) {
localStorage.setItem(SSO_ID_SERVER_URL_KEY, mxClient.getIdentityServerUrl());
}
+ if (idpId) {
+ localStorage.setItem(SSO_IDP_ID_KEY, idpId);
+ }
const callbackUrl = this.getSSOCallbackUrl(fragmentAfterLogin);
window.location.href = mxClient.getSsoLoginUrl(callbackUrl.toString(), loginType, idpId); // redirect to SSO
}
diff --git a/src/CallHandler.tsx b/src/CallHandler.tsx
index bcb2042f84..9dee7594ea 100644
--- a/src/CallHandler.tsx
+++ b/src/CallHandler.tsx
@@ -83,6 +83,8 @@ import {UIFeature} from "./settings/UIFeature";
import { CallError } from "matrix-js-sdk/src/webrtc/call";
import { logger } from 'matrix-js-sdk/src/logger';
import { Action } from './dispatcher/actions';
+import { roomForVirtualRoom, getOrCreateVirtualRoomForRoom } from './VoipUserMapper';
+import { addManagedHybridWidget, isManagedHybridWidgetEnabled } from './widgets/ManagedHybrid';
const CHECK_PSTN_SUPPORT_ATTEMPTS = 3;
@@ -133,6 +135,15 @@ export default class CallHandler {
return window.mxCallHandler;
}
+ /*
+ * Gets the user-facing room associated with a call (call.roomId may be the call "virtual room"
+ * if a voip_mxid_translate_pattern is set in the config)
+ */
+ public static roomIdForCall(call: MatrixCall) {
+ if (!call) return null;
+ return roomForVirtualRoom(call.roomId) || call.roomId;
+ }
+
start() {
this.dispatcherRef = dis.register(this.onAction);
// add empty handlers for media actions, otherwise the media keys
@@ -284,11 +295,15 @@ export default class CallHandler {
// We don't allow placing more than one call per room, but that doesn't mean there
// can't be more than one, eg. in a glare situation. This checks that the given call
// is the call we consider 'the' call for its room.
- const callForThisRoom = this.getCallForRoom(call.roomId);
+ const mappedRoomId = CallHandler.roomIdForCall(call);
+
+ const callForThisRoom = this.getCallForRoom(mappedRoomId);
return callForThisRoom && call.callId === callForThisRoom.callId;
}
private setCallListeners(call: MatrixCall) {
+ const mappedRoomId = CallHandler.roomIdForCall(call);
+
call.on(CallEvent.Error, (err: CallError) => {
if (!this.matchesCallForThisRoom(call)) return;
@@ -318,7 +333,7 @@ export default class CallHandler {
Analytics.trackEvent('voip', 'callHangup');
- this.removeCallForRoom(call.roomId);
+ this.removeCallForRoom(mappedRoomId);
});
call.on(CallEvent.State, (newState: CallState, oldState: CallState) => {
if (!this.matchesCallForThisRoom(call)) return;
@@ -342,8 +357,9 @@ export default class CallHandler {
this.play(AudioID.Ringback);
break;
case CallState.Ended:
+ {
Analytics.trackEvent('voip', 'callEnded', 'hangupReason', call.hangupReason);
- this.removeCallForRoom(call.roomId);
+ this.removeCallForRoom(mappedRoomId);
if (oldState === CallState.InviteSent && (
call.hangupParty === CallParty.Remote ||
(call.hangupParty === CallParty.Local && call.hangupReason === CallErrorCode.InviteTimeout)
@@ -375,10 +391,14 @@ export default class CallHandler {
title: _t("Answered Elsewhere"),
description: _t("The call was answered on another device."),
});
- } else if (oldState !== CallState.Fledgling) {
+ } else if (oldState !== CallState.Fledgling && oldState !== CallState.Ringing) {
// don't play the end-call sound for calls that never got off the ground
this.play(AudioID.CallEnd);
}
+
+ this.logCallStats(call, mappedRoomId);
+ break;
+ }
}
});
call.on(CallEvent.Replaced, (newCall: MatrixCall) => {
@@ -392,25 +412,63 @@ export default class CallHandler {
this.pause(AudioID.Ringback);
}
- this.calls.set(newCall.roomId, newCall);
+ this.calls.set(mappedRoomId, newCall);
this.setCallListeners(newCall);
this.setCallState(newCall, newCall.state);
});
}
+ private async logCallStats(call: MatrixCall, mappedRoomId: string) {
+ const stats = await call.getCurrentCallStats();
+ logger.debug(
+ `Call completed. Call ID: ${call.callId}, virtual room ID: ${call.roomId}, ` +
+ `user-facing room ID: ${mappedRoomId}, direction: ${call.direction}, ` +
+ `our Party ID: ${call.ourPartyId}, hangup party: ${call.hangupParty}, ` +
+ `hangup reason: ${call.hangupReason}`,
+ );
+ logger.debug("Local candidates:");
+ for (const cand of stats.filter(item => item.type === 'local-candidate')) {
+ const address = cand.address || cand.ip; // firefox uses 'address', chrome uses 'ip'
+ logger.debug(
+ `${cand.id} - type: ${cand.candidateType}, address: ${address}, port: ${cand.port}, ` +
+ `protocol: ${cand.protocol}, relay protocol: ${cand.relayProtocol}, network type: ${cand.networkType}`,
+ );
+ }
+ logger.debug("Remote candidates:");
+ for (const cand of stats.filter(item => item.type === 'remote-candidate')) {
+ const address = cand.address || cand.ip; // firefox uses 'address', chrome uses 'ip'
+ logger.debug(
+ `${cand.id} - type: ${cand.candidateType}, address: ${address}, port: ${cand.port}, ` +
+ `protocol: ${cand.protocol}`,
+ );
+ }
+ logger.debug("Candidate pairs:");
+ for (const pair of stats.filter(item => item.type === 'candidate-pair')) {
+ logger.debug(
+ `${pair.localCandidateId} / ${pair.remoteCandidateId} - state: ${pair.state}, ` +
+ `nominated: ${pair.nominated}, ` +
+ `requests sent ${pair.requestsSent}, requests received ${pair.requestsReceived}, ` +
+ `responses received: ${pair.responsesReceived}, responses sent: ${pair.responsesSent}, ` +
+ `bytes received: ${pair.bytesReceived}, bytes sent: ${pair.bytesSent}, `,
+ );
+ }
+ }
+
private setCallAudioElement(call: MatrixCall) {
const audioElement = getRemoteAudioElement();
if (audioElement) call.setRemoteAudioElement(audioElement);
}
private setCallState(call: MatrixCall, status: CallState) {
+ const mappedRoomId = CallHandler.roomIdForCall(call);
+
console.log(
- `Call state in ${call.roomId} changed to ${status}`,
+ `Call state in ${mappedRoomId} changed to ${status}`,
);
dis.dispatch({
action: 'call_state',
- room_id: call.roomId,
+ room_id: mappedRoomId,
state: status,
});
}
@@ -477,14 +535,20 @@ export default class CallHandler {
}, null, true);
}
- private placeCall(
+ private async placeCall(
roomId: string, type: PlaceCallType,
localElement: HTMLVideoElement, remoteElement: HTMLVideoElement,
) {
Analytics.trackEvent('voip', 'placeCall', 'type', type);
CountlyAnalytics.instance.trackStartCall(roomId, type === PlaceCallType.Video, false);
- const call = createNewMatrixCall(MatrixClientPeg.get(), roomId);
+
+ const mappedRoomId = (await getOrCreateVirtualRoomForRoom(roomId)) || roomId;
+ logger.debug("Mapped real room " + roomId + " to room ID " + mappedRoomId);
+
+ const call = createNewMatrixCall(MatrixClientPeg.get(), mappedRoomId);
+
this.calls.set(roomId, call);
+
this.setCallListeners(call);
this.setCallAudioElement(call);
@@ -518,6 +582,12 @@ export default class CallHandler {
switch (payload.action) {
case 'place_call':
{
+ // We might be using managed hybrid widgets
+ if (isManagedHybridWidgetEnabled()) {
+ addManagedHybridWidget(payload.room_id);
+ return;
+ }
+
// if the runtime env doesn't do VoIP, whine.
if (!MatrixClientPeg.get().supportsVoip()) {
Modal.createTrackedDialog('Call Handler', 'VoIP is unsupported', ErrorDialog, {
@@ -586,13 +656,14 @@ export default class CallHandler {
const call = payload.call as MatrixCall;
- if (this.getCallForRoom(call.roomId)) {
+ const mappedRoomId = CallHandler.roomIdForCall(call);
+ if (this.getCallForRoom(mappedRoomId)) {
// ignore multiple incoming calls to the same room
return;
}
Analytics.trackEvent('voip', 'receiveCall', 'type', call.type);
- this.calls.set(call.roomId, call)
+ this.calls.set(mappedRoomId, call)
this.setCallListeners(call);
}
break;
diff --git a/src/ContentMessages.tsx b/src/ContentMessages.tsx
index 5409a606de..bec36d49f6 100644
--- a/src/ContentMessages.tsx
+++ b/src/ContentMessages.tsx
@@ -497,7 +497,7 @@ export default class ContentMessages {
content.info.mimetype = file.type;
}
- const prom = new Promise((resolve) => {
+ const prom = new Promise
{_t( "We'll store an encrypted copy of your keys on our server. " + - "Secure your backup with a recovery passphrase.", + "Secure your backup with a Security Phrase.", )}
{_t("For maximum security, this should be different from your account password.")}
@@ -252,10 +252,10 @@ export default class CreateKeyBackupDialog extends React.PureComponent { onValidate={this._onPassPhraseValidate} fieldRef={this._passphraseField} autoFocus={true} - label={_td("Enter a recovery passphrase")} - labelEnterPassword={_td("Enter a recovery passphrase")} - labelStrongPassword={_td("Great! This recovery passphrase looks strong enough.")} - labelAllowedButUnsafe={_td("Great! This recovery passphrase looks strong enough.")} + label={_td("Enter a Security Phrase")} + labelEnterPassword={_td("Enter a Security Phrase")} + labelStrongPassword={_td("Great! This Security Phrase looks strong enough.")} + labelAllowedButUnsafe={_td("Great! This Security Phrase looks strong enough.")} /> @@ -270,7 +270,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent {