Merge remote-tracking branch 'origin/develop' into hs/blocked-err

This commit is contained in:
Will Hunt 2021-03-05 18:50:00 +00:00
commit d07069238f
269 changed files with 18170 additions and 6833 deletions

View file

@ -22,6 +22,8 @@ module.exports = {
"files": ["src/**/*.{ts,tsx}"], "files": ["src/**/*.{ts,tsx}"],
"extends": ["matrix-org/ts"], "extends": ["matrix-org/ts"],
"rules": { "rules": {
// We're okay being explicit at the moment
"@typescript-eslint/no-empty-interface": "off",
// We disable this while we're transitioning // We disable this while we're transitioning
"@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-explicit-any": "off",
// We'd rather not do this but we do // We'd rather not do this but we do

1
.gitignore vendored
View file

@ -13,3 +13,4 @@ package-lock.json
/src/component-index.js /src/component-index.js
.DS_Store .DS_Store
*.tmp

View file

@ -1,3 +1,288 @@
Changes in [3.15.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.15.0) (2021-03-01)
=====================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.15.0-rc.1...v3.15.0)
## Security notice
matrix-react-sdk 3.15.0 fixes a low severity issue (CVE-2021-21320) where the
user content sandbox can be abused to trick users into opening unexpected
documents. The content is opened with a `blob` origin that cannot access Matrix
user data, so messages and secrets are not at risk. Thanks to @keerok for
responsibly disclosing this via Matrix's Security Disclosure Policy.
## All changes
* Upgrade to JS SDK 9.8.0
Changes in [3.15.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.15.0-rc.1) (2021-02-24)
===============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.14.0...v3.15.0-rc.1)
* Upgrade to JS SDK 9.8.0-rc.1
* Translations update from Weblate
[\#5683](https://github.com/matrix-org/matrix-react-sdk/pull/5683)
* Fix object diffing when objects have different keys
[\#5681](https://github.com/matrix-org/matrix-react-sdk/pull/5681)
* Add <code> if it's missing
[\#5673](https://github.com/matrix-org/matrix-react-sdk/pull/5673)
* Add email only if the verification is complete
[\#5629](https://github.com/matrix-org/matrix-react-sdk/pull/5629)
* Fix portrait videocalls
[\#5676](https://github.com/matrix-org/matrix-react-sdk/pull/5676)
* Tweak code block icon positions
[\#5643](https://github.com/matrix-org/matrix-react-sdk/pull/5643)
* Revert "Improve URL preview formatting and image upload thumbnail size"
[\#5677](https://github.com/matrix-org/matrix-react-sdk/pull/5677)
* Fix context menu leaving visible area
[\#5644](https://github.com/matrix-org/matrix-react-sdk/pull/5644)
* Jitsi conferences names, take 3
[\#5675](https://github.com/matrix-org/matrix-react-sdk/pull/5675)
* Update isUserOnDarkTheme to take use_system_theme in account
[\#5670](https://github.com/matrix-org/matrix-react-sdk/pull/5670)
* Discard some dead code
[\#5665](https://github.com/matrix-org/matrix-react-sdk/pull/5665)
* Add developer tool to explore and edit settings
[\#5664](https://github.com/matrix-org/matrix-react-sdk/pull/5664)
* Use and create new room helpers
[\#5663](https://github.com/matrix-org/matrix-react-sdk/pull/5663)
* Clear message previews when the maximum limit is reached for history
[\#5661](https://github.com/matrix-org/matrix-react-sdk/pull/5661)
* VoIP virtual rooms, mk II
[\#5639](https://github.com/matrix-org/matrix-react-sdk/pull/5639)
* Disable chat effects when reduced motion preferred
[\#5660](https://github.com/matrix-org/matrix-react-sdk/pull/5660)
* Improve URL preview formatting and image upload thumbnail size
[\#5637](https://github.com/matrix-org/matrix-react-sdk/pull/5637)
* Fix border radius when the panel is collapsed
[\#5641](https://github.com/matrix-org/matrix-react-sdk/pull/5641)
* Use a more generic layout setting - useIRCLayout → layout
[\#5571](https://github.com/matrix-org/matrix-react-sdk/pull/5571)
* Remove redundant lockOrigin parameter from usercontent
[\#5657](https://github.com/matrix-org/matrix-react-sdk/pull/5657)
* Set ICE candidate pool size option
[\#5655](https://github.com/matrix-org/matrix-react-sdk/pull/5655)
* Prepare to encrypt when a call arrives
[\#5654](https://github.com/matrix-org/matrix-react-sdk/pull/5654)
* Use config for host signup branding
[\#5650](https://github.com/matrix-org/matrix-react-sdk/pull/5650)
* Use randomly generated conference names for Jitsi
[\#5649](https://github.com/matrix-org/matrix-react-sdk/pull/5649)
* Modified regex to account for an immediate new line after slash commands
[\#5647](https://github.com/matrix-org/matrix-react-sdk/pull/5647)
* Fix codeblock scrollbar color for non-Firefox
[\#5642](https://github.com/matrix-org/matrix-react-sdk/pull/5642)
* Fix codeblock scrollbar colors
[\#5630](https://github.com/matrix-org/matrix-react-sdk/pull/5630)
* Added loading and disabled the button while searching for server
[\#5634](https://github.com/matrix-org/matrix-react-sdk/pull/5634)
Changes in [3.14.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.14.0) (2021-02-16)
=====================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.14.0-rc.1...v3.14.0)
* Upgrade to JS SDK 9.7.0
* [Release] Use config for host signup branding
[\#5651](https://github.com/matrix-org/matrix-react-sdk/pull/5651)
Changes in [3.14.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.14.0-rc.1) (2021-02-10)
===============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.13.1...v3.14.0-rc.1)
* Upgrade to JS SDK 9.7.0-rc.1
* Translations update from Weblate
[\#5636](https://github.com/matrix-org/matrix-react-sdk/pull/5636)
* Add host signup modal with iframe
[\#5450](https://github.com/matrix-org/matrix-react-sdk/pull/5450)
* Fix duplication of codeblock elements
[\#5633](https://github.com/matrix-org/matrix-react-sdk/pull/5633)
* Handle undefined call stats
[\#5632](https://github.com/matrix-org/matrix-react-sdk/pull/5632)
* Avoid delayed displaying of sources in source picker
[\#5631](https://github.com/matrix-org/matrix-react-sdk/pull/5631)
* Give breadcrumbs toolbar an accessibility label.
[\#5628](https://github.com/matrix-org/matrix-react-sdk/pull/5628)
* Fix the %s in logs
[\#5627](https://github.com/matrix-org/matrix-react-sdk/pull/5627)
* Fix jumpy notifications settings UI
[\#5625](https://github.com/matrix-org/matrix-react-sdk/pull/5625)
* Improve displaying of code blocks
[\#5559](https://github.com/matrix-org/matrix-react-sdk/pull/5559)
* Fix desktop Matrix screen sharing and add a screen/window picker
[\#5525](https://github.com/matrix-org/matrix-react-sdk/pull/5525)
* Call "MatrixClientPeg.get()" only once in method "findOverrideMuteRule"
[\#5498](https://github.com/matrix-org/matrix-react-sdk/pull/5498)
* Close current modal when session is logged out
[\#5616](https://github.com/matrix-org/matrix-react-sdk/pull/5616)
* Switch room explorer list to CSS grid
[\#5551](https://github.com/matrix-org/matrix-react-sdk/pull/5551)
* Improve SSO login start screen and 3pid invite handling somewhat
[\#5622](https://github.com/matrix-org/matrix-react-sdk/pull/5622)
* Don't jump to bottom on reaction
[\#5621](https://github.com/matrix-org/matrix-react-sdk/pull/5621)
* Fix several profile settings oddities
[\#5620](https://github.com/matrix-org/matrix-react-sdk/pull/5620)
* Add option to hide the stickers button in the composer
[\#5530](https://github.com/matrix-org/matrix-react-sdk/pull/5530)
* Fix confusing right panel button behaviour
[\#5598](https://github.com/matrix-org/matrix-react-sdk/pull/5598)
* Fix jumping timestamp if hovering a message with e2e indicator bar
[\#5601](https://github.com/matrix-org/matrix-react-sdk/pull/5601)
* Fix avatar and trash alignment
[\#5614](https://github.com/matrix-org/matrix-react-sdk/pull/5614)
* Fix z-index of stickerpicker
[\#5617](https://github.com/matrix-org/matrix-react-sdk/pull/5617)
* Fix permalink via parsing for rooms
[\#5615](https://github.com/matrix-org/matrix-react-sdk/pull/5615)
* Fix "Terms and Conditions" checkbox alignment
[\#5613](https://github.com/matrix-org/matrix-react-sdk/pull/5613)
* Fix flair height after accent changes
[\#5611](https://github.com/matrix-org/matrix-react-sdk/pull/5611)
* Iterate Social Logins work around edge cases and branding
[\#5609](https://github.com/matrix-org/matrix-react-sdk/pull/5609)
* Lock widget room ID when added
[\#5607](https://github.com/matrix-org/matrix-react-sdk/pull/5607)
* Better errors for SSO failures
[\#5605](https://github.com/matrix-org/matrix-react-sdk/pull/5605)
* Increase language search bar width
[\#5549](https://github.com/matrix-org/matrix-react-sdk/pull/5549)
* Scroll to bottom on message_sent
[\#5565](https://github.com/matrix-org/matrix-react-sdk/pull/5565)
* Fix new rooms being titled 'Empty Room'
[\#5587](https://github.com/matrix-org/matrix-react-sdk/pull/5587)
* Fix saving the collapsed state of the left panel
[\#5593](https://github.com/matrix-org/matrix-react-sdk/pull/5593)
* Fix app-url hint in the e2e-test run script output
[\#5600](https://github.com/matrix-org/matrix-react-sdk/pull/5600)
* Fix RoomView re-mounting breaking peeking
[\#5602](https://github.com/matrix-org/matrix-react-sdk/pull/5602)
* Tweak a few room ID checks
[\#5592](https://github.com/matrix-org/matrix-react-sdk/pull/5592)
* Remove pills from event permalinks with text
[\#5575](https://github.com/matrix-org/matrix-react-sdk/pull/5575)
Changes in [3.13.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.13.1) (2021-02-04)
=====================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.13.0...v3.13.1)
* [Release] Fix z-index of stickerpicker
[\#5618](https://github.com/matrix-org/matrix-react-sdk/pull/5618)
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) 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) [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.12.0...v3.12.1)

View file

@ -3,12 +3,15 @@ module.exports = {
"presets": [ "presets": [
["@babel/preset-env", { ["@babel/preset-env", {
"targets": [ "targets": [
"last 2 Chrome versions", "last 2 Firefox versions", "last 2 Safari versions" "last 2 Chrome versions",
"last 2 Firefox versions",
"last 2 Safari versions",
"last 2 Edge versions",
], ],
}], }],
"@babel/preset-typescript", "@babel/preset-typescript",
"@babel/preset-flow", "@babel/preset-flow",
"@babel/preset-react" "@babel/preset-react",
], ],
"plugins": [ "plugins": [
["@babel/plugin-proposal-decorators", {legacy: true}], ["@babel/plugin-proposal-decorators", {legacy: true}],
@ -18,6 +21,6 @@ module.exports = {
"@babel/plugin-proposal-object-rest-spread", "@babel/plugin-proposal-object-rest-spread",
"@babel/plugin-transform-flow-comments", "@babel/plugin-transform-flow-comments",
"@babel/plugin-syntax-dynamic-import", "@babel/plugin-syntax-dynamic-import",
"@babel/plugin-transform-runtime" "@babel/plugin-transform-runtime",
] ],
}; };

View file

@ -35,12 +35,6 @@ General Style
- lowerCamelCase for functions and variables. - lowerCamelCase for functions and variables.
- Single line ternary operators are fine. - Single line ternary operators are fine.
- UPPER_SNAKE_CASE for constants - 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 - Use parentheses or `` ` `` instead of `\` for line continuation where ever possible
- Open braces on the same line (consistent with Node): - 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 - 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. arrow function, they probably all should be.
- Apart from that, newer ES features should be used whenever the author deems them to be appropriate. - 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 React
----- -----
@ -201,6 +202,8 @@ React
this.state = { counter: 0 }; 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 - Think about whether your component really needs state: are you duplicating
information in component state that could be derived from the model? information in component state that could be derived from the model?

View file

@ -1,6 +1,6 @@
{ {
"name": "matrix-react-sdk", "name": "matrix-react-sdk",
"version": "3.12.1", "version": "3.15.0",
"description": "SDK for matrix.org using React", "description": "SDK for matrix.org using React",
"author": "matrix.org", "author": "matrix.org",
"repository": { "repository": {
@ -54,48 +54,46 @@
"test:e2e": "./test/end-to-end-tests/run.sh --app-url http://localhost:8080" "test:e2e": "./test/end-to-end-tests/run.sh --app-url http://localhost:8080"
}, },
"dependencies": { "dependencies": {
"@babel/runtime": "^7.10.5", "@babel/runtime": "^7.12.5",
"await-lock": "^2.0.1", "await-lock": "^2.1.0",
"blueimp-canvas-to-blob": "^3.27.0", "blueimp-canvas-to-blob": "^3.28.0",
"browser-encrypt-attachment": "^0.3.0", "browser-encrypt-attachment": "^0.3.0",
"browser-request": "^0.3.3", "browser-request": "^0.3.3",
"cheerio": "^1.0.0-rc.3", "cheerio": "^1.0.0-rc.5",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"commonmark": "^0.29.1", "commonmark": "^0.29.3",
"counterpart": "^0.18.6", "counterpart": "^0.18.6",
"diff-dom": "^4.1.6", "diff-dom": "^4.2.2",
"diff-match-patch": "^1.0.5", "diff-match-patch": "^1.0.5",
"emojibase-data": "^5.0.1", "emojibase-data": "^5.1.1",
"emojibase-regex": "^4.0.1", "emojibase-regex": "^4.1.1",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"file-saver": "^1.3.8", "file-saver": "^2.0.5",
"filesize": "3.6.1", "filesize": "6.1.0",
"flux": "2.1.1", "flux": "2.1.1",
"focus-visible": "^5.1.0", "focus-visible": "^5.2.0",
"fuse.js": "^2.7.4",
"gfm.css": "^1.1.2", "gfm.css": "^1.1.2",
"glob-to-regexp": "^0.4.1", "glob-to-regexp": "^0.4.1",
"highlight.js": "^10.1.2", "highlight.js": "^10.5.0",
"html-entities": "^1.3.1", "html-entities": "^1.4.0",
"is-ip": "^2.0.0", "is-ip": "^3.1.0",
"katex": "^0.12.0", "katex": "^0.12.0",
"linkifyjs": "^2.1.9", "linkifyjs": "^2.1.9",
"lodash": "^4.17.19", "lodash": "^4.17.20",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
"matrix-widget-api": "0.1.0-beta.11", "matrix-widget-api": "^0.1.0-beta.13",
"minimist": "^1.2.5", "minimist": "^1.2.5",
"pako": "^1.0.11", "pako": "^2.0.3",
"parse5": "^5.1.1", "parse5": "^6.0.1",
"png-chunks-extract": "^1.0.0", "png-chunks-extract": "^1.0.0",
"project-name-generator": "^2.1.7",
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"qrcode": "^1.4.4", "qrcode": "^1.4.4",
"qs": "^6.9.4", "qs": "^6.9.6",
"re-resizable": "^6.5.4", "re-resizable": "^6.9.0",
"react": "^16.13.1", "react": "^16.14.0",
"react-beautiful-dnd": "^4.0.1", "react-beautiful-dnd": "^4.0.1",
"react-dom": "^16.13.1", "react-dom": "^16.14.0",
"react-focus-lock": "^2.4.1", "react-focus-lock": "^2.5.0",
"react-transition-group": "^4.4.1", "react-transition-group": "^4.4.1",
"resize-observer-polyfill": "^1.5.1", "resize-observer-polyfill": "^1.5.1",
"rfc4648": "^1.4.0", "rfc4648": "^1.4.0",
@ -103,73 +101,76 @@
"tar-js": "^0.3.0", "tar-js": "^0.3.0",
"text-encoding-utf-8": "^1.0.2", "text-encoding-utf-8": "^1.0.2",
"url": "^0.11.0", "url": "^0.11.0",
"velocity-animate": "^1.5.2", "velocity-animate": "^2.0.6",
"what-input": "^5.2.10", "what-input": "^5.2.10",
"zxcvbn": "^4.4.2" "zxcvbn": "^4.4.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.10.5", "@babel/cli": "^7.12.10",
"@babel/core": "^7.10.5", "@babel/core": "^7.12.10",
"@babel/parser": "^7.11.0", "@babel/parser": "^7.12.11",
"@babel/plugin-proposal-class-properties": "^7.10.4", "@babel/plugin-proposal-class-properties": "^7.12.1",
"@babel/plugin-proposal-decorators": "^7.10.5", "@babel/plugin-proposal-decorators": "^7.12.12",
"@babel/plugin-proposal-export-default-from": "^7.10.4", "@babel/plugin-proposal-export-default-from": "^7.12.1",
"@babel/plugin-proposal-numeric-separator": "^7.10.4", "@babel/plugin-proposal-numeric-separator": "^7.12.7",
"@babel/plugin-proposal-object-rest-spread": "^7.10.4", "@babel/plugin-proposal-object-rest-spread": "^7.12.1",
"@babel/plugin-transform-flow-comments": "^7.10.4", "@babel/plugin-transform-flow-comments": "^7.12.1",
"@babel/plugin-transform-runtime": "^7.10.5", "@babel/plugin-transform-runtime": "^7.12.10",
"@babel/preset-env": "^7.10.4", "@babel/preset-env": "^7.12.11",
"@babel/preset-flow": "^7.10.4", "@babel/preset-flow": "^7.12.1",
"@babel/preset-react": "^7.10.4", "@babel/preset-react": "^7.12.10",
"@babel/preset-typescript": "^7.10.4", "@babel/preset-typescript": "^7.12.7",
"@babel/register": "^7.10.5", "@babel/register": "^7.12.10",
"@babel/traverse": "^7.11.0", "@babel/traverse": "^7.12.12",
"@peculiar/webcrypto": "^1.1.3", "@peculiar/webcrypto": "^1.1.4",
"@types/classnames": "^2.2.10", "@sinonjs/fake-timers": "^7.0.2",
"@types/classnames": "^2.2.11",
"@types/counterpart": "^0.18.1", "@types/counterpart": "^0.18.1",
"@types/flux": "^3.1.9", "@types/flux": "^3.1.9",
"@types/jest": "^26.0.20", "@types/jest": "^26.0.20",
"@types/linkifyjs": "^2.1.3", "@types/linkifyjs": "^2.1.3",
"@types/lodash": "^4.14.158", "@types/lodash": "^4.14.168",
"@types/modernizr": "^3.5.3", "@types/modernizr": "^3.5.3",
"@types/node": "^12.12.51", "@types/node": "^14.14.22",
"@types/pako": "^1.0.1", "@types/pako": "^1.0.1",
"@types/qrcode": "^1.3.4", "@types/qrcode": "^1.3.5",
"@types/react": "^16.9", "@types/react": "^16.9",
"@types/react-dom": "^16.9.8", "@types/react-dom": "^16.9.10",
"@types/react-transition-group": "^4.4.0", "@types/react-transition-group": "^4.4.0",
"@types/sanitize-html": "^1.23.3", "@types/sanitize-html": "^1.27.0",
"@types/zxcvbn": "^4.4.0", "@types/zxcvbn": "^4.4.0",
"@typescript-eslint/eslint-plugin": "^3.7.0", "@typescript-eslint/eslint-plugin": "^4.14.0",
"@typescript-eslint/parser": "^3.7.0", "@typescript-eslint/parser": "^4.14.0",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"babel-jest": "^24.9.0", "babel-jest": "^26.6.3",
"chokidar": "^3.4.1", "chokidar": "^3.5.1",
"concurrently": "^4.1.2", "concurrently": "^5.3.0",
"enzyme": "^3.11.0", "enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.2", "enzyme-adapter-react-16": "^1.15.6",
"eslint": "7.5.0", "eslint": "7.18.0",
"eslint-config-matrix-org": "^0.1.2", "eslint-config-matrix-org": "^0.2.0",
"eslint-plugin-babel": "^5.3.1", "eslint-plugin-babel": "^5.3.1",
"eslint-plugin-flowtype": "^2.50.3", "eslint-plugin-flowtype": "^5.2.0",
"eslint-plugin-react": "^7.20.3", "eslint-plugin-react": "^7.22.0",
"eslint-plugin-react-hooks": "^2.5.1", "eslint-plugin-react-hooks": "^4.2.0",
"glob": "^5.0.15", "glob": "^7.1.6",
"jest": "^26.5.2", "jest": "^26.6.3",
"jest-canvas-mock": "^2.3.0", "jest-canvas-mock": "^2.3.0",
"jest-environment-jsdom-sixteen": "^1.0.3", "jest-environment-jsdom-sixteen": "^1.0.3",
"lolex": "^5.1.2",
"matrix-mock-request": "^1.2.3", "matrix-mock-request": "^1.2.3",
"matrix-react-test-utils": "^0.2.2", "matrix-react-test-utils": "^0.2.2",
"olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz", "olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
"react-test-renderer": "^16.13.1", "react-test-renderer": "^16.14.0",
"rimraf": "^2.7.1", "rimraf": "^3.0.2",
"stylelint": "^9.10.1", "stylelint": "^13.9.0",
"stylelint-config-standard": "^18.3.0", "stylelint-config-standard": "^20.0.0",
"stylelint-scss": "^3.18.0", "stylelint-scss": "^3.18.0",
"typescript": "^3.9.7", "typescript": "^4.1.3",
"walk": "^2.3.14" "walk": "^2.3.14"
}, },
"resolutions": {
"**/@types/react": "^16.14"
},
"jest": { "jest": {
"testEnvironment": "./__test-utils__/environment.js", "testEnvironment": "./__test-utils__/environment.js",
"testMatch": [ "testMatch": [

View file

@ -21,6 +21,11 @@ limitations under the License.
$hover-transition: 0.08s cubic-bezier(.46, .03, .52, .96); // quadratic $hover-transition: 0.08s cubic-bezier(.46, .03, .52, .96); // quadratic
$EventTile_e2e_state_indicator_width: 4px;
$MessageTimestamp_width: 46px; /* 8 + 30 (avatar) + 8 */
$MessageTimestamp_width_hover: calc($MessageTimestamp_width - 2 * $EventTile_e2e_state_indicator_width);
:root { :root {
font-size: 10px; font-size: 10px;
} }

View file

@ -27,6 +27,9 @@
@import "./structures/_RoomView.scss"; @import "./structures/_RoomView.scss";
@import "./structures/_ScrollPanel.scss"; @import "./structures/_ScrollPanel.scss";
@import "./structures/_SearchBox.scss"; @import "./structures/_SearchBox.scss";
@import "./structures/_SpacePanel.scss";
@import "./structures/_SpaceRoomDirectory.scss";
@import "./structures/_SpaceRoomView.scss";
@import "./structures/_TabbedView.scss"; @import "./structures/_TabbedView.scss";
@import "./structures/_ToastContainer.scss"; @import "./structures/_ToastContainer.scss";
@import "./structures/_UploadBar.scss"; @import "./structures/_UploadBar.scss";
@ -56,6 +59,7 @@
@import "./views/context_menus/_MessageContextMenu.scss"; @import "./views/context_menus/_MessageContextMenu.scss";
@import "./views/context_menus/_StatusMessageContextMenu.scss"; @import "./views/context_menus/_StatusMessageContextMenu.scss";
@import "./views/context_menus/_TagTileContextMenu.scss"; @import "./views/context_menus/_TagTileContextMenu.scss";
@import "./views/dialogs/_AddExistingToSpaceDialog.scss";
@import "./views/dialogs/_AddressPickerDialog.scss"; @import "./views/dialogs/_AddressPickerDialog.scss";
@import "./views/dialogs/_Analytics.scss"; @import "./views/dialogs/_Analytics.scss";
@import "./views/dialogs/_BugReportDialog.scss"; @import "./views/dialogs/_BugReportDialog.scss";
@ -71,6 +75,7 @@
@import "./views/dialogs/_EditCommunityPrototypeDialog.scss"; @import "./views/dialogs/_EditCommunityPrototypeDialog.scss";
@import "./views/dialogs/_FeedbackDialog.scss"; @import "./views/dialogs/_FeedbackDialog.scss";
@import "./views/dialogs/_GroupAddressPicker.scss"; @import "./views/dialogs/_GroupAddressPicker.scss";
@import "./views/dialogs/_HostSignupDialog.scss";
@import "./views/dialogs/_IncomingSasDialog.scss"; @import "./views/dialogs/_IncomingSasDialog.scss";
@import "./views/dialogs/_InviteDialog.scss"; @import "./views/dialogs/_InviteDialog.scss";
@import "./views/dialogs/_KeyboardShortcutsDialog.scss"; @import "./views/dialogs/_KeyboardShortcutsDialog.scss";
@ -88,6 +93,7 @@
@import "./views/dialogs/_SettingsDialog.scss"; @import "./views/dialogs/_SettingsDialog.scss";
@import "./views/dialogs/_ShareDialog.scss"; @import "./views/dialogs/_ShareDialog.scss";
@import "./views/dialogs/_SlashCommandHelpDialog.scss"; @import "./views/dialogs/_SlashCommandHelpDialog.scss";
@import "./views/dialogs/_SpaceSettingsDialog.scss";
@import "./views/dialogs/_TabbedIntegrationManagerDialog.scss"; @import "./views/dialogs/_TabbedIntegrationManagerDialog.scss";
@import "./views/dialogs/_TermsDialog.scss"; @import "./views/dialogs/_TermsDialog.scss";
@import "./views/dialogs/_UploadConfirmDialog.scss"; @import "./views/dialogs/_UploadConfirmDialog.scss";
@ -106,6 +112,7 @@
@import "./views/elements/_AddressTile.scss"; @import "./views/elements/_AddressTile.scss";
@import "./views/elements/_DesktopBuildsNotice.scss"; @import "./views/elements/_DesktopBuildsNotice.scss";
@import "./views/elements/_DirectorySearchBox.scss"; @import "./views/elements/_DirectorySearchBox.scss";
@import "./views/elements/_DesktopCapturerSourcePicker.scss";
@import "./views/elements/_Dropdown.scss"; @import "./views/elements/_Dropdown.scss";
@import "./views/elements/_EditableItemList.scss"; @import "./views/elements/_EditableItemList.scss";
@import "./views/elements/_ErrorBoundary.scss"; @import "./views/elements/_ErrorBoundary.scss";
@ -210,6 +217,7 @@
@import "./views/settings/_DevicesPanel.scss"; @import "./views/settings/_DevicesPanel.scss";
@import "./views/settings/_E2eAdvancedPanel.scss"; @import "./views/settings/_E2eAdvancedPanel.scss";
@import "./views/settings/_EmailAddresses.scss"; @import "./views/settings/_EmailAddresses.scss";
@import "./views/settings/_SpellCheckLanguages.scss";
@import "./views/settings/_IntegrationManager.scss"; @import "./views/settings/_IntegrationManager.scss";
@import "./views/settings/_Notifications.scss"; @import "./views/settings/_Notifications.scss";
@import "./views/settings/_PhoneNumbers.scss"; @import "./views/settings/_PhoneNumbers.scss";
@ -230,6 +238,9 @@
@import "./views/settings/tabs/user/_PreferencesUserSettingsTab.scss"; @import "./views/settings/tabs/user/_PreferencesUserSettingsTab.scss";
@import "./views/settings/tabs/user/_SecurityUserSettingsTab.scss"; @import "./views/settings/tabs/user/_SecurityUserSettingsTab.scss";
@import "./views/settings/tabs/user/_VoiceUserSettingsTab.scss"; @import "./views/settings/tabs/user/_VoiceUserSettingsTab.scss";
@import "./views/spaces/_SpaceBasicSettings.scss";
@import "./views/spaces/_SpaceCreateMenu.scss";
@import "./views/spaces/_SpacePublicShare.scss";
@import "./views/terms/_InlineTermsAgreement.scss"; @import "./views/terms/_InlineTermsAgreement.scss";
@import "./views/toasts/_AnalyticsToast.scss"; @import "./views/toasts/_AnalyticsToast.scss";
@import "./views/toasts/_NonUrgentEchoFailureToast.scss"; @import "./views/toasts/_NonUrgentEchoFailureToast.scss";

View file

@ -15,6 +15,7 @@ limitations under the License.
*/ */
$groupFilterPanelWidth: 56px; // only applies in this file, used for calculations $groupFilterPanelWidth: 56px; // only applies in this file, used for calculations
$roomListCollapsedWidth: 68px;
.mx_LeftPanel { .mx_LeftPanel {
background-color: $roomlist-bg-color; background-color: $roomlist-bg-color;
@ -37,18 +38,12 @@ $groupFilterPanelWidth: 56px; // only applies in this file, used for calculation
// GroupFilterPanel handles its own CSS // GroupFilterPanel handles its own CSS
} }
&:not(.mx_LeftPanel_hasGroupFilterPanel) {
.mx_LeftPanel_roomListContainer {
width: 100%;
}
}
// Note: The 'room list' in this context is actually everything that isn't the tag // Note: The 'room list' in this context is actually everything that isn't the tag
// panel, such as the menu options, breadcrumbs, filtering, etc // panel, such as the menu options, breadcrumbs, filtering, etc
.mx_LeftPanel_roomListContainer { .mx_LeftPanel_roomListContainer {
width: calc(100% - $groupFilterPanelWidth);
background-color: $roomlist-bg-color; background-color: $roomlist-bg-color;
flex: 1 0 0;
min-width: 0;
// Create another flexbox (this time a column) for the room list components // Create another flexbox (this time a column) for the room list components
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -168,17 +163,10 @@ $groupFilterPanelWidth: 56px; // only applies in this file, used for calculation
// These styles override the defaults for the minimized (66px) layout // These styles override the defaults for the minimized (66px) layout
&.mx_LeftPanel_minimized { &.mx_LeftPanel_minimized {
min-width: unset; min-width: unset;
width: unset !important;
// We have to forcefully set the width to override the resizer's style attribute.
&.mx_LeftPanel_hasGroupFilterPanel {
width: calc(68px + $groupFilterPanelWidth) !important;
}
&:not(.mx_LeftPanel_hasGroupFilterPanel) {
width: 68px !important;
}
.mx_LeftPanel_roomListContainer { .mx_LeftPanel_roomListContainer {
width: 68px; width: $roomListCollapsedWidth;
.mx_LeftPanel_userHeader { .mx_LeftPanel_userHeader {
flex-direction: row; flex-direction: row;

View file

@ -134,7 +134,7 @@ limitations under the License.
mask-position: center; mask-position: center;
mask-size: contain; mask-size: contain;
mask-repeat: no-repeat; mask-repeat: no-repeat;
mask-image: url('$(res)/img/feather-customised/widget/maximise.svg'); mask-image: url('$(res)/img/feather-customised/maximise.svg');
background: $muted-fg-color; background: $muted-fg-color;
} }
} }

View file

@ -18,6 +18,7 @@ limitations under the License.
display: flex; display: flex;
flex-direction: row; flex-direction: row;
min-width: 0; min-width: 0;
min-height: 0;
height: 100%; height: 100%;
} }

View file

@ -160,3 +160,20 @@ limitations under the License.
mask-position: center; mask-position: center;
} }
} }
.mx_RightPanel_scopeHeader {
margin: 24px;
text-align: center;
font-weight: $font-semi-bold;
font-size: $font-18px;
line-height: $font-22px;
.mx_BaseAvatar {
margin-right: 8px;
vertical-align: middle;
}
.mx_BaseAvatar_image {
border-radius: 8px;
}
}

View file

@ -64,28 +64,23 @@ limitations under the License.
} }
.mx_RoomDirectory_table { .mx_RoomDirectory_table {
font-size: $font-12px;
color: $primary-fg-color; color: $primary-fg-color;
width: 100%; display: grid;
font-size: $font-12px;
grid-template-columns: max-content auto max-content max-content max-content;
row-gap: 24px;
text-align: left; text-align: left;
table-layout: fixed; width: 100%;
} }
.mx_RoomDirectory_roomAvatar { .mx_RoomDirectory_roomAvatar {
width: 32px; padding: 2px 14px 0 0;
padding-right: 14px;
vertical-align: top;
}
.mx_RoomDirectory_roomDescription {
padding-bottom: 16px;
} }
.mx_RoomDirectory_roomMemberCount { .mx_RoomDirectory_roomMemberCount {
align-self: center;
color: $light-fg-color; color: $light-fg-color;
width: 60px; padding: 3px 10px 0;
padding: 0 10px;
text-align: center;
&::before { &::before {
background-color: $light-fg-color; background-color: $light-fg-color;
@ -105,8 +100,7 @@ limitations under the License.
} }
.mx_RoomDirectory_join, .mx_RoomDirectory_preview { .mx_RoomDirectory_join, .mx_RoomDirectory_preview {
width: 80px; align-self: center;
text-align: center;
white-space: nowrap; white-space: nowrap;
} }

View file

@ -20,35 +20,54 @@ limitations under the License.
flex-direction: column; flex-direction: column;
} }
@keyframes mx_RoomView_fileDropTarget_animation {
from {
opacity: 0;
}
to {
opacity: 0.95;
}
}
.mx_RoomView_fileDropTarget { .mx_RoomView_fileDropTarget {
min-width: 0px; min-width: 0px;
width: 100%; width: 100%;
height: 100%;
font-size: $font-18px; font-size: $font-18px;
text-align: center; text-align: center;
pointer-events: none; pointer-events: none;
padding-left: 12px; background-color: $primary-bg-color;
padding-right: 12px; opacity: 0.95;
margin-left: -12px;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
background-color: $droptarget-bg-color;
border: 2px #e1dddd solid;
border-bottom: none;
position: absolute; position: absolute;
top: 52px;
bottom: 0px;
z-index: 3000; z-index: 3000;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
animation: mx_RoomView_fileDropTarget_animation;
animation-duration: 0.5s;
} }
.mx_RoomView_fileDropTargetLabel { @keyframes mx_RoomView_fileDropTarget_image_animation {
top: 50%; from {
width: 100%; width: 0px;
margin-top: -50px; }
position: absolute; to {
width: 32px;
}
}
.mx_RoomView_fileDropTarget_image {
animation: mx_RoomView_fileDropTarget_image_animation;
animation-duration: 0.5s;
margin-bottom: 16px;
} }
.mx_RoomView_auxPanel { .mx_RoomView_auxPanel {
@ -117,7 +136,6 @@ limitations under the License.
} }
.mx_RoomView_body { .mx_RoomView_body {
position: relative; //for .mx_RoomView_auxPanel_fullHeight
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: 1; flex: 1;
@ -219,7 +237,7 @@ hr.mx_RoomView_myReadMarker {
position: relative; position: relative;
top: -1px; top: -1px;
z-index: 1; z-index: 1;
transition: width 400ms easeInSine 1s, opacity 400ms easeInSine 1s; transition: width 400ms easeinsine 1s, opacity 400ms easeinsine 1s;
width: 99%; width: 99%;
opacity: 1; opacity: 1;
} }

View file

@ -0,0 +1,349 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
$topLevelHeight: 32px;
$nestedHeight: 24px;
$gutterSize: 17px;
$activeStripeSize: 4px;
$activeBorderTransparentGap: 2px;
$activeBackgroundColor: $roomtile-selected-bg-color;
$activeBorderColor: $secondary-fg-color;
.mx_SpacePanel {
flex: 0 0 auto;
background-color: $groupFilterPanel-bg-color;
padding: 0;
margin: 0;
// Create another flexbox so the Panel fills the container
display: flex;
flex-direction: column;
overflow-y: auto;
.mx_SpacePanel_spaceTreeWrapper {
flex: 1;
}
.mx_SpacePanel_toggleCollapse {
flex: 0 0 auto;
width: 40px;
height: 40px;
mask-position: center;
mask-size: 32px;
mask-repeat: no-repeat;
margin-left: $gutterSize;
margin-bottom: 12px;
background-color: $roomlist-header-color;
mask-image: url('$(res)/img/element-icons/expand-space-panel.svg');
&.expanded {
transform: scaleX(-1);
}
}
ul {
margin: 0;
list-style: none;
padding: 0;
padding-left: 16px;
}
.mx_AutoHideScrollbar {
padding: 16px 12px 16px 0;
}
.mx_SpaceButton_toggleCollapse {
cursor: pointer;
}
.mx_SpaceItem.collapsed {
.mx_SpaceButton {
.mx_NotificationBadge {
right: -4px;
top: -4px;
}
}
& > .mx_SpaceButton > .mx_SpaceButton_toggleCollapse {
transform: rotate(-90deg);
}
& > .mx_SpaceTreeLevel {
display: none;
}
}
.mx_SpaceItem:not(.hasSubSpaces) > .mx_SpaceButton {
margin-left: $gutterSize;
}
.mx_SpaceButton {
border-radius: 8px;
position: relative;
margin-bottom: 2px;
display: flex;
align-items: center;
padding: 4px;
&.mx_SpaceButton_active {
&:not(.mx_SpaceButton_narrow) .mx_SpaceButton_selectionWrapper {
background-color: $activeBackgroundColor;
border-radius: 8px;
}
&.mx_SpaceButton_narrow {
.mx_BaseAvatar, .mx_SpaceButton_avatarPlaceholder {
border: 2px $activeBorderColor solid;
border-radius: 11px;
}
}
}
.mx_SpaceButton_selectionWrapper {
display: flex;
flex: 1;
align-items: center;
}
.mx_SpaceButton_name {
flex: 1;
margin-left: 8px;
white-space: nowrap;
display: block;
max-width: 150px;
text-overflow: ellipsis;
overflow: hidden;
padding-right: 8px;
font-size: $font-14px;
line-height: $font-18px;
}
.mx_SpaceButton_toggleCollapse {
width: calc($gutterSize - $activeStripeSize);
margin-left: 1px;
height: 20px;
mask-position: center;
mask-size: 20px;
mask-repeat: no-repeat;
background-color: $roomlist-header-color;
mask-image: url('$(res)/img/feather-customised/chevron-down.svg');
}
.mx_SpaceButton_icon {
width: $topLevelHeight;
min-width: $topLevelHeight;
height: $topLevelHeight;
border-radius: 8px;
position: relative;
&::before {
position: absolute;
content: '';
width: $topLevelHeight;
height: $topLevelHeight;
top: 0;
left: 0;
mask-position: center;
mask-repeat: no-repeat;
mask-size: 18px;
}
}
&.mx_SpaceButton_home .mx_SpaceButton_icon {
background-color: #ffffff;
&::before {
background-color: #3f3d3d;
mask-image: url('$(res)/img/element-icons/home.svg');
}
}
.mx_SpaceButton_avatarPlaceholder {
border: $activeBorderTransparentGap transparent solid;
padding: $activeBorderTransparentGap;
}
&.mx_SpaceButton_new .mx_SpaceButton_icon {
background-color: $accent-color;
transition: all .1s ease-in-out; // TODO transition
&::before {
background-color: #ffffff;
mask-image: url('$(res)/img/element-icons/plus.svg');
transition: all .2s ease-in-out; // TODO transition
}
}
&.mx_SpaceButton_newCancel .mx_SpaceButton_icon {
background-color: $icon-button-color;
&::before {
transform: rotate(45deg);
}
}
.mx_BaseAvatar {
/* moving the border-radius to this element from _image
element so we can add a border to it without the initials being displaced */
overflow: hidden;
border: 2px transparent solid;
padding: $activeBorderTransparentGap;
.mx_BaseAvatar_initial {
top: $activeBorderTransparentGap;
left: $activeBorderTransparentGap;
}
.mx_BaseAvatar_image {
border-radius: 8px;
}
}
.mx_SpaceButton_menuButton {
width: 20px;
min-width: 20px; // yay flex
height: 20px;
margin-top: auto;
margin-bottom: auto;
position: relative;
display: none;
&::before {
top: 2px;
left: 2px;
content: '';
width: 16px;
height: 16px;
position: absolute;
mask-position: center;
mask-size: contain;
mask-repeat: no-repeat;
mask-image: url('$(res)/img/element-icons/context-menu.svg');
background: $primary-fg-color;
}
}
}
.mx_SpacePanel_badgeContainer {
height: 16px;
// don't set width so that it takes no space when there is no badge to show
margin: auto 0; // vertically align
// Create a flexbox to make aligning dot badges easier
display: flex;
align-items: center;
.mx_NotificationBadge {
margin: 0 2px; // centering
}
.mx_NotificationBadge_dot {
// make the smaller dot occupy the same width for centering
margin-left: 7px;
margin-right: 7px;
}
}
&.collapsed {
.mx_SpaceButton {
.mx_SpacePanel_badgeContainer {
position: absolute;
right: 0px;
top: 2px;
}
}
}
&:not(.collapsed) {
.mx_SpaceButton:hover,
.mx_SpaceButton:focus-within,
.mx_SpaceButton_hasMenuOpen {
// Hide the badge container on hover because it'll be a menu button
.mx_SpacePanel_badgeContainer {
width: 0;
height: 0;
display: none;
}
.mx_SpaceButton_menuButton {
display: block;
}
}
}
/* root space buttons are bigger and not indented */
& > .mx_AutoHideScrollbar {
& > .mx_SpaceButton {
height: $topLevelHeight;
&.mx_SpaceButton_active::before {
height: $topLevelHeight;
}
}
& > ul {
padding-left: 0;
}
}
}
.mx_SpacePanel_contextMenu {
.mx_SpacePanel_contextMenu_header {
margin: 12px 16px 12px;
font-weight: $font-semi-bold;
font-size: $font-15px;
line-height: $font-18px;
}
.mx_IconizedContextMenu_optionList .mx_AccessibleButton.mx_SpacePanel_contextMenu_inviteButton {
color: $accent-color;
.mx_SpacePanel_iconInvite::before {
background-color: $accent-color;
mask-image: url('$(res)/img/element-icons/room/invite.svg');
}
}
.mx_SpacePanel_iconSettings::before {
mask-image: url('$(res)/img/element-icons/settings.svg');
}
.mx_SpacePanel_iconLeave::before {
mask-image: url('$(res)/img/element-icons/leave.svg');
}
.mx_SpacePanel_iconHome::before {
mask-image: url('$(res)/img/element-icons/roomlist/home.svg');
}
.mx_SpacePanel_iconMembers::before {
mask-image: url('$(res)/img/element-icons/room/members.svg');
}
.mx_SpacePanel_iconPlus::before {
mask-image: url('$(res)/img/element-icons/plus.svg');
}
.mx_SpacePanel_iconExplore::before {
mask-image: url('$(res)/img/element-icons/roomlist/explore.svg');
}
}
.mx_SpacePanel_sharePublicSpace {
margin: 0;
}

View file

@ -0,0 +1,231 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_SpaceRoomDirectory_dialogWrapper > .mx_Dialog {
max-width: 960px;
height: 100%;
}
.mx_SpaceRoomDirectory {
height: 100%;
margin-bottom: 12px;
color: $primary-fg-color;
word-break: break-word;
display: flex;
flex-direction: column;
.mx_Dialog_title {
display: flex;
.mx_BaseAvatar {
margin-right: 16px;
}
.mx_BaseAvatar_image {
border-radius: 8px;
}
> div {
> h1 {
font-weight: $font-semi-bold;
font-size: $font-18px;
line-height: $font-22px;
margin: 0;
}
> div {
color: $secondary-fg-color;
font-size: $font-15px;
line-height: $font-24px;
}
}
}
.mx_Dialog_content {
// TODO fix scrollbar
//display: flex;
//flex-direction: column;
//height: calc(100% - 80px);
.mx_AccessibleButton_kind_link {
padding: 0;
}
.mx_SearchBox {
margin: 24px 0 28px;
}
.mx_SpaceRoomDirectory_listHeader {
display: flex;
font-size: $font-12px;
line-height: $font-15px;
color: $secondary-fg-color;
.mx_FormButton {
margin-bottom: 8px;
}
> span {
margin: auto 0 0 auto;
}
}
}
}
.mx_SpaceRoomDirectory_list {
margin-top: 8px;
.mx_SpaceRoomDirectory_roomCount {
> h3 {
display: inline;
font-weight: $font-semi-bold;
font-size: $font-18px;
line-height: $font-22px;
color: $primary-fg-color;
}
> span {
margin-left: 8px;
font-size: $font-15px;
line-height: $font-24px;
color: $secondary-fg-color;
}
}
.mx_SpaceRoomDirectory_subspace {
margin-top: 8px;
.mx_SpaceRoomDirectory_subspace_info {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 8px;
color: $secondary-fg-color;
font-weight: $font-semi-bold;
font-size: $font-12px;
line-height: $font-15px;
.mx_BaseAvatar {
margin-right: 12px;
vertical-align: middle;
}
.mx_BaseAvatar_image {
border-radius: 8px;
}
.mx_SpaceRoomDirectory_actions {
text-align: right;
height: min-content;
margin-left: auto;
margin-right: 16px;
}
}
.mx_SpaceRoomDirectory_subspace_children {
margin-left: 12px;
border-left: 2px solid $space-button-outline-color;
padding-left: 24px;
}
}
.mx_SpaceRoomDirectory_roomTile {
padding: 16px;
border-radius: 8px;
border: 1px solid $space-button-outline-color;
margin: 8px 0 16px;
display: flex;
min-height: 76px;
box-sizing: border-box;
&.mx_AccessibleButton:hover {
background-color: rgba(141, 151, 165, 0.1);
}
.mx_BaseAvatar {
margin-right: 16px;
margin-top: 6px;
}
.mx_SpaceRoomDirectory_roomTile_info {
display: inline-block;
font-size: $font-15px;
flex-grow: 1;
height: min-content;
margin: auto 0;
.mx_SpaceRoomDirectory_roomTile_name {
font-weight: $font-semi-bold;
line-height: $font-18px;
}
.mx_SpaceRoomDirectory_roomTile_topic {
line-height: $font-24px;
color: $secondary-fg-color;
}
}
.mx_SpaceRoomDirectory_roomTile_memberCount {
position: relative;
margin: auto 0 auto 24px;
padding: 0 0 0 28px;
line-height: $font-24px;
display: inline-block;
width: 32px;
&::before {
position: absolute;
content: '';
width: 24px;
height: 24px;
top: 0;
left: 0;
mask-position: center;
mask-repeat: no-repeat;
mask-size: contain;
background-color: $secondary-fg-color;
mask-image: url('$(res)/img/element-icons/community-members.svg');
}
}
.mx_SpaceRoomDirectory_actions {
width: 180px;
text-align: right;
height: min-content;
margin: auto 0 auto 28px;
.mx_AccessibleButton {
vertical-align: middle;
& + .mx_AccessibleButton {
margin-left: 24px;
}
}
}
}
.mx_SpaceRoomDirectory_actions {
.mx_SpaceRoomDirectory_actionsText {
font-weight: normal;
font-size: $font-12px;
line-height: $font-15px;
color: $secondary-fg-color;
}
.mx_Checkbox {
display: inline-block;
}
}
}

View file

@ -0,0 +1,336 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
$SpaceRoomViewInnerWidth: 428px;
.mx_SpaceRoomView {
.mx_MainSplit > div:first-child {
padding: 80px 60px;
flex-grow: 1;
h1 {
margin: 0;
font-size: $font-24px;
font-weight: $font-semi-bold;
color: $primary-fg-color;
width: max-content;
}
.mx_SpaceRoomView_description {
font-size: $font-15px;
color: $secondary-fg-color;
margin-top: 12px;
margin-bottom: 24px;
}
.mx_SpaceRoomView_buttons {
display: block;
margin-top: 44px;
width: $SpaceRoomViewInnerWidth;
text-align: right; // button alignment right
.mx_FormButton {
padding: 8px 22px;
margin-left: 16px;
}
}
.mx_Field {
max-width: $SpaceRoomViewInnerWidth;
& + .mx_Field {
margin-top: 28px;
}
}
.mx_SpaceRoomView_errorText {
font-weight: $font-semi-bold;
font-size: $font-12px;
line-height: $font-15px;
color: $notice-primary-color;
margin-bottom: 28px;
}
.mx_AccessibleButton_disabled {
cursor: not-allowed;
}
}
.mx_SpaceRoomView_landing {
overflow-y: auto;
> .mx_BaseAvatar_image,
> .mx_BaseAvatar > .mx_BaseAvatar_image {
border-radius: 12px;
}
.mx_SpaceRoomView_landing_name {
margin: 24px 0 16px;
font-size: $font-15px;
color: $secondary-fg-color;
> span {
display: inline-block;
}
.mx_SpaceRoomView_landing_nameRow {
margin-top: 12px;
> h1 {
display: inline-block;
}
}
.mx_SpaceRoomView_landing_inviter {
.mx_BaseAvatar {
margin-right: 4px;
vertical-align: middle;
}
}
.mx_SpaceRoomView_landing_memberCount {
position: relative;
margin-left: 24px;
padding: 0 0 0 28px;
line-height: $font-24px;
vertical-align: text-bottom;
&::before {
position: absolute;
content: '';
width: 24px;
height: 24px;
top: 0;
left: 0;
mask-position: center;
mask-repeat: no-repeat;
mask-size: contain;
background-color: $accent-color;
mask-image: url('$(res)/img/element-icons/community-members.svg');
}
}
}
.mx_SpaceRoomView_landing_topic {
font-size: $font-15px;
}
.mx_SpaceRoomView_landing_joinButtons {
margin-top: 24px;
.mx_FormButton {
padding: 8px 22px;
}
}
.mx_SpaceRoomView_landing_adminButtons {
margin-top: 32px;
.mx_AccessibleButton {
position: relative;
width: 160px;
height: 124px;
box-sizing: border-box;
padding: 72px 16px 0;
border-radius: 12px;
border: 1px solid $space-button-outline-color;
margin-right: 28px;
margin-bottom: 28px;
font-size: $font-14px;
display: inline-block;
vertical-align: bottom;
&:last-child {
margin-right: 0;
}
&:hover {
background-color: rgba(141, 151, 165, 0.1);
}
&::before, &::after {
position: absolute;
content: "";
left: 16px;
top: 16px;
height: 40px;
width: 40px;
border-radius: 20px;
}
&::after {
mask-position: center;
mask-size: 30px;
mask-repeat: no-repeat;
background: #ffffff; // white icon fill
}
&.mx_SpaceRoomView_landing_inviteButton {
&::before {
background-color: $accent-color;
}
&::after {
mask-image: url('$(res)/img/element-icons/room/invite.svg');
}
}
&.mx_SpaceRoomView_landing_addButton {
&::before {
background-color: #ac3ba8;
}
&::after {
mask-image: url('$(res)/img/element-icons/roomlist/explore.svg');
}
}
&.mx_SpaceRoomView_landing_createButton {
&::before {
background-color: #368bd6;
}
&::after {
mask-image: url('$(res)/img/element-icons/roomlist/explore.svg');
}
}
&.mx_SpaceRoomView_landing_settingsButton {
&::before {
background-color: #5c56f5;
}
&::after {
mask-image: url('$(res)/img/element-icons/settings.svg');
}
}
}
}
.mx_SpaceRoomDirectory_list {
max-width: 600px;
.mx_SpaceRoomDirectory_roomTile_actions {
display: none;
}
}
}
.mx_SpaceRoomView_privateScope {
.mx_RadioButton {
width: $SpaceRoomViewInnerWidth;
border-radius: 8px;
border: 1px solid $space-button-outline-color;
padding: 16px 16px 16px 72px;
margin-top: 36px;
cursor: pointer;
box-sizing: border-box;
position: relative;
> div:first-of-type {
// hide radio dot
display: none;
}
.mx_RadioButton_content {
margin: 0;
> h3 {
margin: 0 0 4px;
font-size: $font-15px;
font-weight: $font-semi-bold;
line-height: $font-18px;
}
> div {
color: $secondary-fg-color;
font-size: $font-15px;
line-height: $font-24px;
}
}
&::before {
content: "";
position: absolute;
height: 32px;
width: 32px;
top: 24px;
left: 20px;
background-color: $secondary-fg-color;
mask-repeat: no-repeat;
mask-position: center;
mask-size: contain;
}
}
.mx_RadioButton_checked {
border-color: $accent-color;
.mx_RadioButton_content {
> div {
color: $primary-fg-color;
}
}
&::before {
background-color: $accent-color;
}
}
.mx_SpaceRoomView_privateScope_justMeButton::before {
mask-image: url('$(res)/img/element-icons/room/members.svg');
}
.mx_SpaceRoomView_privateScope_meAndMyTeammatesButton::before {
mask-image: url('$(res)/img/element-icons/community-members.svg');
}
}
.mx_SpaceRoomView_inviteTeammates {
.mx_SpaceRoomView_inviteTeammates_buttons {
color: $secondary-fg-color;
margin-top: 28px;
.mx_AccessibleButton {
position: relative;
display: inline-block;
padding-left: 32px;
line-height: 24px; // to center icons
&::before {
content: "";
position: absolute;
height: 24px;
width: 24px;
top: 0;
left: 0;
background-color: $secondary-fg-color;
mask-repeat: no-repeat;
mask-position: center;
mask-size: contain;
}
& + .mx_AccessibleButton {
margin-left: 32px;
}
}
.mx_SpaceRoomView_inviteTeammates_inviteDialogButton::before {
mask-image: url('$(res)/img/element-icons/room/invite.svg');
}
}
}
}

View file

@ -72,6 +72,7 @@ limitations under the License.
position: relative; // to make default avatars work position: relative; // to make default avatars work
margin-right: 8px; margin-right: 8px;
height: 32px; // to remove the unknown 4px gap the browser puts below it height: 32px; // to remove the unknown 4px gap the browser puts below it
padding: 3px 0; // to align with and without using doubleName
.mx_UserMenu_userAvatar { .mx_UserMenu_userAvatar {
border-radius: 32px; // should match avatar size border-radius: 32px; // should match avatar size
@ -128,7 +129,7 @@ limitations under the License.
} }
.mx_UserMenu_contextMenu { .mx_UserMenu_contextMenu {
width: 247px; width: 258px;
// These override the styles already present on the user menu rather than try to // These override the styles already present on the user menu rather than try to
// define a new menu. They are specifically for the stacked menu when a community // define a new menu. They are specifically for the stacked menu when a community
@ -272,6 +273,9 @@ limitations under the License.
.mx_UserMenu_iconHome::before { .mx_UserMenu_iconHome::before {
mask-image: url('$(res)/img/element-icons/roomlist/home.svg'); mask-image: url('$(res)/img/element-icons/roomlist/home.svg');
} }
.mx_UserMenu_iconHosting::before {
mask-image: url('$(res)/img/element-icons/brands/element.svg');
}
.mx_UserMenu_iconBell::before { .mx_UserMenu_iconBell::before {
mask-image: url('$(res)/img/element-icons/notifications.svg'); mask-image: url('$(res)/img/element-icons/notifications.svg');

View file

@ -34,7 +34,7 @@ limitations under the License.
h3 { h3 {
font-size: $font-14px; font-size: $font-14px;
font-weight: 600; font-weight: 600;
color: $authpage-primary-color; color: $authpage-secondary-color;
} }
h3.mx_AuthBody_centered { h3.mx_AuthBody_centered {

View file

@ -18,7 +18,7 @@ limitations under the License.
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 206px; width: 206px;
padding: 25px 40px; padding: 25px 25px;
box-sizing: border-box; box-sizing: border-box;
} }

View file

@ -17,7 +17,7 @@ limitations under the License.
.mx_AuthHeaderLogo { .mx_AuthHeaderLogo {
margin-top: 15px; margin-top: 15px;
flex: 1; flex: 1;
padding: 0 10px; padding: 0 25px;
} }
.mx_AuthHeaderLogo img { .mx_AuthHeaderLogo img {

View file

@ -83,7 +83,10 @@ limitations under the License.
} }
.mx_InteractiveAuthEntryComponents_termsPolicy { .mx_InteractiveAuthEntryComponents_termsPolicy {
display: block; display: flex;
flex-direction: row;
justify-content: start;
align-items: center;
} }
.mx_InteractiveAuthEntryComponents_passwordSection { .mx_InteractiveAuthEntryComponents_passwordSection {

View file

@ -23,6 +23,7 @@ limitations under the License.
font-size: $font-14px; font-size: $font-14px;
font-weight: 600; font-weight: 600;
color: $authpage-lang-color; color: $authpage-lang-color;
width: auto;
} }
.mx_AuthBody_language .mx_Dropdown_arrow { .mx_AuthBody_language .mx_Dropdown_arrow {

View file

@ -18,7 +18,6 @@ limitations under the License.
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
&.mx_WelcomePage_registrationDisabled { &.mx_WelcomePage_registrationDisabled {
.mx_ButtonCreateAccount { .mx_ButtonCreateAccount {
display: none; display: none;
@ -27,6 +26,6 @@ limitations under the License.
} }
.mx_Welcome .mx_AuthBody_language { .mx_Welcome .mx_AuthBody_language {
width: 120px; width: 160px;
margin-bottom: 10px; margin-bottom: 10px;
} }

View file

@ -75,6 +75,11 @@ limitations under the License.
background-color: $menu-selected-color; background-color: $menu-selected-color;
} }
&.mx_AccessibleButton_disabled {
opacity: 0.5;
cursor: not-allowed;
}
img, .mx_IconizedContextMenu_icon { // icons img, .mx_IconizedContextMenu_icon { // icons
width: 16px; width: 16px;
min-width: 16px; min-width: 16px;

View file

@ -0,0 +1,185 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_AddExistingToSpaceDialog_wrapper {
.mx_Dialog {
display: flex;
flex-direction: column;
}
}
.mx_AddExistingToSpaceDialog {
width: 480px;
color: $primary-fg-color;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
min-height: 0;
.mx_Dialog_title {
display: flex;
.mx_BaseAvatar {
display: inline-flex;
margin: 5px 16px 5px 5px;
vertical-align: middle;
}
.mx_BaseAvatar_image {
border-radius: 8px;
margin: 0;
vertical-align: unset;
}
> div {
> h1 {
font-weight: $font-semi-bold;
font-size: $font-18px;
line-height: $font-22px;
margin: 0;
}
.mx_AddExistingToSpaceDialog_onlySpace {
color: $secondary-fg-color;
font-size: $font-15px;
line-height: $font-24px;
}
}
.mx_Dropdown_input {
border: none;
> .mx_Dropdown_option {
padding-left: 0;
flex: unset;
height: unset;
color: $secondary-fg-color;
font-size: $font-15px;
line-height: $font-24px;
.mx_BaseAvatar {
display: none;
}
}
.mx_Dropdown_menu {
.mx_AddExistingToSpaceDialog_dropdownOptionActive {
color: $accent-color;
padding-right: 32px;
position: relative;
&::before {
content: '';
width: 20px;
height: 20px;
top: 8px;
right: 0;
position: absolute;
mask-position: center;
mask-size: contain;
mask-repeat: no-repeat;
background-color: $accent-color;
mask-image: url('$(res)/img/element-icons/roomlist/checkmark.svg');
}
}
}
}
}
.mx_SearchBox {
margin: 0;
}
.mx_AddExistingToSpaceDialog_errorText {
font-weight: $font-semi-bold;
font-size: $font-12px;
line-height: $font-15px;
color: $notice-primary-color;
margin-bottom: 28px;
}
.mx_AddExistingToSpaceDialog_content {
.mx_AddExistingToSpaceDialog_noResults {
margin-top: 24px;
}
}
.mx_AddExistingToSpaceDialog_section {
margin-top: 24px;
> h3 {
margin: 0;
color: $secondary-fg-color;
font-size: $font-12px;
font-weight: $font-semi-bold;
line-height: $font-15px;
}
.mx_AddExistingToSpaceDialog_entry {
display: flex;
margin-top: 12px;
.mx_BaseAvatar {
margin-right: 12px;
}
.mx_AddExistingToSpaceDialog_entry_name {
font-size: $font-15px;
line-height: 30px;
flex-grow: 1;
}
.mx_FormButton {
min-width: 92px;
font-weight: normal;
box-sizing: border-box;
}
}
}
.mx_AddExistingToSpaceDialog_section_spaces {
.mx_BaseAvatar_image {
border-radius: 8px;
}
}
.mx_AddExistingToSpaceDialog_footer {
display: flex;
margin-top: 32px;
> span {
flex-grow: 1;
font-size: $font-12px;
line-height: $font-15px;
> * {
vertical-align: middle;
}
}
.mx_AccessibleButton {
display: inline-block;
}
.mx_AccessibleButton_kind_link {
padding: 0;
}
}
.mx_FormButton {
padding: 8px 22px;
}
}

View file

@ -223,3 +223,54 @@ limitations under the License.
content: ":"; content: ":";
} }
} }
.mx_DevTools_SettingsExplorer {
table {
width: 100%;
table-layout: fixed;
border-collapse: collapse;
th {
// Colour choice: first one autocomplete gave me.
border-bottom: 1px solid $accent-color;
text-align: left;
}
td, th {
width: 360px; // "feels right" number
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
td + td, th + th {
width: auto;
}
tr:hover {
// Colour choice: first one autocomplete gave me.
background-color: $accent-color-50pct;
}
}
.mx_DevTools_SettingsExplorer_mutable {
background-color: $accent-color;
}
.mx_DevTools_SettingsExplorer_immutable {
background-color: $warning-color;
}
.mx_DevTools_SettingsExplorer_edit {
float: right;
margin-right: 16px;
}
.mx_DevTools_SettingsExplorer_warning {
border: 2px solid $warning-color;
border-radius: 4px;
padding: 4px;
margin-bottom: 8px;
}
}

View file

@ -0,0 +1,143 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_HostSignupDialog {
width: 90vw;
max-width: 580px;
height: 80vh;
max-height: 600px;
// Ensure dialog borders are always white as the HostSignupDialog
// does not yet support dark mode or theming in general.
// In the future we might want to pass the theme to the called
// iframe, should some hosting provider have that need.
background-color: #ffffff;
.mx_HostSignupDialog_info {
text-align: center;
.mx_HostSignupDialog_content_top {
margin-bottom: 24px;
}
.mx_HostSignupDialog_paragraphs {
text-align: left;
padding-left: 25%;
padding-right: 25%;
}
.mx_HostSignupDialog_buttons {
margin-bottom: 24px;
display: flex;
justify-content: center;
button {
padding: 12px;
margin: 0 16px;
}
}
.mx_HostSignupDialog_footer {
display: flex;
justify-content: center;
align-items: baseline;
img {
padding-right: 5px;
}
}
}
iframe {
width: 100%;
height: 100%;
border: none;
background-color: #fff;
min-height: 540px;
}
}
.mx_HostSignupDialog_text_dark {
color: $primary-fg-color;
}
.mx_HostSignupDialog_text_light {
color: $secondary-fg-color;
}
.mx_HostSignup_maximize_button {
mask: url('$(res)/img/feather-customised/maximise.svg');
mask-repeat: no-repeat;
mask-position: center;
mask-size: cover;
width: 14px;
height: 14px;
background-color: $dialog-close-fg-color;
cursor: pointer;
position: absolute;
top: 10px;
right: 10px;
}
.mx_HostSignup_minimize_button {
mask: url('$(res)/img/feather-customised/minimise.svg');
mask-repeat: no-repeat;
mask-position: center;
mask-size: cover;
width: 14px;
height: 14px;
background-color: $dialog-close-fg-color;
cursor: pointer;
position: absolute;
top: 10px;
right: 25px;
}
.mx_HostSignup_persisted {
width: 90vw;
max-width: 580px;
height: 80vh;
max-height: 600px;
top: 0;
left: 0;
position: fixed;
display: none;
}
.mx_HostSignupDialog_minimized {
position: fixed;
bottom: 80px;
right: 26px;
width: 314px;
height: 217px;
overflow: hidden;
&.mx_Dialog {
padding: 12px;
}
.mx_Dialog_title {
text-align: left !important;
padding-left: 20px;
font-size: $font-15px;
}
iframe {
width: 100%;
height: 100%;
border: none;
background-color: #fff;
}
}

View file

@ -0,0 +1,55 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_SpaceSettingsDialog {
width: 480px;
color: $primary-fg-color;
.mx_SpaceSettings_errorText {
font-weight: $font-semi-bold;
font-size: $font-12px;
line-height: $font-15px;
color: $notice-primary-color;
margin-bottom: 28px;
}
.mx_ToggleSwitch {
display: inline-block;
vertical-align: middle;
margin-left: 16px;
}
.mx_AccessibleButton_kind_danger {
margin-top: 28px;
}
.mx_SpaceSettingsDialog_buttons {
display: flex;
margin-top: 64px;
.mx_AccessibleButton {
display: inline-block;
}
.mx_AccessibleButton_kind_link {
margin-left: auto;
}
}
.mx_FormButton {
padding: 8px 22px;
}
}

View file

@ -0,0 +1,72 @@
/*
Copyright 2021 Šimon Brandner <simon.bra.ag@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_desktopCapturerSourcePicker {
overflow: hidden;
}
.mx_desktopCapturerSourcePicker_tabLabels {
display: flex;
padding: 0 0 8px 0;
}
.mx_desktopCapturerSourcePicker_tabLabel,
.mx_desktopCapturerSourcePicker_tabLabel_selected {
width: 100%;
text-align: center;
border-radius: 8px;
padding: 8px 0;
font-size: $font-13px;
}
.mx_desktopCapturerSourcePicker_tabLabel_selected {
background-color: $tab-label-active-bg-color;
color: $tab-label-active-fg-color;
}
.mx_desktopCapturerSourcePicker_panel {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: flex-start;
height: 500px;
overflow: overlay;
}
.mx_desktopCapturerSourcePicker_stream_button {
display: flex;
flex-direction: column;
margin: 8px;
border-radius: 4px;
}
.mx_desktopCapturerSourcePicker_stream_button:hover,
.mx_desktopCapturerSourcePicker_stream_button:focus {
background: $roomtile-selected-bg-color;
}
.mx_desktopCapturerSourcePicker_stream_thumbnail {
margin: 4px;
width: 312px;
}
.mx_desktopCapturerSourcePicker_stream_name {
margin: 0 4px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
width: 312px;
}

View file

@ -28,8 +28,14 @@ limitations under the License.
.mx_SSOButton { .mx_SSOButton {
position: relative; position: relative;
width: 100%; width: 100%;
padding-left: 32px; padding: 7px 32px;
padding-right: 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 { > img {
object-fit: contain; object-fit: contain;
@ -39,6 +45,16 @@ 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 { .mx_SSOButton_mini {
box-sizing: border-box; box-sizing: border-box;
width: 50px; // 48px + 1px border on all sides width: 50px; // 48px + 1px border on all sides

View file

@ -59,7 +59,7 @@ limitations under the License.
} }
.mx_ServerPicker_server { .mx_ServerPicker_server {
color: $primary-fg-color; color: $authpage-primary-color;
grid-column: 1; grid-column: 1;
grid-row: 2; grid-row: 2;
margin-bottom: 16px; margin-bottom: 16px;

View file

@ -30,7 +30,7 @@ limitations under the License.
mask-size: contain; mask-size: contain;
content: ''; content: '';
position: absolute; position: absolute;
top: 2px; top: 1px;
left: 0; left: 0;
} }
} }

View file

@ -35,13 +35,13 @@ limitations under the License.
mask-size: auto 12px; mask-size: auto 12px;
visibility: hidden; visibility: hidden;
background-color: $accent-color; background-color: $accent-color;
mask-image: url('$(res)/img/feather-customised/widget/maximise.svg'); mask-image: url('$(res)/img/feather-customised/maximise.svg');
} }
&.mx_ViewSourceEvent_expanded .mx_ViewSourceEvent_toggle { &.mx_ViewSourceEvent_expanded .mx_ViewSourceEvent_toggle {
mask-position: 0 bottom; mask-position: 0 bottom;
margin-bottom: 7px; margin-bottom: 7px;
mask-image: url('$(res)/img/feather-customised/widget/minimise.svg'); mask-image: url('$(res)/img/feather-customised/minimise.svg');
} }
&:hover .mx_ViewSourceEvent_toggle { &:hover .mx_ViewSourceEvent_toggle {

View file

@ -17,7 +17,7 @@ limitations under the License.
.m_RoomView_auxPanel_stateViews { .m_RoomView_auxPanel_stateViews {
padding: 5px; padding: 5px;
padding-left: 19px; padding-left: 19px;
border-bottom: 1px solid #e5e5e5; border-bottom: 1px solid $primary-hairline-color;
} }
.m_RoomView_auxPanel_stateViews_span a { .m_RoomView_auxPanel_stateViews_span a {

View file

@ -26,7 +26,7 @@ $left-gutter: 64px;
} }
.mx_EventTile.mx_EventTile_info { .mx_EventTile.mx_EventTile_info {
padding-top: 0px; padding-top: 1px;
} }
.mx_EventTile_avatar { .mx_EventTile_avatar {
@ -37,7 +37,7 @@ $left-gutter: 64px;
} }
.mx_EventTile.mx_EventTile_info .mx_EventTile_avatar { .mx_EventTile.mx_EventTile_info .mx_EventTile_avatar {
top: $font-8px; top: $font-6px;
left: $left-gutter; left: $left-gutter;
} }
@ -74,7 +74,6 @@ $left-gutter: 64px;
margin-left: 5px; margin-left: 5px;
display: inline-block; display: inline-block;
vertical-align: top; vertical-align: top;
height: 16px;
overflow: hidden; overflow: hidden;
user-select: none; user-select: none;
@ -258,17 +257,13 @@ $left-gutter: 64px;
display: inline-block; display: inline-block;
width: 14px; width: 14px;
height: 14px; height: 14px;
top: 29px; // This aligns the avatar with the last line of the
// message. We want to move it one line up - 2.2rem
top: -2.2rem;
user-select: none; user-select: none;
z-index: 1; z-index: 1;
} }
.mx_EventTile_continuation .mx_EventTile_readAvatars,
.mx_EventTile_info .mx_EventTile_readAvatars,
.mx_EventTile_emote .mx_EventTile_readAvatars {
top: 7px;
}
.mx_EventTile_readAvatars .mx_BaseAvatar { .mx_EventTile_readAvatars .mx_BaseAvatar {
position: absolute; position: absolute;
display: inline-block; display: inline-block;
@ -421,15 +416,15 @@ $left-gutter: 64px;
} }
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line { .mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line {
border-left: $e2e-verified-color 4px solid; border-left: $e2e-verified-color $EventTile_e2e_state_indicator_width solid;
} }
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line { .mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line {
border-left: $e2e-unverified-color 4px solid; border-left: $e2e-unverified-color $EventTile_e2e_state_indicator_width solid;
} }
.mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line { .mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line {
border-left: $e2e-unknown-color 4px solid; border-left: $e2e-unknown-color $EventTile_e2e_state_indicator_width solid;
} }
.mx_EventTile:hover.mx_EventTile_verified.mx_EventTile_info .mx_EventTile_line, .mx_EventTile:hover.mx_EventTile_verified.mx_EventTile_info .mx_EventTile_line,
@ -447,8 +442,7 @@ $left-gutter: 64px;
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line > a > .mx_MessageTimestamp, .mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line > a > .mx_MessageTimestamp,
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line > a > .mx_MessageTimestamp, .mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line > a > .mx_MessageTimestamp,
.mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line > a > .mx_MessageTimestamp { .mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line > a > .mx_MessageTimestamp {
left: 3px; width: $MessageTimestamp_width_hover;
width: auto;
} }
// Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies) // Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies)
@ -493,7 +487,6 @@ $left-gutter: 64px;
// https://github.com/vector-im/vector-web/issues/754 // https://github.com/vector-im/vector-web/issues/754
overflow-x: overlay; overflow-x: overlay;
overflow-y: visible; overflow-y: visible;
max-height: 30vh;
} }
code { code {
@ -502,6 +495,22 @@ $left-gutter: 64px;
} }
} }
.mx_EventTile_lineNumbers {
float: left;
margin: 0 0.5em 0 -1.5em;
color: gray;
}
.mx_EventTile_lineNumber {
text-align: right;
display: block;
padding-left: 1em;
}
.mx_EventTile_collapsedCodeBlock {
max-height: 30vh;
}
.mx_EventTile:hover .mx_EventTile_body pre, .mx_EventTile:hover .mx_EventTile_body pre,
.mx_EventTile.focus-visible:focus-within .mx_EventTile_body pre { .mx_EventTile.focus-visible:focus-within .mx_EventTile_body pre {
border: 1px solid #e5e5e5; // deliberate constant as we're behind an invert filter border: 1px solid #e5e5e5; // deliberate constant as we're behind an invert filter
@ -513,21 +522,42 @@ $left-gutter: 64px;
} }
// Inserted adjacent to <pre> blocks, (See TextualBody) // Inserted adjacent to <pre> blocks, (See TextualBody)
.mx_EventTile_copyButton { .mx_EventTile_button {
position: absolute; position: absolute;
display: inline-block; display: inline-block;
visibility: hidden; visibility: hidden;
cursor: pointer; cursor: pointer;
top: 6px; top: 8px;
right: 6px; right: 8px;
width: 19px; width: 19px;
height: 19px; height: 19px;
mask-image: url($copy-button-url);
background-color: $message-action-bar-fg-color; background-color: $message-action-bar-fg-color;
} }
.mx_EventTile_buttonBottom {
top: 33px;
}
.mx_EventTile_copyButton {
mask-image: url($copy-button-url);
}
.mx_EventTile_collapseButton {
mask-size: 75%;
mask-position: center;
mask-repeat: no-repeat;
mask-image: url($collapse-button-url);
}
.mx_EventTile_expandButton {
mask-size: 75%;
mask-position: center;
mask-repeat: no-repeat;
mask-image: url($expand-button-url);
}
.mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_copyButton, .mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_copyButton,
.mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_copyButton { .mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_copyButton,
.mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_collapseButton,
.mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_collapseButton,
.mx_EventTile_body .mx_EventTile_pre_container:focus-within .mx_EventTile_expandButton,
.mx_EventTile_body .mx_EventTile_pre_container:hover .mx_EventTile_expandButton {
visibility: visible; visibility: visible;
} }

View file

@ -20,7 +20,7 @@ $left-gutter: 64px;
.mx_GroupLayout { .mx_GroupLayout {
.mx_EventTile { .mx_EventTile {
> .mx_SenderProfile { > .mx_SenderProfile {
line-height: $font-17px; line-height: $font-20px;
padding-left: $left-gutter; padding-left: $left-gutter;
} }
@ -34,11 +34,11 @@ $left-gutter: 64px;
.mx_MessageTimestamp { .mx_MessageTimestamp {
position: absolute; position: absolute;
width: 46px; /* 8 + 30 (avatar) + 8 */ width: $MessageTimestamp_width;
} }
.mx_EventTile_line, .mx_EventTile_reply { .mx_EventTile_line, .mx_EventTile_reply {
padding-top: 3px; padding-top: 1px;
padding-bottom: 3px; padding-bottom: 3px;
line-height: $font-22px; line-height: $font-22px;
} }
@ -105,16 +105,9 @@ $left-gutter: 64px;
} }
.mx_EventTile_readAvatars { .mx_EventTile_readAvatars {
top: 27px; // This aligns the avatar with the last line of the
} // message. We want to move it one line up - 2rem
top: -2rem;
&.mx_EventTile_continuation .mx_EventTile_readAvatars,
&.mx_EventTile_emote .mx_EventTile_readAvatars {
top: 5px;
}
&.mx_EventTile_info .mx_EventTile_readAvatars {
top: 4px;
} }
.mx_EventTile_content .markdown-body { .mx_EventTile_content .markdown-body {

View file

@ -207,6 +207,17 @@ $irc-line-height: $font-18px;
width: unset; width: unset;
max-width: var(--name-width); max-width: var(--name-width);
} }
.mx_SenderProfile_hover {
background: transparent;
> span {
> .mx_SenderProfile_name,
> .mx_SenderProfile_aux {
min-width: inherit;
}
}
}
} }
.mx_ProfileResizer { .mx_ProfileResizer {

View file

@ -19,6 +19,7 @@ limitations under the License.
flex-direction: column; flex-direction: column;
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
margin-top: 8px;
} }
.mx_MemberInfo_name { .mx_MemberInfo_name {

View file

@ -44,6 +44,12 @@ limitations under the License.
.mx_AutoHideScrollbar { .mx_AutoHideScrollbar {
flex: 1 1 0; flex: 1 1 0;
} }
.mx_RightPanel_scopeHeader {
// vertically align with position on other right panel cards
// to prevent it bouncing as user navigates right panel
margin-top: -8px;
}
} }
.mx_GroupMemberList_query, .mx_GroupMemberList_query,

View file

@ -227,18 +227,6 @@ limitations under the License.
mask-image: url('$(res)/img/element-icons/room/composer/attach.svg'); mask-image: url('$(res)/img/element-icons/room/composer/attach.svg');
} }
.mx_MessageComposer_hangup::before {
mask-image: url('$(res)/img/element-icons/call/hangup.svg');
}
.mx_MessageComposer_voicecall::before {
mask-image: url('$(res)/img/element-icons/call/voice-call.svg');
}
.mx_MessageComposer_videocall::before {
mask-image: url('$(res)/img/element-icons/call/video-call.svg');
}
.mx_MessageComposer_emoji::before { .mx_MessageComposer_emoji::before {
mask-image: url('$(res)/img/element-icons/room/composer/emoji.svg'); mask-image: url('$(res)/img/element-icons/room/composer/emoji.svg');
} }
@ -247,6 +235,32 @@ limitations under the License.
mask-image: url('$(res)/img/element-icons/room/composer/sticker.svg'); mask-image: url('$(res)/img/element-icons/room/composer/sticker.svg');
} }
.mx_MessageComposer_sendMessage {
cursor: pointer;
position: relative;
margin-right: 6px;
width: 32px;
height: 32px;
border-radius: 100%;
background-color: $button-bg-color;
&::before {
position: absolute;
height: 16px;
width: 16px;
top: 8px;
left: 9px;
mask-image: url('$(res)/img/element-icons/send-message.svg');
mask-repeat: no-repeat;
mask-size: contain;
mask-position: center;
background-color: $button-fg-color;
content: '';
}
}
.mx_MessageComposer_formatting { .mx_MessageComposer_formatting {
cursor: pointer; cursor: pointer;
margin: 0 11px; margin: 0 11px;

View file

@ -252,6 +252,19 @@ limitations under the License.
mask-image: url('$(res)/img/element-icons/room/search-inset.svg'); mask-image: url('$(res)/img/element-icons/room/search-inset.svg');
} }
.mx_RoomHeader_voiceCallButton::before {
mask-image: url('$(res)/img/element-icons/call/voice-call.svg');
// The call button SVG is padded slightly differently, so match it up to the size
// of the other icons
mask-size: 20px;
mask-position: center;
}
.mx_RoomHeader_videoCallButton::before {
mask-image: url('$(res)/img/element-icons/call/video-call.svg');
}
.mx_RoomHeader_showPanel { .mx_RoomHeader_showPanel {
height: 16px; height: 16px;
} }

View file

@ -19,7 +19,10 @@ limitations under the License.
} }
.mx_RoomList_iconPlus::before { .mx_RoomList_iconPlus::before {
mask-image: url('$(res)/img/element-icons/roomlist/plus.svg'); mask-image: url('$(res)/img/element-icons/roomlist/plus-circle.svg');
}
.mx_RoomList_iconHash::before {
mask-image: url('$(res)/img/element-icons/roomlist/hash-circle.svg');
} }
.mx_RoomList_iconExplore::before { .mx_RoomList_iconExplore::before {
mask-image: url('$(res)/img/element-icons/roomlist/explore.svg'); mask-image: url('$(res)/img/element-icons/roomlist/explore.svg');

View file

@ -197,6 +197,9 @@ limitations under the License.
.mx_RoomSublist_resizerHandles { .mx_RoomSublist_resizerHandles {
flex: 0 0 4px; flex: 0 0 4px;
display: flex;
justify-content: center;
width: 100%;
} }
// Class name comes from the ResizableBox component // Class name comes from the ResizableBox component
@ -207,17 +210,12 @@ limitations under the License.
border-radius: 3px; border-radius: 3px;
// Override styles from library // Override styles from library
width: unset !important; max-width: 64px;
height: 4px !important; // Update RESIZE_HANDLE_HEIGHT if this changes height: 4px !important; // Update RESIZE_HANDLE_HEIGHT if this changes
// This is positioned directly below the 'show more' button. // This is positioned directly below the 'show more' button.
position: absolute; position: relative !important;
bottom: 0 !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;
} }
&:hover, &.mx_RoomSublist_hasMenuOpen { &:hover, &.mx_RoomSublist_hasMenuOpen {

View file

@ -64,6 +64,7 @@ limitations under the License.
.mx_UserNotifSettings_notifTable { .mx_UserNotifSettings_notifTable {
display: table; display: table;
position: relative;
} }
.mx_UserNotifSettings_notifTable .mx_Spinner { .mx_UserNotifSettings_notifTable .mx_Spinner {

View file

@ -0,0 +1,35 @@
/*
Copyright 2021 Šimon Brandner <simon.bra.ag@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_ExistingSpellCheckLanguage {
display: flex;
align-items: center;
margin-bottom: 5px;
}
.mx_ExistingSpellCheckLanguage_language {
flex: 1;
margin-right: 10px;
}
.mx_GeneralUserSettingsTab_spellCheckLanguageInput {
margin-top: 1em;
margin-bottom: 1em;
}
.mx_SpellCheckLanguages {
@mixin mx_Settings_fullWidthField;
}

View file

@ -0,0 +1,86 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_SpaceBasicSettings {
.mx_Field {
margin: 32px 0;
}
.mx_SpaceBasicSettings_avatarContainer {
display: flex;
margin-top: 24px;
.mx_SpaceBasicSettings_avatar {
position: relative;
height: 80px;
width: 80px;
background-color: $tertiary-fg-color;
border-radius: 16px;
}
img.mx_SpaceBasicSettings_avatar {
width: 80px;
height: 80px;
object-fit: cover;
border-radius: 16px;
}
// only show it when the button is a div and not an img (has avatar)
div.mx_SpaceBasicSettings_avatar {
cursor: pointer;
&::before {
content: "";
position: absolute;
height: 80px;
width: 80px;
top: 0;
left: 0;
background-color: #ffffff; // white icon fill
mask-repeat: no-repeat;
mask-position: center;
mask-size: 20px;
mask-image: url('$(res)/img/element-icons/camera.svg');
}
}
> input[type="file"] {
display: none;
}
> .mx_AccessibleButton_kind_link {
display: inline-block;
padding: 0;
margin: auto 16px;
color: #368bd6;
}
> .mx_SpaceBasicSettings_avatar_remove {
color: $notice-primary-color;
}
}
.mx_FormButton {
padding: 8px 22px;
margin-left: auto;
display: block;
width: min-content;
}
.mx_AccessibleButton_disabled {
cursor: not-allowed;
}
}

View file

@ -0,0 +1,138 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// TODO: the space panel currently does not have a fixed width,
// just the headers at each level have a max-width of 150px
// so this will look slightly off for now. We should probably use css grid for the whole main layout...
$spacePanelWidth: 200px;
.mx_SpaceCreateMenu_wrapper {
// background blur everything except SpacePanel
.mx_ContextualMenu_background {
background-color: $dialog-backdrop-color;
opacity: 0.6;
left: $spacePanelWidth;
}
.mx_ContextualMenu {
padding: 24px;
width: 480px;
box-sizing: border-box;
background-color: $primary-bg-color;
> div {
> h2 {
font-weight: $font-semi-bold;
font-size: $font-18px;
margin-top: 4px;
}
> p {
font-size: $font-15px;
color: $secondary-fg-color;
margin: 0;
}
}
.mx_SpaceCreateMenuType {
position: relative;
padding: 16px 32px 16px 72px;
width: 432px;
box-sizing: border-box;
border-radius: 8px;
border: 1px solid $input-darker-bg-color;
font-size: $font-15px;
margin: 20px 0;
> h3 {
font-weight: $font-semi-bold;
margin: 0 0 4px;
}
> span {
color: $secondary-fg-color;
}
&::before {
position: absolute;
content: '';
width: 32px;
height: 32px;
top: 24px;
left: 20px;
mask-position: center;
mask-repeat: no-repeat;
mask-size: 32px;
background-color: $tertiary-fg-color;
}
&:hover {
border-color: $accent-color;
&::before {
background-color: $accent-color;
}
> span {
color: $primary-fg-color;
}
}
}
.mx_SpaceCreateMenuType_public::before {
mask-image: url('$(res)/img/globe.svg');
mask-size: 26px;
}
.mx_SpaceCreateMenuType_private::before {
mask-image: url('$(res)/img/element-icons/lock.svg');
}
.mx_SpaceCreateMenu_back {
width: 28px;
height: 28px;
position: relative;
background-color: $theme-button-bg-color;
border-radius: 14px;
margin-bottom: 12px;
&::before {
content: "";
position: absolute;
height: 28px;
width: 28px;
top: 0;
left: 0;
background-color: $muted-fg-color;
transform: rotate(90deg);
mask-repeat: no-repeat;
mask-position: 2px 3px;
mask-size: 24px;
mask-image: url('$(res)/img/feather-customised/chevron-down.svg');
}
}
.mx_FormButton {
padding: 8px 22px;
margin-left: auto;
display: block;
width: min-content;
}
.mx_AccessibleButton_disabled {
cursor: not-allowed;
}
}
}

View file

@ -0,0 +1,60 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_SpacePublicShare {
.mx_AccessibleButton {
border: 1px solid $space-button-outline-color;
box-sizing: border-box;
border-radius: 8px;
padding: 12px 24px 12px 52px;
margin-top: 16px;
width: $SpaceRoomViewInnerWidth;
font-size: $font-15px;
line-height: $font-24px;
position: relative;
display: flex;
> span {
color: #368bd6;
margin-left: auto;
}
&:hover {
background-color: rgba(141, 151, 165, 0.1);
}
&::before {
content: "";
position: absolute;
width: 30px;
height: 30px;
mask-repeat: no-repeat;
mask-size: contain;
mask-position: center;
background: $muted-fg-color;
left: 12px;
top: 9px;
}
&.mx_SpacePublicShare_shareButton::before {
mask-image: url('$(res)/img/element-icons/link.svg');
}
&.mx_SpacePublicShare_inviteButton::before {
mask-image: url('$(res)/img/element-icons/room/invite.svg');
}
}
}

View file

@ -16,6 +16,7 @@ limitations under the License.
.mx_VideoFeed_remote { .mx_VideoFeed_remote {
width: 100%; width: 100%;
max-height: 100%;
background-color: #000; background-color: #000;
z-index: 50; z-index: 50;
} }

View file

@ -0,0 +1,3 @@
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.9803 1.2796C17.0771 2.54383 16.6773 3.79601 15.8657 4.77022C15.0784 5.74949 13.8854 6.31354 12.629 6.3006C12.5491 5.07276 12.9605 3.86352 13.7727 2.93921C14.5952 2.00238 15.7404 1.40982 16.9803 1.2796ZM20.9539 8.70795C19.5086 9.59652 18.6192 11.1635 18.5974 12.86C18.5994 14.7794 19.7489 16.5115 21.5166 17.2592C21.1766 18.3636 20.6642 19.4073 19.9982 20.3517C19.1038 21.6896 18.1661 22.9967 16.6777 23.0208C15.9698 23.0372 15.492 22.8336 14.9941 22.6215C14.4747 22.4003 13.9335 22.1697 13.0867 22.1697C12.1885 22.1697 11.6231 22.4077 11.0778 22.6372C10.6065 22.8355 10.1503 23.0275 9.50727 23.0542C8.08982 23.1067 7.00654 21.6263 6.07964 20.3009C4.22703 17.5943 2.78444 12.6733 4.71844 9.32483C5.62662 7.69286 7.32468 6.65727 9.19136 6.59696C9.99528 6.58042 10.7667 6.89028 11.443 7.16193C11.9602 7.36969 12.4219 7.5551 12.7999 7.5551C13.1321 7.5551 13.5809 7.37701 14.1038 7.16946C14.9276 6.84251 15.9356 6.44246 16.9628 6.55027C18.5589 6.60021 20.038 7.39984 20.9539 8.70795Z" fill="#17191C"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,6 @@
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.12012 1.02C6.12012 0.45667 6.57679 0 7.14012 0C10.8957 0 13.9401 3.04446 13.9401 6.8C13.9401 7.36333 13.4834 7.82 12.9201 7.82C12.3568 7.82 11.9001 7.36333 11.9001 6.8C11.9001 4.17112 9.76899 2.04 7.14012 2.04C6.57679 2.04 6.12012 1.58333 6.12012 1.02Z" fill="#1E1E1E"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.8799 15.98C10.8799 16.5433 10.4232 17 9.85988 17C6.10435 17 3.05989 13.9555 3.05989 10.2C3.05989 9.63667 3.51656 9.18 4.07989 9.18C4.64322 9.18 5.09989 9.63667 5.09989 10.2C5.09989 12.8289 7.23101 14.96 9.85988 14.96C10.4232 14.96 10.8799 15.4167 10.8799 15.98Z" fill="#1E1E1E"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.02 10.88C0.45667 10.88 -1.99617e-08 10.4233 -4.45856e-08 9.86C-2.08745e-07 6.10447 3.04446 3.06 6.8 3.06C7.36333 3.06 7.82 3.51667 7.82 4.08C7.82 4.64334 7.36333 5.1 6.8 5.1C4.17113 5.1 2.04 7.23113 2.04 9.86C2.04 10.4233 1.58333 10.88 1.02 10.88Z" fill="#1E1E1E"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.98 6.12C16.5433 6.12 17 6.57666 17 7.14C17 10.8955 13.9555 13.94 10.2 13.94C9.63667 13.94 9.18 13.4833 9.18 12.92C9.18 12.3567 9.63667 11.9 10.2 11.9C12.8289 11.9 14.96 9.76887 14.96 7.14C14.96 6.57666 15.4167 6.12 15.98 6.12Z" fill="#1E1E1E"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,9 @@
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="2" y="1" width="22" height="22">
<path d="M2.10154 1.5H23.1003V22.3716H2.10154V1.5Z" fill="white"/>
</mask>
<g mask="url(#mask0)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.1 11.999C23.1 6.20003 18.399 1.49902 12.6 1.49902C6.801 1.49902 2.1 6.20003 2.1 11.999C2.1 17.2399 5.9397 21.5838 10.9594 22.3715V15.0342H8.29336V11.999H10.9594V9.68574C10.9594 7.05418 12.5269 5.60059 14.9254 5.60059C16.0742 5.60059 17.2758 5.80566 17.2758 5.80566V8.38965H15.9518C14.6474 8.38965 14.2406 9.19903 14.2406 10.0294V11.999H17.1527L16.6872 15.0342H14.2406V22.3715C19.2603 21.5838 23.1 17.2399 23.1 11.999Z" fill="#1877F2"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.6872 15.0342L17.1527 11.999H14.2406V10.0294C14.2406 9.19903 14.6474 8.38965 15.9518 8.38965H17.2758V5.80566C17.2758 5.80566 16.0742 5.60059 14.9254 5.60059C12.5269 5.60059 10.9594 7.05418 10.9594 9.68574V11.999H8.29336V15.0342H10.9594V22.3715C11.494 22.4553 12.0419 22.499 12.6 22.499C13.1581 22.499 13.706 22.4553 14.2406 22.3715V15.0342H16.6872Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,3 @@
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20.8421 7.10595C19.9703 5.6121 18.7876 4.42942 17.2939 3.55764C15.8 2.68581 14.169 2.25001 12.3999 2.25001C10.6311 2.25001 8.9996 2.68594 7.50597 3.55764C6.01212 4.42938 4.82953 5.6121 3.95765 7.10595C3.08592 8.59976 2.65002 10.231 2.65002 11.9997C2.65002 14.1242 3.26987 16.0346 4.50987 17.7315C5.74973 19.4284 7.35145 20.6027 9.3149 21.2543C9.54345 21.2967 9.71264 21.2669 9.82265 21.1656C9.9327 21.0641 9.98766 20.937 9.98766 20.7848C9.98766 20.7595 9.98548 20.531 9.98126 20.0993C9.9769 19.6676 9.97485 19.291 9.97485 18.9696L9.68285 19.0202C9.49667 19.0543 9.26181 19.0687 8.97826 19.0646C8.69484 19.0607 8.40061 19.031 8.09598 18.9757C7.79121 18.921 7.50775 18.7941 7.24536 18.5952C6.98311 18.3963 6.79693 18.1359 6.68688 17.8145L6.55993 17.5224C6.47531 17.3279 6.3421 17.1119 6.1601 16.875C5.97811 16.638 5.79406 16.4773 5.60789 16.3927L5.519 16.329C5.45978 16.2868 5.40482 16.2358 5.35399 16.1766C5.30321 16.1174 5.2652 16.0582 5.23981 15.9988C5.21437 15.9395 5.23545 15.8908 5.30326 15.8526C5.37107 15.8144 5.49361 15.7959 5.67143 15.7959L5.92524 15.8338C6.09451 15.8677 6.3039 15.9691 6.55366 16.1384C6.80329 16.3077 7.0085 16.5277 7.16933 16.7984C7.36408 17.1455 7.59873 17.4099 7.87392 17.5919C8.14889 17.7739 8.42613 17.8648 8.70537 17.8648C8.98461 17.8648 9.22579 17.8436 9.429 17.8015C9.63198 17.7592 9.82243 17.6955 10.0002 17.611C10.0764 17.0437 10.2838 16.6079 10.6222 16.3033C10.1399 16.2526 9.70619 16.1762 9.32099 16.0747C8.93601 15.9731 8.53818 15.8081 8.12777 15.5794C7.71714 15.3509 7.37649 15.0673 7.10574 14.7289C6.83495 14.3904 6.61271 13.9459 6.43934 13.3959C6.26588 12.8457 6.17913 12.211 6.17913 11.4916C6.17913 10.4674 6.51351 9.59578 7.18213 8.87633C6.86892 8.10629 6.89849 7.24304 7.27093 6.28668C7.51638 6.21043 7.88037 6.26765 8.36273 6.45801C8.84517 6.64845 9.1984 6.8116 9.42277 6.94686C9.64714 7.08208 9.82692 7.19666 9.96236 7.2896C10.7496 7.06963 11.562 6.95962 12.3998 6.95962C13.2377 6.95962 14.0503 7.06963 14.8376 7.2896L15.32 6.98505C15.6498 6.78185 16.0394 6.59563 16.4877 6.42635C16.9363 6.25716 17.2793 6.21056 17.5164 6.28682C17.8971 7.24322 17.931 8.10642 17.6177 8.87647C18.2863 9.59591 18.6208 10.4677 18.6208 11.4918C18.6208 12.2111 18.5337 12.8478 18.3605 13.4023C18.1871 13.9568 17.963 14.4008 17.688 14.7353C17.4127 15.0697 17.0699 15.3511 16.6595 15.5795C16.249 15.808 15.851 15.973 15.466 16.0747C15.0809 16.1763 14.6472 16.2527 14.1648 16.3035C14.6048 16.6842 14.8248 17.2851 14.8248 18.106V20.7845C14.8248 20.9367 14.8777 21.0637 14.9836 21.1652C15.0894 21.2665 15.2565 21.2964 15.485 21.2539C17.4487 20.6024 19.0505 19.4281 20.2903 17.7311C21.53 16.0343 22.15 14.1238 22.15 11.9993C22.1496 10.2309 21.7135 8.59976 20.8421 7.10595Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -0,0 +1,9 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.0005 20.3296L15.3166 10.1293H8.68921L12.0005 20.3296Z" fill="#E24329"/>
<path d="M4.04348 10.1293L3.03364 13.2283C2.94226 13.5097 3.04095 13.8203 3.28214 13.9957L11.9996 20.3296L4.04348 10.1293Z" fill="#FCA326"/>
<path d="M4.04248 10.1289H8.68727L6.68828 3.98572C6.58597 3.67143 6.1401 3.67143 6.03411 3.98572L4.04248 10.1289Z" fill="#E24329"/>
<path d="M19.9602 10.1293L20.9664 13.2283C21.0577 13.5097 20.9591 13.8203 20.7179 13.9957L11.9991 20.3296L19.9602 10.1293Z" fill="#FCA326"/>
<path d="M19.9616 10.1289H15.3168L17.3121 3.98572C17.4144 3.67143 17.8603 3.67143 17.9663 3.98572L19.9616 10.1289Z" fill="#E24329"/>
<path d="M11.9991 20.3296L15.3153 10.1293H19.9601L11.9991 20.3296Z" fill="#FC6D26"/>
<path d="M11.9985 20.3296L4.04248 10.1293H8.68727L11.9985 20.3296Z" fill="#FC6D26"/>
</svg>

After

Width:  |  Height:  |  Size: 905 B

View file

@ -0,0 +1,6 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M22.501 12.2333C22.501 11.37 22.4296 10.74 22.2748 10.0867H12.2153V13.9833H18.12C18.001 14.9517 17.3582 16.41 15.9296 17.3899L15.9096 17.5204L19.0902 19.9351L19.3106 19.9567C21.3343 18.125 22.501 15.43 22.501 12.2333Z" fill="#4285F4"/>
<path d="M12.2147 22.5001C15.1075 22.5001 17.5361 21.5667 19.3099 19.9567L15.929 17.39C15.0242 18.0083 13.8099 18.44 12.2147 18.44C9.38142 18.44 6.97669 16.6083 6.11947 14.0767L5.99382 14.0871L2.68656 16.5955L2.64331 16.7133C4.40519 20.1433 8.02423 22.5001 12.2147 22.5001Z" fill="#34A853"/>
<path d="M6.12022 14.0767C5.89403 13.4234 5.76313 12.7233 5.76313 12C5.76313 11.2767 5.89403 10.5767 6.10832 9.92337L6.10233 9.78423L2.75361 7.2356L2.64405 7.28667C1.91789 8.71002 1.50122 10.3084 1.50122 12C1.50122 13.6917 1.91789 15.29 2.64405 16.7133L6.12022 14.0767Z" fill="#FBBC05"/>
<path d="M12.2148 5.55997C14.2267 5.55997 15.5838 6.41163 16.3576 7.12335L19.3814 4.23C17.5243 2.53834 15.1076 1.5 12.2148 1.5C8.02426 1.5 4.4052 3.85665 2.64331 7.28662L6.10759 9.92332C6.97671 7.39166 9.38146 5.55997 12.2148 5.55997Z" fill="#EB4335"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,3 @@
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.04155 21C6.6153 21 4.35363 20.2943 2.45 19.0767C4.06624 19.1813 6.91855 18.9308 8.69268 17.2386C6.0238 17.1161 4.82019 15.0692 4.6632 14.1945C4.88997 14.2819 5.97147 14.3869 6.582 14.142C3.51192 13.3722 3.04094 10.678 3.1456 9.85573C3.72124 10.2581 4.69809 10.3981 5.08185 10.3631C2.22109 8.31618 3.25027 5.23707 3.75613 4.57226C5.80911 7.4165 8.8859 9.01393 12.6923 9.10278C12.6205 8.78802 12.5826 8.46032 12.5826 8.12373C12.5826 5.70819 14.5351 3.75 16.9435 3.75C18.2019 3.75 19.3358 4.28457 20.1318 5.13963C20.9727 4.94258 22.2382 4.4813 22.8569 4.0824C22.5451 5.20208 21.5742 6.13612 20.9869 6.48231C20.9918 6.49408 20.9821 6.47048 20.9869 6.48231C21.5028 6.40428 22.8986 6.13603 23.45 5.76192C23.1773 6.39094 22.148 7.4368 21.3033 8.02232C21.4604 14.9535 16.1574 21 9.04155 21Z" fill="#1D9BF0"/>
</svg>

After

Width:  |  Height:  |  Size: 916 B

View file

@ -0,0 +1,4 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.7701 16.617H22.3721L18.614 20.3751C18.3137 20.6754 18.3137 21.1683 18.614 21.4686C18.9143 21.769 19.3995 21.769 19.6998 21.4686L24.7747 16.3937C25.0751 16.0934 25.0751 15.6082 24.7747 15.3079L19.7075 10.2253C19.4072 9.92492 18.922 9.92492 18.6217 10.2253C18.3214 10.5256 18.3214 11.0107 18.6217 11.3111L22.3721 15.0768H13.7701C13.3465 15.0768 13 15.4234 13 15.8469C13 16.2705 13.3465 16.617 13.7701 16.617Z" fill="#86888A"/>
<rect x="7" y="10" width="1.5" height="12" rx="0.75" fill="#86888A"/>
</svg>

After

Width:  |  Height:  |  Size: 651 B

View file

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.5285 6.54089L13.0273 6.04207C14.4052 4.66426 16.6259 4.65104 17.9874 6.01253C19.349 7.37402 19.3357 9.59466 17.9579 10.9725L15.5878 13.3425C14.21 14.7203 11.9893 14.7335 10.6277 13.372M11.4717 17.4589L10.9727 17.9579C9.59481 19.3357 7.37409 19.349 6.01256 17.9875C4.65102 16.626 4.66426 14.4053 6.04211 13.0275L8.41203 10.6577C9.78988 9.27988 12.0106 9.26665 13.3721 10.6281" stroke="black" stroke-width="2" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 549 B

View file

@ -0,0 +1,3 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.1113 2.6665C11.1839 2.6665 8.00016 5.85026 8.00016 9.77762V13.3332L7.3335 13.3332C6.22893 13.3332 5.3335 14.2286 5.3335 15.3332V27.3332C5.3335 28.4377 6.22893 29.3332 7.3335 29.3332H24.6668C25.7714 29.3332 26.6668 28.4377 26.6668 27.3332V15.3332C26.6668 14.2286 25.7714 13.3332 24.6668 13.3332L24.0002 13.3332V9.77762C24.0002 5.85026 20.8164 2.6665 16.8891 2.6665H15.1113ZM20.4446 13.3332V9.77762C20.4446 7.81394 18.8527 6.22206 16.8891 6.22206H15.1113C13.1476 6.22206 11.5557 7.81394 11.5557 9.77762V13.3332H20.4446Z" fill="#8E99A4"/>
</svg>

After

Width:  |  Height:  |  Size: 692 B

View file

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.74986 3.55554C8.74986 3.14133 8.41408 2.80554 7.99986 2.80554C7.58565 2.80554 7.24986 3.14133 7.24986 3.55554V7.24999L3.55542 7.24999C3.14121 7.24999 2.80542 7.58577 2.80542 7.99999C2.80542 8.4142 3.14121 8.74999 3.55542 8.74999L7.24987 8.74999V12.4444C7.24987 12.8586 7.58565 13.1944 7.99987 13.1944C8.41408 13.1944 8.74987 12.8586 8.74987 12.4444V8.74999L12.4443 8.74999C12.8585 8.74999 13.1943 8.4142 13.1943 7.99999C13.1943 7.58577 12.8585 7.24999 12.4443 7.24999L8.74986 7.24999V3.55554Z" fill="#8E99A4"/>
</svg>

After

Width:  |  Height:  |  Size: 670 B

View file

@ -1,3 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="24" height="24" viewBox="-0.4 1 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.1001 9C18.7779 9 18.5168 8.73883 18.5168 8.41667V6.08333H16.1834C15.8613 6.08333 15.6001 5.82217 15.6001 5.5C15.6001 5.17783 15.8613 4.91667 16.1834 4.91667H18.5168V2.58333C18.5168 2.26117 18.7779 2 19.1001 2C19.4223 2 19.6834 2.26117 19.6834 2.58333V4.91667H22.0168C22.3389 4.91667 22.6001 5.17783 22.6001 5.5C22.6001 5.82217 22.3389 6.08333 22.0168 6.08333H19.6834V8.41667C19.6834 8.73883 19.4223 9 19.1001 9ZM19.6001 11C20.0669 11 20.5212 10.9467 20.9574 10.8458C21.1161 11.5383 21.2 12.2594 21.2 13C21.2 16.1409 19.6917 18.9294 17.3598 20.6808V20.6807C16.0014 21.7011 14.3635 22.3695 12.5815 22.5505C12.2588 22.5832 11.9314 22.6 11.6 22.6C6.29807 22.6 2 18.302 2 13C2 7.69809 6.29807 3.40002 11.6 3.40002C12.3407 3.40002 13.0618 3.48391 13.7543 3.64268C13.6534 4.07884 13.6001 4.53319 13.6001 5C13.6001 8.31371 16.2864 11 19.6001 11ZM11.5999 20.68C13.6754 20.68 15.5585 19.8567 16.9407 18.5189C16.0859 16.4086 14.0167 14.92 11.5998 14.92C9.18298 14.92 7.11378 16.4086 6.25901 18.5189C7.64115 19.8567 9.52436 20.68 11.5999 20.68ZM11.7426 7.41172C10.3168 7.54168 9.2 8.74043 9.2 10.2C9.2 11.7464 10.4536 13 12 13C13.0308 13 13.9315 12.443 14.4176 11.6135C13.0673 10.6058 12.0929 9.12248 11.7426 7.41172Z" fill="black"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M19.1001 9C18.7779 9 18.5168 8.73883 18.5168 8.41667V6.08333H16.1834C15.8613 6.08333 15.6001 5.82217 15.6001 5.5C15.6001 5.17783 15.8613 4.91667 16.1834 4.91667H18.5168V2.58333C18.5168 2.26117 18.7779 2 19.1001 2C19.4223 2 19.6834 2.26117 19.6834 2.58333V4.91667H22.0168C22.3389 4.91667 22.6001 5.17783 22.6001 5.5C22.6001 5.82217 22.3389 6.08333 22.0168 6.08333H19.6834V8.41667C19.6834 8.73883 19.4223 9 19.1001 9ZM19.6001 11C20.0669 11 20.5212 10.9467 20.9574 10.8458C21.1161 11.5383 21.2 12.2594 21.2 13C21.2 16.1409 19.6917 18.9294 17.3598 20.6808V20.6807C16.0014 21.7011 14.3635 22.3695 12.5815 22.5505C12.2588 22.5832 11.9314 22.6 11.6 22.6C6.29807 22.6 2 18.302 2 13C2 7.69809 6.29807 3.40002 11.6 3.40002C12.3407 3.40002 13.0618 3.48391 13.7543 3.64268C13.6534 4.07884 13.6001 4.53319 13.6001 5C13.6001 8.31371 16.2864 11 19.6001 11ZM11.5999 20.68C13.6754 20.68 15.5585 19.8567 16.9407 18.5189C16.0859 16.4086 14.0167 14.92 11.5998 14.92C9.18298 14.92 7.11378 16.4086 6.25901 18.5189C7.64115 19.8567 9.52436 20.68 11.5999 20.68ZM11.7426 7.41172C10.3168 7.54168 9.2 8.74043 9.2 10.2C9.2 11.7464 10.4536 13 12 13C13.0308 13 13.9315 12.443 14.4176 11.6135C13.0673 10.6058 12.0929 9.12248 11.7426 7.41172Z" fill="black"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.4 KiB

View file

@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M17 9C17 13.4183 13.4183 17 9 17C4.58172 17 1 13.4183 1 9C1 4.58172 4.58172 1 9 1C13.4183 1 17 4.58172 17 9ZM5.25 9C5.25 8.58579 5.58579 8.25 6 8.25H8.25V6C8.25 5.58579 8.58579 5.25 9 5.25C9.41421 5.25 9.75 5.58579 9.75 6V8.25H12C12.4142 8.25 12.75 8.58579 12.75 9C12.75 9.41421 12.4142 9.75 12 9.75H9.75V12C9.75 12.4142 9.41421 12.75 9 12.75C8.58579 12.75 8.25 12.4142 8.25 12V9.75H6C5.58579 9.75 5.25 9.41421 5.25 9Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 587 B

View file

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M 18.792 11.145 L 2.356 19.359 C 1.249 19.913 0.097 18.725 0.638 17.642 C 0.638 17.642 2.675 13.528 3.235 12.451 C 3.796 11.373 4.437 11.187 10.393 10.417 C 10.614 10.388 10.794 10.222 10.794 10 C 10.794 9.778 10.614 9.612 10.393 9.583 C 4.437 8.813 3.796 8.627 3.235 7.549 C 2.675 6.472 0.638 2.358 0.638 2.358 C 0.097 1.275 1.249 0.087 2.356 0.64 L 18.792 8.855 C 19.736 9.326 19.736 10.674 18.792 11.145 Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 537 B

View file

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -1,19 +1,3 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg width="45px" height="59px" viewBox="-1 -1 45 59" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"> <path fill-rule="evenodd" clip-rule="evenodd" d="M16 0C7.16344 0 0 7.16344 0 16C0 24.8366 7.16344 32 16 32C24.8366 32 32 24.8366 32 16C32 7.16344 24.8366 0 16 0ZM17.2511 6.97409C16.9775 6.68236 16.5885 6.50012 16.157 6.50012C15.793 6.50012 15.4593 6.62973 15.1996 6.84532C15.1545 6.88267 15.1115 6.92281 15.0707 6.96564L8.79618 13.5539C8.22485 14.1538 8.24801 15.1032 8.8479 15.6746C9.4478 16.2459 10.3973 16.2227 10.9686 15.6228L14.657 11.7501V23.0589C14.657 23.8874 15.3285 24.5589 16.157 24.5589C16.9854 24.5589 17.657 23.8874 17.657 23.0589V11.7502L21.3452 15.6228C21.9165 16.2227 22.866 16.2459 23.4659 15.6746C24.0658 15.1032 24.0889 14.1538 23.5176 13.5539L17.2511 6.97409Z" fill="#0DBD8B"/>
<!-- Generator: bin/sketchtool 1.4 (305) - http://www.bohemiancoding.com/sketch -->
<title>icons_upload_drop</title>
<desc>Created with bin/sketchtool.</desc>
<defs></defs>
<g id="03-Input" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g id="03_05-File-drop" sketch:type="MSArtboardGroup" transform="translate(-570.000000, -368.000000)">
<g id="icons_upload_drop" sketch:type="MSLayerGroup" transform="translate(570.000000, 368.000000)">
<g id="Rectangle-5-+-Rectangle-6" sketch:type="MSShapeGroup">
<path d="M0,4.00812931 C0,1.79450062 1.78537926,0 4.00241155,0 L24.8253683,0 C24.8253683,0 42.2466793,16.8210687 42.2466793,16.8210687 L42.2466793,53.000599 C42.2466793,55.2094072 40.4583762,57 38.2531894,57 L3.99348992,57 C1.78794634,57 0,55.1999609 0,52.9918707 L0,4.00812931 Z" id="Rectangle-5" stroke="#76CFA6"></path>
<path d="M40.5848017,19.419576 L29.8354335,19.419576 C26.7387692,19.419576 24.2284269,16.9063989 24.2284269,13.8067771 L24.2284269,4.88501382 L40.5848017,19.419576 Z" id="Rectangle-6-Copy" fill="#FFFFFF"></path>
<path d="M42.2466793,18.3870968 L29.539478,18.3870968 C26.4130381,18.3870968 23.8785579,15.8497544 23.8785579,12.7203286 L23.8785579,0" id="Rectangle-6" stroke="#76CFA6"></path>
</g>
<path d="M31.3419737,32.9284726 C31.701384,32.9284726 32.0607942,32.8000473 32.3359677,32.5414375 C32.8825707,32.0259772 32.8825707,31.1920926 32.3359677,30.6766323 L21.622922,20.6119619 C21.076319,20.0965016 20.187153,20.0982608 19.638678,20.6102026 L8.9125289,30.6607991 C8.36405391,31.1762594 8.36218198,32.0119032 8.90878504,32.5273635 C9.4553881,33.0445831 10.344554,33.0445831 10.893029,32.530882 L19.2399573,24.7092556 L19.2437012,46.487014 C19.2437012,47.2153435 19.874541,47.8064516 20.6476474,47.8064516 C21.4244976,47.8064516 22.0515936,47.2153435 22.0515936,46.487014 L22.0478497,24.7426814 L30.3498517,32.5414375 C30.6231533,32.8000473 30.9825635,32.9284726 31.3419737,32.9284726 L31.3419737,32.9284726 Z" id="Fill-75" fill="#76CFA6" sketch:type="MSShapeGroup"></path>
</g>
</g>
</g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 802 B

View file

@ -123,6 +123,7 @@ $roomsublist-divider-color: $primary-fg-color;
$roomsublist-skeleton-ui-bg: linear-gradient(180deg, #3e444c 0%, #3e444c00 100%); $roomsublist-skeleton-ui-bg: linear-gradient(180deg, #3e444c 0%, #3e444c00 100%);
$groupFilterPanel-divider-color: $roomlist-header-color; $groupFilterPanel-divider-color: $roomlist-header-color;
$space-button-outline-color: rgba(141, 151, 165, 0.2);
$roomtile-preview-color: $secondary-fg-color; $roomtile-preview-color: $secondary-fg-color;
$roomtile-default-badge-bg-color: #61708b; $roomtile-default-badge-bg-color: #61708b;
@ -258,6 +259,12 @@ $composer-shadow-color: rgba(0, 0, 0, 0.28);
// markdown overrides: // markdown overrides:
.mx_EventTile_content .markdown-body pre:hover { .mx_EventTile_content .markdown-body pre:hover {
border-color: #808080 !important; // inverted due to rules below border-color: #808080 !important; // inverted due to rules below
scrollbar-color: rgba(0, 0, 0, 0.2) transparent; // copied from light theme due to inversion below
// the code above works only in Firefox, this is for other browsers
// see https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-color
&::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.2); // copied from light theme due to inversion below
}
} }
.mx_EventTile_content .markdown-body { .mx_EventTile_content .markdown-body {
pre, code { pre, code {

View file

@ -120,6 +120,7 @@ $roomsublist-divider-color: $primary-fg-color;
$roomsublist-skeleton-ui-bg: linear-gradient(180deg, #3e444c 0%, #3e444c00 100%); $roomsublist-skeleton-ui-bg: linear-gradient(180deg, #3e444c 0%, #3e444c00 100%);
$groupFilterPanel-divider-color: $roomlist-header-color; $groupFilterPanel-divider-color: $roomlist-header-color;
$space-button-outline-color: rgba(141, 151, 165, 0.2);
$roomtile-preview-color: #9e9e9e; $roomtile-preview-color: #9e9e9e;
$roomtile-default-badge-bg-color: #61708b; $roomtile-default-badge-bg-color: #61708b;

View file

@ -187,6 +187,7 @@ $roomsublist-divider-color: $primary-fg-color;
$roomsublist-skeleton-ui-bg: linear-gradient(180deg, #ffffff 0%, #ffffff00 100%); $roomsublist-skeleton-ui-bg: linear-gradient(180deg, #ffffff 0%, #ffffff00 100%);
$groupFilterPanel-divider-color: $roomlist-header-color; $groupFilterPanel-divider-color: $roomlist-header-color;
$space-button-outline-color: #E3E8F0;
$roomtile-preview-color: #9e9e9e; $roomtile-preview-color: #9e9e9e;
$roomtile-default-badge-bg-color: #61708b; $roomtile-default-badge-bg-color: #61708b;
@ -237,7 +238,8 @@ $event-redacted-border-color: #cccccc;
$event-timestamp-color: #acacac; $event-timestamp-color: #acacac;
$copy-button-url: "$(res)/img/feather-customised/clipboard.svg"; $copy-button-url: "$(res)/img/feather-customised/clipboard.svg";
$collapse-button-url: "$(res)/img/feather-customised/minimise.svg";
$expand-button-url: "$(res)/img/feather-customised/maximise.svg";
// e2e // e2e
$e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color $e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color

View file

@ -67,9 +67,6 @@ $groupFilterPanel-bg-color: rgba(232, 232, 232, 0.77);
// used by RoomDirectory permissions // used by RoomDirectory permissions
$plinth-bg-color: $secondary-accent-color; $plinth-bg-color: $secondary-accent-color;
// used by RoomDropTarget
$droptarget-bg-color: rgba(255,255,255,0.5);
// used by AddressSelector // used by AddressSelector
$selected-color: $secondary-accent-color; $selected-color: $secondary-accent-color;
@ -181,6 +178,7 @@ $roomsublist-divider-color: $primary-fg-color;
$roomsublist-skeleton-ui-bg: linear-gradient(180deg, #ffffff 0%, #ffffff00 100%); $roomsublist-skeleton-ui-bg: linear-gradient(180deg, #ffffff 0%, #ffffff00 100%);
$groupFilterPanel-divider-color: $roomlist-header-color; $groupFilterPanel-divider-color: $roomlist-header-color;
$space-button-outline-color: #E3E8F0;
$roomtile-preview-color: $secondary-fg-color; $roomtile-preview-color: $secondary-fg-color;
$roomtile-default-badge-bg-color: #61708b; $roomtile-default-badge-bg-color: #61708b;
@ -237,6 +235,8 @@ $event-redacted-border-color: #cccccc;
$event-timestamp-color: #acacac; $event-timestamp-color: #acacac;
$copy-button-url: "$(res)/img/feather-customised/clipboard.svg"; $copy-button-url: "$(res)/img/feather-customised/clipboard.svg";
$collapse-button-url: "$(res)/img/feather-customised/minimise.svg";
$expand-button-url: "$(res)/img/feather-customised/maximise.svg";
// e2e // e2e
$e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color $e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color

View file

@ -16,6 +16,10 @@
backdrop-filter: blur($groupFilterPanel-background-blur-amount); backdrop-filter: blur($groupFilterPanel-background-blur-amount);
} }
.mx_SpacePanel {
backdrop-filter: blur($groupFilterPanel-background-blur-amount);
}
.mx_LeftPanel .mx_LeftPanel_roomListContainer { .mx_LeftPanel .mx_LeftPanel_roomListContainer {
backdrop-filter: blur($roomlist-background-blur-amount); backdrop-filter: blur($roomlist-background-blur-amount);
} }

View file

@ -37,6 +37,8 @@ import CountlyAnalytics from "../CountlyAnalytics";
import UserActivity from "../UserActivity"; import UserActivity from "../UserActivity";
import {ModalWidgetStore} from "../stores/ModalWidgetStore"; import {ModalWidgetStore} from "../stores/ModalWidgetStore";
import { WidgetLayoutStore } from "../stores/widgets/WidgetLayoutStore"; import { WidgetLayoutStore } from "../stores/widgets/WidgetLayoutStore";
import VoipUserMapper from "../VoipUserMapper";
import {SpaceStoreClass} from "../stores/SpaceStore";
declare global { declare global {
interface Window { interface Window {
@ -66,6 +68,8 @@ declare global {
mxCountlyAnalytics: typeof CountlyAnalytics; mxCountlyAnalytics: typeof CountlyAnalytics;
mxUserActivity: UserActivity; mxUserActivity: UserActivity;
mxModalWidgetStore: ModalWidgetStore; mxModalWidgetStore: ModalWidgetStore;
mxVoipUserMapper: VoipUserMapper;
mxSpaceStore: SpaceStoreClass;
} }
interface Document { interface Document {

View file

@ -165,6 +165,9 @@ export function avatarUrlForRoom(room: Room, width: number, height: number, resi
return explicitRoomAvatar; return explicitRoomAvatar;
} }
// space rooms cannot be DMs so skip the rest
if (room.isSpaceRoom()) return null;
let otherMember = null; let otherMember = null;
const otherUserId = DMRoomMap.shared().getUserIdForRoomId(room.roomId); const otherUserId = DMRoomMap.shared().getUserIdForRoomId(room.roomId);
if (otherUserId) { if (otherUserId) {

View file

@ -30,6 +30,7 @@ import {idbLoad, idbSave, idbDelete} from "./utils/StorageManager";
export const SSO_HOMESERVER_URL_KEY = "mx_sso_hs_url"; export const SSO_HOMESERVER_URL_KEY = "mx_sso_hs_url";
export const SSO_ID_SERVER_URL_KEY = "mx_sso_is_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 { export enum UpdateCheckStatus {
Checking = "CHECKING", Checking = "CHECKING",
@ -56,7 +57,7 @@ export default abstract class BasePlatform {
this.startUpdateCheck = this.startUpdateCheck.bind(this); this.startUpdateCheck = this.startUpdateCheck.bind(this);
} }
abstract async getConfig(): Promise<{}>; abstract getConfig(): Promise<{}>;
abstract getDefaultDeviceDisplayName(): string; abstract getDefaultDeviceDisplayName(): string;
@ -130,6 +131,14 @@ export default abstract class BasePlatform {
hideUpdateToast(); hideUpdateToast();
} }
/**
* Return true if platform supports multi-language
* spell-checking, otherwise false.
*/
supportsMultiLanguageSpellCheck(): boolean {
return false;
}
/** /**
* Returns true if the platform supports displaying * Returns true if the platform supports displaying
* notifications, otherwise false. * notifications, otherwise false.
@ -239,6 +248,16 @@ export default abstract class BasePlatform {
setLanguage(preferredLangs: string[]) {} setLanguage(preferredLangs: string[]) {}
setSpellCheckLanguages(preferredLangs: string[]) {}
getSpellCheckLanguages(): Promise<string[]> | null {
return null;
}
getAvailableSpellCheckLanguages(): Promise<string[]> | null {
return null;
}
protected getSSOCallbackUrl(fragmentAfterLogin: string): URL { protected getSSOCallbackUrl(fragmentAfterLogin: string): URL {
const url = new URL(window.location.href); const url = new URL(window.location.href);
url.hash = fragmentAfterLogin || ""; url.hash = fragmentAfterLogin || "";
@ -258,6 +277,9 @@ export default abstract class BasePlatform {
if (mxClient.getIdentityServerUrl()) { if (mxClient.getIdentityServerUrl()) {
localStorage.setItem(SSO_ID_SERVER_URL_KEY, 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); const callbackUrl = this.getSSOCallbackUrl(fragmentAfterLogin);
window.location.href = mxClient.getSsoLoginUrl(callbackUrl.toString(), loginType, idpId); // redirect to SSO window.location.href = mxClient.getSsoLoginUrl(callbackUrl.toString(), loginType, idpId); // redirect to SSO
} }

View file

@ -64,7 +64,6 @@ import dis from './dispatcher/dispatcher';
import WidgetUtils from './utils/WidgetUtils'; import WidgetUtils from './utils/WidgetUtils';
import WidgetEchoStore from './stores/WidgetEchoStore'; import WidgetEchoStore from './stores/WidgetEchoStore';
import SettingsStore from './settings/SettingsStore'; import SettingsStore from './settings/SettingsStore';
import {generateHumanReadableId} from "./utils/NamingUtils";
import {Jitsi} from "./widgets/Jitsi"; import {Jitsi} from "./widgets/Jitsi";
import {WidgetType} from "./widgets/WidgetType"; import {WidgetType} from "./widgets/WidgetType";
import {SettingLevel} from "./settings/SettingLevel"; import {SettingLevel} from "./settings/SettingLevel";
@ -82,10 +81,21 @@ import CountlyAnalytics from "./CountlyAnalytics";
import {UIFeature} from "./settings/UIFeature"; import {UIFeature} from "./settings/UIFeature";
import { CallError } from "matrix-js-sdk/src/webrtc/call"; import { CallError } from "matrix-js-sdk/src/webrtc/call";
import { logger } from 'matrix-js-sdk/src/logger'; import { logger } from 'matrix-js-sdk/src/logger';
import DesktopCapturerSourcePicker from "./components/views/elements/DesktopCapturerSourcePicker"
import { Action } from './dispatcher/actions'; import { Action } from './dispatcher/actions';
import { roomForVirtualRoom, getOrCreateVirtualRoomForRoom } from './VoipUserMapper'; import VoipUserMapper from './VoipUserMapper';
import { addManagedHybridWidget, isManagedHybridWidgetEnabled } from './widgets/ManagedHybrid';
import { randomUppercaseString, randomLowercaseString } from "matrix-js-sdk/src/randomstring";
const CHECK_PSTN_SUPPORT_ATTEMPTS = 3; export const PROTOCOL_PSTN = 'm.protocol.pstn';
export const PROTOCOL_PSTN_PREFIXED = 'im.vector.protocol.pstn';
export const PROTOCOL_SIP_NATIVE = 'im.vector.protocol.sip_native';
export const PROTOCOL_SIP_VIRTUAL = 'im.vector.protocol.sip_virtual';
const CHECK_PROTOCOLS_ATTEMPTS = 3;
// Event type for room account data and room creation content used to mark rooms as virtual rooms
// (and store the ID of their native room)
export const VIRTUAL_ROOM_EVENT_TYPE = 'im.vector.is_virtual_room';
enum AudioID { enum AudioID {
Ring = 'ringAudio', Ring = 'ringAudio',
@ -94,6 +104,29 @@ enum AudioID {
Busy = 'busyAudio', Busy = 'busyAudio',
} }
interface ThirdpartyLookupResponseFields {
/* eslint-disable camelcase */
// im.vector.sip_native
virtual_mxid?: string;
is_virtual?: boolean;
// im.vector.sip_virtual
native_mxid?: string;
is_native?: boolean;
// common
lookup_success?: boolean;
/* eslint-enable camelcase */
}
interface ThirdpartyLookupResponse {
userid: string,
protocol: string,
fields: ThirdpartyLookupResponseFields,
}
// Unlike 'CallType' in js-sdk, this one includes screen sharing // Unlike 'CallType' in js-sdk, this one includes screen sharing
// (because a screen sharing call is only a screen sharing call to the caller, // (because a screen sharing call is only a screen sharing call to the caller,
// to the callee it's just a video call, at least as far as the current impl // to the callee it's just a video call, at least as far as the current impl
@ -124,7 +157,12 @@ export default class CallHandler {
private audioPromises = new Map<AudioID, Promise<void>>(); private audioPromises = new Map<AudioID, Promise<void>>();
private dispatcherRef: string = null; private dispatcherRef: string = null;
private supportsPstnProtocol = null; private supportsPstnProtocol = null;
private pstnSupportPrefixed = null; // True if the server only support the prefixed pstn protocol
private supportsSipNativeVirtual = null; // im.vector.protocol.sip_virtual and im.vector.protocol.sip_native
private pstnSupportCheckTimer: NodeJS.Timeout; // number actually because we're in the browser private pstnSupportCheckTimer: NodeJS.Timeout; // number actually because we're in the browser
// For rooms we've been invited to, true if they're from virtual user, false if we've checked and they aren't.
private invitedRoomsAreVirtual = new Map<string, boolean>();
private invitedRoomCheckInProgress = false;
static sharedInstance() { static sharedInstance() {
if (!window.mxCallHandler) { if (!window.mxCallHandler) {
@ -138,9 +176,9 @@ export default class CallHandler {
* Gets the user-facing room associated with a call (call.roomId may be the call "virtual room" * 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) * if a voip_mxid_translate_pattern is set in the config)
*/ */
public static roomIdForCall(call: MatrixCall) { public static roomIdForCall(call: MatrixCall): string {
if (!call) return null; if (!call) return null;
return roomForVirtualRoom(call.roomId) || call.roomId; return VoipUserMapper.sharedInstance().nativeRoomForVirtualRoom(call.roomId) || call.roomId;
} }
start() { start() {
@ -161,7 +199,7 @@ export default class CallHandler {
MatrixClientPeg.get().on('Call.incoming', this.onCallIncoming); MatrixClientPeg.get().on('Call.incoming', this.onCallIncoming);
} }
this.checkForPstnSupport(CHECK_PSTN_SUPPORT_ATTEMPTS); this.checkProtocols(CHECK_PROTOCOLS_ATTEMPTS);
} }
stop() { stop() {
@ -175,33 +213,73 @@ export default class CallHandler {
} }
} }
private async checkForPstnSupport(maxTries) { private async checkProtocols(maxTries) {
try { try {
const protocols = await MatrixClientPeg.get().getThirdpartyProtocols(); const protocols = await MatrixClientPeg.get().getThirdpartyProtocols();
if (protocols['im.vector.protocol.pstn'] !== undefined) {
this.supportsPstnProtocol = protocols['im.vector.protocol.pstn']; if (protocols[PROTOCOL_PSTN] !== undefined) {
} else if (protocols['m.protocol.pstn'] !== undefined) { this.supportsPstnProtocol = Boolean(protocols[PROTOCOL_PSTN]);
this.supportsPstnProtocol = protocols['m.protocol.pstn']; if (this.supportsPstnProtocol) this.pstnSupportPrefixed = false;
} else if (protocols[PROTOCOL_PSTN_PREFIXED] !== undefined) {
this.supportsPstnProtocol = Boolean(protocols[PROTOCOL_PSTN_PREFIXED]);
if (this.supportsPstnProtocol) this.pstnSupportPrefixed = true;
} else { } else {
this.supportsPstnProtocol = null; this.supportsPstnProtocol = null;
} }
dis.dispatch({action: Action.PstnSupportUpdated}); dis.dispatch({action: Action.PstnSupportUpdated});
if (protocols[PROTOCOL_SIP_NATIVE] !== undefined && protocols[PROTOCOL_SIP_VIRTUAL] !== undefined) {
this.supportsSipNativeVirtual = Boolean(
protocols[PROTOCOL_SIP_NATIVE] && protocols[PROTOCOL_SIP_VIRTUAL],
);
}
dis.dispatch({action: Action.VirtualRoomSupportUpdated});
} catch (e) { } catch (e) {
if (maxTries === 1) { if (maxTries === 1) {
console.log("Failed to check for pstn protocol support and no retries remain: assuming no support", e); console.log("Failed to check for protocol support and no retries remain: assuming no support", e);
} else { } else {
console.log("Failed to check for pstn protocol support: will retry", e); console.log("Failed to check for protocol support: will retry", e);
this.pstnSupportCheckTimer = setTimeout(() => { this.pstnSupportCheckTimer = setTimeout(() => {
this.checkForPstnSupport(maxTries - 1); this.checkProtocols(maxTries - 1);
}, 10000); }, 10000);
} }
} }
} }
getSupportsPstnProtocol() { public getSupportsPstnProtocol() {
return this.supportsPstnProtocol; return this.supportsPstnProtocol;
} }
public getSupportsVirtualRooms() {
return this.supportsPstnProtocol;
}
public pstnLookup(phoneNumber: string): Promise<ThirdpartyLookupResponse[]> {
return MatrixClientPeg.get().getThirdpartyUser(
this.pstnSupportPrefixed ? PROTOCOL_PSTN_PREFIXED : PROTOCOL_PSTN, {
'm.id.phone': phoneNumber,
},
);
}
public sipVirtualLookup(nativeMxid: string): Promise<ThirdpartyLookupResponse[]> {
return MatrixClientPeg.get().getThirdpartyUser(
PROTOCOL_SIP_VIRTUAL, {
'native_mxid': nativeMxid,
},
);
}
public sipNativeLookup(virtualMxid: string): Promise<ThirdpartyLookupResponse[]> {
return MatrixClientPeg.get().getThirdpartyUser(
PROTOCOL_SIP_NATIVE, {
'virtual_mxid': virtualMxid,
},
);
}
private onCallIncoming = (call) => { private onCallIncoming = (call) => {
// we dispatch this synchronously to make sure that the event // we dispatch this synchronously to make sure that the event
// handlers on the call are set up immediately (so that if // handlers on the call are set up immediately (so that if
@ -356,6 +434,7 @@ export default class CallHandler {
this.play(AudioID.Ringback); this.play(AudioID.Ringback);
break; break;
case CallState.Ended: case CallState.Ended:
{
Analytics.trackEvent('voip', 'callEnded', 'hangupReason', call.hangupReason); Analytics.trackEvent('voip', 'callEnded', 'hangupReason', call.hangupReason);
this.removeCallForRoom(mappedRoomId); this.removeCallForRoom(mappedRoomId);
if (oldState === CallState.InviteSent && ( if (oldState === CallState.InviteSent && (
@ -393,6 +472,10 @@ export default class CallHandler {
// don't play the end-call sound for calls that never got off the ground // don't play the end-call sound for calls that never got off the ground
this.play(AudioID.CallEnd); this.play(AudioID.CallEnd);
} }
this.logCallStats(call, mappedRoomId);
break;
}
} }
}); });
call.on(CallEvent.Replaced, (newCall: MatrixCall) => { call.on(CallEvent.Replaced, (newCall: MatrixCall) => {
@ -412,6 +495,49 @@ export default class CallHandler {
}); });
} }
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}`,
);
if (!stats) {
logger.debug(
"Call statistics are undefined. The call has " +
"probably failed before a peerConn was established",
);
return;
}
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) { private setCallAudioElement(call: MatrixCall) {
const audioElement = getRemoteAudioElement(); const audioElement = getRemoteAudioElement();
if (audioElement) call.setRemoteAudioElement(audioElement); if (audioElement) call.setRemoteAudioElement(audioElement);
@ -500,9 +626,11 @@ export default class CallHandler {
Analytics.trackEvent('voip', 'placeCall', 'type', type); Analytics.trackEvent('voip', 'placeCall', 'type', type);
CountlyAnalytics.instance.trackStartCall(roomId, type === PlaceCallType.Video, false); CountlyAnalytics.instance.trackStartCall(roomId, type === PlaceCallType.Video, false);
const mappedRoomId = (await getOrCreateVirtualRoomForRoom(roomId)) || roomId; const mappedRoomId = (await VoipUserMapper.sharedInstance().getOrCreateVirtualRoomForRoom(roomId)) || roomId;
logger.debug("Mapped real room " + roomId + " to room ID " + mappedRoomId); logger.debug("Mapped real room " + roomId + " to room ID " + mappedRoomId);
const timeUntilTurnCresExpire = MatrixClientPeg.get().getTurnServersExpiry() - Date.now();
console.log("Current turn creds expire in " + timeUntilTurnCresExpire + " seconds");
const call = createNewMatrixCall(MatrixClientPeg.get(), mappedRoomId); const call = createNewMatrixCall(MatrixClientPeg.get(), mappedRoomId);
this.calls.set(roomId, call); this.calls.set(roomId, call);
@ -530,9 +658,17 @@ export default class CallHandler {
}); });
return; return;
} }
call.placeScreenSharingCall(remoteElement, localElement);
call.placeScreenSharingCall(
remoteElement,
localElement,
async () : Promise<DesktopCapturerSource> => {
const {finished} = Modal.createDialog(DesktopCapturerSourcePicker);
const [source] = await finished;
return source;
});
} else { } else {
console.error("Unknown conf call type: %s", type); console.error("Unknown conf call type: " + type);
} }
} }
@ -540,6 +676,12 @@ export default class CallHandler {
switch (payload.action) { switch (payload.action) {
case 'place_call': 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 the runtime env doesn't do VoIP, whine.
if (!MatrixClientPeg.get().supportsVoip()) { if (!MatrixClientPeg.get().supportsVoip()) {
Modal.createTrackedDialog('Call Handler', 'VoIP is unsupported', ErrorDialog, { Modal.createTrackedDialog('Call Handler', 'VoIP is unsupported', ErrorDialog, {
@ -560,7 +702,15 @@ export default class CallHandler {
const room = MatrixClientPeg.get().getRoom(payload.room_id); const room = MatrixClientPeg.get().getRoom(payload.room_id);
if (!room) { if (!room) {
console.error("Room %s does not exist.", payload.room_id); console.error(`Room ${payload.room_id} does not exist.`);
return;
}
if (this.getCallForRoom(room.roomId)) {
Modal.createTrackedDialog('Call Handler', 'Existing Call with user', ErrorDialog, {
title: _t('Already in call'),
description: _t("You're already in a call with this person."),
});
return; return;
} }
@ -571,7 +721,7 @@ export default class CallHandler {
}); });
return; return;
} else if (members.length === 2) { } else if (members.length === 2) {
console.info("Place %s call in %s", payload.type, payload.room_id); console.info(`Place ${payload.type} call in ${payload.room_id}`);
this.placeCall(payload.room_id, payload.type, payload.local_element, payload.remote_element); this.placeCall(payload.room_id, payload.type, payload.local_element, payload.remote_element);
} else { // > 2 } else { // > 2
@ -586,17 +736,17 @@ export default class CallHandler {
} }
break; break;
case 'place_conference_call': case 'place_conference_call':
console.info("Place conference call in %s", payload.room_id); console.info("Place conference call in " + payload.room_id);
Analytics.trackEvent('voip', 'placeConferenceCall'); Analytics.trackEvent('voip', 'placeConferenceCall');
CountlyAnalytics.instance.trackStartCall(payload.room_id, payload.type === PlaceCallType.Video, true); CountlyAnalytics.instance.trackStartCall(payload.room_id, payload.type === PlaceCallType.Video, true);
this.startCallApp(payload.room_id, payload.type); this.startCallApp(payload.room_id, payload.type);
break; break;
case 'end_conference': case 'end_conference':
console.info("Terminating conference call in %s", payload.room_id); console.info("Terminating conference call in " + payload.room_id);
this.terminateCallApp(payload.room_id); this.terminateCallApp(payload.room_id);
break; break;
case 'hangup_conference': case 'hangup_conference':
console.info("Leaving conference call in %s", payload.room_id); console.info("Leaving conference call in "+ payload.room_id);
this.hangupCallApp(payload.room_id); this.hangupCallApp(payload.room_id);
break; break;
case 'incoming_call': case 'incoming_call':
@ -617,6 +767,12 @@ export default class CallHandler {
Analytics.trackEvent('voip', 'receiveCall', 'type', call.type); Analytics.trackEvent('voip', 'receiveCall', 'type', call.type);
this.calls.set(mappedRoomId, call) this.calls.set(mappedRoomId, call)
this.setCallListeners(call); this.setCallListeners(call);
// get ready to send encrypted events in the room, so if the user does answer
// the call, we'll be ready to send. NB. This is the protocol-level room ID not
// the mapped one: that's where we'll send the events.
const cli = MatrixClientPeg.get();
cli.prepareToEncrypt(cli.getRoom(call.roomId));
} }
break; break;
case 'hangup': case 'hangup':
@ -715,8 +871,9 @@ export default class CallHandler {
// https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification // https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification
confId = base32.stringify(Buffer.from(roomId), { pad: false }); confId = base32.stringify(Buffer.from(roomId), { pad: false });
} else { } else {
// Create a random human readable conference ID // Create a random conference ID
confId = `JitsiConference${generateHumanReadableId()}`; const random = randomUppercaseString(1) + randomLowercaseString(23);
confId = 'Jitsi' + random;
} }
let widgetUrl = WidgetUtils.getLocalJitsiWrapperUrl({auth: jitsiAuth}); let widgetUrl = WidgetUtils.getLocalJitsiWrapperUrl({auth: jitsiAuth});
@ -732,6 +889,7 @@ export default class CallHandler {
isAudioOnly: type === 'voice', isAudioOnly: type === 'voice',
domain: jitsiDomain, domain: jitsiDomain,
auth: jitsiAuth, auth: jitsiAuth,
roomName: room.name,
}; };
const widgetId = ( const widgetId = (

View file

@ -497,7 +497,7 @@ export default class ContentMessages {
content.info.mimetype = file.type; content.info.mimetype = file.type;
} }
const prom = new Promise((resolve) => { const prom = new Promise<void>((resolve) => {
if (file.type.indexOf('image/') === 0) { if (file.type.indexOf('image/') === 0) {
content.msgtype = 'm.image'; content.msgtype = 'm.image';
infoForImageFile(matrixClient, roomId, file).then((imageInfo) => { infoForImageFile(matrixClient, roomId, file).then((imageInfo) => {

View file

@ -840,7 +840,7 @@ export default class CountlyAnalytics {
let endTime = CountlyAnalytics.getTimestamp(); let endTime = CountlyAnalytics.getTimestamp();
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
if (!cli.getRoom(roomId)) { if (!cli.getRoom(roomId)) {
await new Promise(resolve => { await new Promise<void>(resolve => {
const handler = (room) => { const handler = (room) => {
if (room.roomId === roomId) { if (room.roomId === roomId) {
cli.off("Room", handler); cli.off("Room", handler);
@ -880,7 +880,7 @@ export default class CountlyAnalytics {
let endTime = CountlyAnalytics.getTimestamp(); let endTime = CountlyAnalytics.getTimestamp();
if (!room.findEventById(eventId)) { if (!room.findEventById(eventId)) {
await new Promise(resolve => { await new Promise<void>(resolve => {
const handler = (ev) => { const handler = (ev) => {
if (ev.getId() === eventId) { if (ev.getId() === eventId) {
room.off("Room.localEchoUpdated", handler); room.off("Room.localEchoUpdated", handler);

View file

@ -422,6 +422,8 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts
if (SettingsStore.getValue("feature_latex_maths")) { if (SettingsStore.getValue("feature_latex_maths")) {
const phtml = cheerio.load(safeBody, const phtml = cheerio.load(safeBody,
{ _useHtmlParser2: true, decodeEntities: false }) { _useHtmlParser2: true, decodeEntities: false })
// @ts-ignore - The types for `replaceWith` wrongly expect
// Cheerio instance to be returned.
phtml('div, span[data-mx-maths!=""]').replaceWith(function(i, e) { phtml('div, span[data-mx-maths!=""]').replaceWith(function(i, e) {
return katex.renderToString( return katex.renderToString(
AllHtmlEntities.decode(phtml(e).attr('data-mx-maths')), AllHtmlEntities.decode(phtml(e).attr('data-mx-maths')),

View file

@ -165,6 +165,7 @@ export default class IdentityAuthClient {
}); });
const [confirmed] = await finished; const [confirmed] = await finished;
if (confirmed) { if (confirmed) {
// eslint-disable-next-line react-hooks/rules-of-hooks
useDefaultIdentityServer(); useDefaultIdentityServer();
} else { } else {
throw new AbortedIdentityActionError( throw new AbortedIdentityActionError(

View file

@ -46,11 +46,13 @@ import {IntegrationManagers} from "./integrations/IntegrationManagers";
import {Mjolnir} from "./mjolnir/Mjolnir"; import {Mjolnir} from "./mjolnir/Mjolnir";
import DeviceListener from "./DeviceListener"; import DeviceListener from "./DeviceListener";
import {Jitsi} from "./widgets/Jitsi"; import {Jitsi} from "./widgets/Jitsi";
import {SSO_HOMESERVER_URL_KEY, SSO_ID_SERVER_URL_KEY} from "./BasePlatform"; import {SSO_HOMESERVER_URL_KEY, SSO_ID_SERVER_URL_KEY, SSO_IDP_ID_KEY} from "./BasePlatform";
import ThreepidInviteStore from "./stores/ThreepidInviteStore"; import ThreepidInviteStore from "./stores/ThreepidInviteStore";
import CountlyAnalytics from "./CountlyAnalytics"; import CountlyAnalytics from "./CountlyAnalytics";
import CallHandler from './CallHandler'; import CallHandler from './CallHandler';
import LifecycleCustomisations from "./customisations/Lifecycle"; import LifecycleCustomisations from "./customisations/Lifecycle";
import ErrorDialog from "./components/views/dialogs/ErrorDialog";
import {_t} from "./languageHandler";
const HOMESERVER_URL_KEY = "mx_hs_url"; const HOMESERVER_URL_KEY = "mx_hs_url";
const ID_SERVER_URL_KEY = "mx_is_url"; const ID_SERVER_URL_KEY = "mx_is_url";
@ -162,7 +164,8 @@ export async function getStoredSessionOwner(): Promise<[string, boolean]> {
* query-parameters extracted from the real query-string of the starting * query-parameters extracted from the real query-string of the starting
* URI. * URI.
* *
* @param {String} defaultDeviceDisplayName * @param {string} defaultDeviceDisplayName
* @param {string} fragmentAfterLogin path to go to after a successful login, only used for "Try again"
* *
* @returns {Promise} promise which resolves to true if we completed the token * @returns {Promise} promise which resolves to true if we completed the token
* login, else false * login, else false
@ -170,6 +173,7 @@ export async function getStoredSessionOwner(): Promise<[string, boolean]> {
export function attemptTokenLogin( export function attemptTokenLogin(
queryParams: Record<string, string>, queryParams: Record<string, string>,
defaultDeviceDisplayName?: string, defaultDeviceDisplayName?: string,
fragmentAfterLogin?: string,
): Promise<boolean> { ): Promise<boolean> {
if (!queryParams.loginToken) { if (!queryParams.loginToken) {
return Promise.resolve(false); return Promise.resolve(false);
@ -179,6 +183,12 @@ export function attemptTokenLogin(
const identityServer = localStorage.getItem(SSO_ID_SERVER_URL_KEY); const identityServer = localStorage.getItem(SSO_ID_SERVER_URL_KEY);
if (!homeserver) { if (!homeserver) {
console.warn("Cannot log in with token: can't determine HS URL to use"); console.warn("Cannot log in with token: can't determine HS URL to use");
Modal.createTrackedDialog("SSO", "Unknown HS", ErrorDialog, {
title: _t("We couldn't log you in"),
description: _t("We asked the browser to remember which homeserver you use to let you sign in, " +
"but unfortunately your browser has forgotten it. Go to the sign in page and try again."),
button: _t("Try again"),
});
return Promise.resolve(false); return Promise.resolve(false);
} }
@ -198,8 +208,28 @@ export function attemptTokenLogin(
return true; return true;
}); });
}).catch((err) => { }).catch((err) => {
console.error("Failed to log in with login token: " + err + " " + Modal.createTrackedDialog("SSO", "Token Rejected", ErrorDialog, {
err.data); title: _t("We couldn't log you in"),
description: err.name === "ConnectionError"
? _t("Your homeserver was unreachable and was not able to log you in. Please try again. " +
"If this continues, please contact your homeserver administrator.")
: _t("Your homeserver rejected your log in attempt. " +
"This could be due to things just taking too long. Please try again. " +
"If this continues, please contact your homeserver administrator."),
button: _t("Try again"),
onFinished: tryAgain => {
if (tryAgain) {
const cli = Matrix.createClient({
baseUrl: homeserver,
idBaseUrl: identityServer,
});
const idpId = localStorage.getItem(SSO_IDP_ID_KEY) || undefined;
PlatformPeg.get().startSingleSignOn(cli, "sso", fragmentAfterLogin, idpId);
}
},
});
console.error("Failed to log in with login token:");
console.error(err);
return false; return false;
}); });
} }
@ -366,7 +396,7 @@ async function abortLogin() {
// The plan is to gradually move the localStorage access done here into // The plan is to gradually move the localStorage access done here into
// SessionStore to avoid bugs where the view becomes out-of-sync with // SessionStore to avoid bugs where the view becomes out-of-sync with
// localStorage (e.g. isGuest etc.) // localStorage (e.g. isGuest etc.)
async function restoreFromLocalStorage(opts?: { ignoreGuest?: boolean }): Promise<boolean> { export async function restoreFromLocalStorage(opts?: { ignoreGuest?: boolean }): Promise<boolean> {
const ignoreGuest = opts?.ignoreGuest; const ignoreGuest = opts?.ignoreGuest;
if (!localStorage) { if (!localStorage) {

View file

@ -33,10 +33,20 @@ interface IPasswordFlow {
type: "m.login.password"; type: "m.login.password";
} }
export enum IdentityProviderBrand {
Gitlab = "org.matrix.gitlab",
Github = "org.matrix.github",
Apple = "org.matrix.apple",
Google = "org.matrix.google",
Facebook = "org.matrix.facebook",
Twitter = "org.matrix.twitter",
}
export interface IIdentityProvider { export interface IIdentityProvider {
id: string; id: string;
name: string; name: string;
icon?: string; icon?: string;
brand?: IdentityProviderBrand | string;
} }
export interface ISSOFlow { export interface ISSOFlow {

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import commonmark from 'commonmark'; import * as commonmark from 'commonmark';
import {escape} from "lodash"; import {escape} from "lodash";
const ALLOWED_HTML_TAGS = ['sub', 'sup', 'del', 'u']; const ALLOWED_HTML_TAGS = ['sub', 'sup', 'del', 'u'];

View file

@ -279,6 +279,10 @@ class _MatrixClientPeg implements IMatrixClientPeg {
timelineSupport: true, timelineSupport: true,
forceTURN: !SettingsStore.getValue('webRtcAllowPeerToPeer'), forceTURN: !SettingsStore.getValue('webRtcAllowPeerToPeer'),
fallbackICEServerAllowed: !!SettingsStore.getValue('fallbackICEServerAllowed'), fallbackICEServerAllowed: !!SettingsStore.getValue('fallbackICEServerAllowed'),
// Gather up to 20 ICE candidates when a call arrives: this should be more than we'd
// ever normally need, so effectively this should make all the gathering happen when
// the call arrives.
iceCandidatePoolSize: 20,
verificationMethods: [ verificationMethods: [
verificationMethods.SAS, verificationMethods.SAS,
SHOW_QR_CODE_METHOD, SHOW_QR_CODE_METHOD,

View file

@ -1,113 +0,0 @@
/*
Copyright 2016 OpenMarket Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* For two objects of the form { key: [val1, val2, val3] }, work out the added/removed
* values. Entirely new keys will result in the entire value array being added.
* @param {Object} before
* @param {Object} after
* @return {Object[]} An array of objects with the form:
* { key: $KEY, val: $VALUE, place: "add|del" }
*/
export function getKeyValueArrayDiffs(before, after) {
const results = [];
const delta = {};
Object.keys(before).forEach(function(beforeKey) {
delta[beforeKey] = delta[beforeKey] || 0; // init to 0 initially
delta[beforeKey]--; // keys present in the past have -ve values
});
Object.keys(after).forEach(function(afterKey) {
delta[afterKey] = delta[afterKey] || 0; // init to 0 initially
delta[afterKey]++; // keys present in the future have +ve values
});
Object.keys(delta).forEach(function(muxedKey) {
switch (delta[muxedKey]) {
case 1: // A new key in after
after[muxedKey].forEach(function(afterVal) {
results.push({ place: "add", key: muxedKey, val: afterVal });
});
break;
case -1: // A before key was removed
before[muxedKey].forEach(function(beforeVal) {
results.push({ place: "del", key: muxedKey, val: beforeVal });
});
break;
case 0: {// A mix of added/removed keys
// compare old & new vals
const itemDelta = {};
before[muxedKey].forEach(function(beforeVal) {
itemDelta[beforeVal] = itemDelta[beforeVal] || 0;
itemDelta[beforeVal]--;
});
after[muxedKey].forEach(function(afterVal) {
itemDelta[afterVal] = itemDelta[afterVal] || 0;
itemDelta[afterVal]++;
});
Object.keys(itemDelta).forEach(function(item) {
if (itemDelta[item] === 1) {
results.push({ place: "add", key: muxedKey, val: item });
} else if (itemDelta[item] === -1) {
results.push({ place: "del", key: muxedKey, val: item });
} else {
// itemDelta of 0 means it was unchanged between before/after
}
});
break;
}
default:
console.error("Calculated key delta of " + delta[muxedKey] + " - this should never happen!");
break;
}
});
return results;
}
/**
* Shallow-compare two objects for equality: each key and value must be identical
* @param {Object} objA First object to compare against the second
* @param {Object} objB Second object to compare against the first
* @return {boolean} whether the two objects have same key=values
*/
export function shallowEqual(objA, objB) {
if (objA === objB) {
return true;
}
if (typeof objA !== 'object' || objA === null ||
typeof objB !== 'object' || objB === null) {
return false;
}
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
if (keysA.length !== keysB.length) {
return false;
}
for (let i = 0; i < keysA.length; i++) {
const key = keysA[i];
if (!objB.hasOwnProperty(key) || objA[key] !== objB[key]) {
return false;
}
}
return true;
}

View file

@ -1,51 +0,0 @@
/*
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import SdkConfig from './SdkConfig';
import {hashCode} from './utils/FormattingUtils';
export function phasedRollOutExpiredForUser(username, feature, now, rollOutConfig = SdkConfig.get().phasedRollOut) {
if (!rollOutConfig) {
console.log(`no phased rollout configuration, so enabling ${feature}`);
return true;
}
const featureConfig = rollOutConfig[feature];
if (!featureConfig) {
console.log(`${feature} doesn't have phased rollout configured, so enabling`);
return true;
}
if (!Number.isFinite(featureConfig.offset) || !Number.isFinite(featureConfig.period)) {
console.error(`phased rollout of ${feature} is misconfigured, ` +
`offset and/or period are not numbers, so disabling`, featureConfig);
return false;
}
const hash = hashCode(username);
//ms -> min, enable users at minute granularity
const bucketRatio = 1000 * 60;
const bucketCount = featureConfig.period / bucketRatio;
const userBucket = hash % bucketCount;
const userMs = userBucket * bucketRatio;
const enableAt = featureConfig.offset + userMs;
const result = now >= enableAt;
const bucketStr = `(bucket ${userBucket}/${bucketCount})`;
if (result) {
console.log(`${feature} enabled for ${username} ${bucketStr}`);
} else {
console.log(`${feature} will be enabled for ${username} in ${Math.ceil((enableAt - now)/1000)}s ${bucketStr}`);
}
return result;
}

View file

@ -22,7 +22,7 @@ import MultiInviter from './utils/MultiInviter';
import Modal from './Modal'; import Modal from './Modal';
import * as sdk from './'; import * as sdk from './';
import { _t } from './languageHandler'; import { _t } from './languageHandler';
import {KIND_DM, KIND_INVITE} from "./components/views/dialogs/InviteDialog"; import InviteDialog, {KIND_DM, KIND_INVITE, KIND_SPACE_INVITE} from "./components/views/dialogs/InviteDialog";
import CommunityPrototypeInviteDialog from "./components/views/dialogs/CommunityPrototypeInviteDialog"; import CommunityPrototypeInviteDialog from "./components/views/dialogs/CommunityPrototypeInviteDialog";
import {CommunityPrototypeStore} from "./stores/CommunityPrototypeStore"; import {CommunityPrototypeStore} from "./stores/CommunityPrototypeStore";
@ -50,10 +50,13 @@ export function showStartChatInviteDialog(initialText) {
} }
export function showRoomInviteDialog(roomId) { export function showRoomInviteDialog(roomId) {
const isSpace = MatrixClientPeg.get()?.getRoom(roomId)?.isSpaceRoom();
// This dialog handles the room creation internally - we don't need to worry about it. // This dialog handles the room creation internally - we don't need to worry about it.
const InviteDialog = sdk.getComponent("dialogs.InviteDialog");
Modal.createTrackedDialog( Modal.createTrackedDialog(
'Invite Users', '', InviteDialog, {kind: KIND_INVITE, roomId}, "Invite Users", isSpace ? "Space" : "Room", InviteDialog, {
kind: isSpace ? KIND_SPACE_INVITE : KIND_INVITE,
roomId,
},
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true,
); );
} }

View file

@ -202,12 +202,13 @@ function setRoomNotifsStateUnmuted(roomId, newState) {
} }
function findOverrideMuteRule(roomId) { function findOverrideMuteRule(roomId) {
if (!MatrixClientPeg.get().pushRules || const cli = MatrixClientPeg.get();
!MatrixClientPeg.get().pushRules['global'] || if (!cli.pushRules ||
!MatrixClientPeg.get().pushRules['global'].override) { !cli.pushRules['global'] ||
!cli.pushRules['global'].override) {
return null; return null;
} }
for (const rule of MatrixClientPeg.get().pushRules['global'].override) { for (const rule of cli.pushRules['global'].override) {
if (isRuleForRoom(roomId, rule)) { if (isRuleForRoom(roomId, rule)) {
if (isMuteRule(rule) && rule.enabled) { if (isMuteRule(rule) && rule.enabled) {
return rule; return rule;

View file

@ -98,11 +98,27 @@ async function getSecretStorageKey(
{ keys: keyInfos }: { keys: Record<string, ISecretStorageKeyInfo> }, { keys: keyInfos }: { keys: Record<string, ISecretStorageKeyInfo> },
ssssItemName, ssssItemName,
): Promise<[string, Uint8Array]> { ): Promise<[string, Uint8Array]> {
const cli = MatrixClientPeg.get();
let keyId = await cli.getDefaultSecretStorageKeyId();
let keyInfo;
if (keyId) {
// use the default SSSS key if set
keyInfo = keyInfos[keyId];
if (!keyInfo) {
// if the default key is not available, pretend the default key
// isn't set
keyId = undefined;
}
}
if (!keyId) {
// if no default SSSS key is set, fall back to a heuristic of using the
// only available key, if only one key is set
const keyInfoEntries = Object.entries(keyInfos); const keyInfoEntries = Object.entries(keyInfos);
if (keyInfoEntries.length > 1) { if (keyInfoEntries.length > 1) {
throw new Error("Multiple storage key requests not implemented"); throw new Error("Multiple storage key requests not implemented");
} }
const [keyId, keyInfo] = keyInfoEntries[0]; [keyId, keyInfo] = keyInfoEntries[0];
}
// Check the in-memory cache // Check the in-memory cache
if (isCachingAllowed() && secretStorageKeys[keyId]) { if (isCachingAllowed() && secretStorageKeys[keyId]) {

View file

@ -48,6 +48,7 @@ import SettingsStore from "./settings/SettingsStore";
import {UIFeature} from "./settings/UIFeature"; import {UIFeature} from "./settings/UIFeature";
import {CHAT_EFFECTS} from "./effects" import {CHAT_EFFECTS} from "./effects"
import CallHandler from "./CallHandler"; import CallHandler from "./CallHandler";
import {guessAndSetDMRoom} from "./Rooms";
// XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816 // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816
interface HTMLInputEvent extends Event { interface HTMLInputEvent extends Event {
@ -1039,9 +1040,7 @@ export const Commands = [
return success((async () => { return success((async () => {
if (isPhoneNumber) { if (isPhoneNumber) {
const results = await MatrixClientPeg.get().getThirdpartyUser('im.vector.protocol.pstn', { const results = await CallHandler.sharedInstance().pstnLookup(this.state.value);
'm.id.phone': userId,
});
if (!results || results.length === 0 || !results[0].userid) { if (!results || results.length === 0 || !results[0].userid) {
throw new Error("Unable to find Matrix ID for phone number"); throw new Error("Unable to find Matrix ID for phone number");
} }
@ -1112,6 +1111,24 @@ export const Commands = [
return success(); return success();
}, },
}), }),
new Command({
command: "converttodm",
description: _td("Converts the room to a DM"),
category: CommandCategories.other,
runFn: function(roomId, args) {
const room = MatrixClientPeg.get().getRoom(roomId);
return success(guessAndSetDMRoom(room, true));
},
}),
new Command({
command: "converttoroom",
description: _td("Converts the DM to a room"),
category: CommandCategories.other,
runFn: function(roomId, args) {
const room = MatrixClientPeg.get().getRoom(roomId);
return success(guessAndSetDMRoom(room, false));
},
}),
// Command definitions for autocompletion ONLY: // Command definitions for autocompletion ONLY:
// /me is special because its not handled by SlashCommands.js and is instead done inside the Composer classes // /me is special because its not handled by SlashCommands.js and is instead done inside the Composer classes
@ -1163,7 +1180,7 @@ export function parseCommandString(input: string) {
input = input.replace(/\s+$/, ''); input = input.replace(/\s+$/, '');
if (input[0] !== '/') return {}; // not a command if (input[0] !== '/') return {}; // not a command
const bits = input.match(/^(\S+?)(?: +((.|\n)*))?$/); const bits = input.match(/^(\S+?)(?:[ \n]+((.|\n)*))?$/);
let cmd; let cmd;
let args; let args;
if (bits) { if (bits) {

View file

@ -118,25 +118,10 @@ export default class Velociraptor extends React.Component {
domNode.style.visibility = restingStyle.visibility; domNode.style.visibility = restingStyle.visibility;
}); });
/* // console.log("enter:",
console.log("enter:", // JSON.stringify(transitionOpts[i-1]),
JSON.stringify(transitionOpts[i-1]), // "->",
"->", // JSON.stringify(restingStyle));
JSON.stringify(restingStyle));
*/
} else if (node === null) {
// Velocity stores data on elements using the jQuery .data()
// method, and assumes you'll be using jQuery's .remove() to
// remove the element, but we don't use jQuery, so we need to
// blow away the element's data explicitly otherwise it will leak.
// This uses Velocity's internal jQuery compatible wrapper.
// See the bug at
// https://github.com/julianshapiro/velocity/issues/300
// and the FAQ entry, "Preventing memory leaks when
// creating/destroying large numbers of elements"
// (https://github.com/julianshapiro/velocity/issues/47)
const domNode = ReactDom.findDOMNode(this.nodes[k]);
if (domNode) Velocity.Utilities.removeData(domNode);
} }
this.nodes[k] = node; this.nodes[k] = node;
} }

View file

@ -14,66 +14,97 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { ensureDMExists, findDMForUser } from './createRoom'; import { ensureVirtualRoomExists, findDMForUser } from './createRoom';
import { MatrixClientPeg } from "./MatrixClientPeg"; import { MatrixClientPeg } from "./MatrixClientPeg";
import DMRoomMap from "./utils/DMRoomMap"; import DMRoomMap from "./utils/DMRoomMap";
import SdkConfig from "./SdkConfig"; import CallHandler, { VIRTUAL_ROOM_EVENT_TYPE } from './CallHandler';
import { Room } from 'matrix-js-sdk/src/models/room';
// Functions for mapping users & rooms for the voip_mxid_translate_pattern // Functions for mapping virtual users & rooms. Currently the only lookup
// config option // is sip virtual: there could be others in the future.
export function voipUserMapperEnabled(): boolean { export default class VoipUserMapper {
return SdkConfig.get()['voip_mxid_translate_pattern'] !== undefined; private virtualRoomIdCache = new Set<string>();
}
// only exported for tests public static sharedInstance(): VoipUserMapper {
export function userToVirtualUser(userId: string, templateString?: string): string { if (window.mxVoipUserMapper === undefined) window.mxVoipUserMapper = new VoipUserMapper();
if (templateString === undefined) templateString = SdkConfig.get()['voip_mxid_translate_pattern']; return window.mxVoipUserMapper;
if (!templateString) return null; }
return templateString.replace('${mxid}', encodeURIComponent(userId).replace(/%/g, '=').toLowerCase());
}
// only exported for tests private async userToVirtualUser(userId: string): Promise<string> {
export function virtualUserToUser(userId: string, templateString?: string): string { const results = await CallHandler.sharedInstance().sipVirtualLookup(userId);
if (templateString === undefined) templateString = SdkConfig.get()['voip_mxid_translate_pattern']; if (results.length === 0) return null;
if (!templateString) return null; return results[0].userid;
}
const regexString = templateString.replace('${mxid}', '(.+)'); public async getOrCreateVirtualRoomForRoom(roomId: string):Promise<string> {
const userId = DMRoomMap.shared().getUserIdForRoomId(roomId);
if (!userId) return null;
const match = userId.match('^' + regexString + '$'); const virtualUser = await this.userToVirtualUser(userId);
if (!match) return null;
return decodeURIComponent(match[1].replace(/=/g, '%'));
}
async function getOrCreateVirtualRoomForUser(userId: string):Promise<string> {
const virtualUser = userToVirtualUser(userId);
if (!virtualUser) return null; if (!virtualUser) return null;
return await ensureDMExists(MatrixClientPeg.get(), virtualUser); const virtualRoomId = await ensureVirtualRoomExists(MatrixClientPeg.get(), virtualUser, roomId);
} MatrixClientPeg.get().setRoomAccountData(virtualRoomId, VIRTUAL_ROOM_EVENT_TYPE, {
native_room: roomId,
});
export async function getOrCreateVirtualRoomForRoom(roomId: string):Promise<string> { return virtualRoomId;
const user = DMRoomMap.shared().getUserIdForRoomId(roomId); }
if (!user) return null;
return getOrCreateVirtualRoomForUser(user);
}
export function roomForVirtualRoom(roomId: string):string { public nativeRoomForVirtualRoom(roomId: string):string {
const virtualUser = DMRoomMap.shared().getUserIdForRoomId(roomId); const virtualRoom = MatrixClientPeg.get().getRoom(roomId);
if (!virtualUser) return null; if (!virtualRoom) return null;
const realUser = virtualUserToUser(virtualUser); const virtualRoomEvent = virtualRoom.getAccountData(VIRTUAL_ROOM_EVENT_TYPE);
const room = findDMForUser(MatrixClientPeg.get(), realUser); if (!virtualRoomEvent || !virtualRoomEvent.getContent()) return null;
if (room) { return virtualRoomEvent.getContent()['native_room'] || null;
return room.roomId; }
} else {
return null; public isVirtualRoom(room: Room):boolean {
if (this.nativeRoomForVirtualRoom(room.roomId)) return true;
if (this.virtualRoomIdCache.has(room.roomId)) return true;
// also look in the create event for the claimed native room ID, which is the only
// way we can recognise a virtual room we've created when it first arrives down
// our stream. We don't trust this in general though, as it could be faked by an
// inviter: our main source of truth is the DM state.
const roomCreateEvent = room.currentState.getStateEvents("m.room.create", "");
if (!roomCreateEvent || !roomCreateEvent.getContent()) return false;
// we only look at this for rooms we created (so inviters can't just cause rooms
// to be invisible)
if (roomCreateEvent.getSender() !== MatrixClientPeg.get().getUserId()) return false;
const claimedNativeRoomId = roomCreateEvent.getContent()[VIRTUAL_ROOM_EVENT_TYPE];
return Boolean(claimedNativeRoomId);
}
public async onNewInvitedRoom(invitedRoom: Room) {
const inviterId = invitedRoom.getDMInviter();
console.log(`Checking virtual-ness of room ID ${invitedRoom.roomId}, invited by ${inviterId}`);
const result = await CallHandler.sharedInstance().sipNativeLookup(inviterId);
if (result.length === 0) {
return true;
}
if (result[0].fields.is_virtual) {
const nativeUser = result[0].userid;
const nativeRoom = findDMForUser(MatrixClientPeg.get(), nativeUser);
if (nativeRoom) {
// It's a virtual room with a matching native room, so set the room account data. This
// will make sure we know where how to map calls and also allow us know not to display
// it in the future.
MatrixClientPeg.get().setRoomAccountData(invitedRoom.roomId, VIRTUAL_ROOM_EVENT_TYPE, {
native_room: nativeRoom.roomId,
});
// also auto-join the virtual room if we have a matching native room
// (possibly we should only join if we've also joined the native room, then we'd also have
// to make sure we joined virtual rooms on joining a native one)
MatrixClientPeg.get().joinRoom(invitedRoom.roomId);
}
// also put this room in the virtual room ID cache so isVirtualRoom return the right answer
// in however long it takes for the echo of setAccountData to come down the sync
this.virtualRoomIdCache.add(invitedRoom.roomId);
}
} }
} }
export function isVirtualRoom(roomId: string):boolean {
const virtualUser = DMRoomMap.shared().getUserIdForRoomId(roomId);
if (!virtualUser) return null;
const realUser = virtualUserToUser(virtualUser);
return Boolean(realUser);
}

View file

@ -168,6 +168,12 @@ const shortcuts: Record<Categories, IShortcut[]> = {
key: Key.U, key: Key.U,
}], }],
description: _td("Upload a file"), description: _td("Upload a file"),
}, {
keybinds: [{
modifiers: [CMD_OR_CTRL],
key: Key.F,
}],
description: _td("Search (must be enabled)"),
}, },
], ],

View file

@ -19,14 +19,23 @@ limitations under the License.
import React from "react"; import React from "react";
import AccessibleButton from "../../components/views/elements/AccessibleButton"; import AccessibleButton from "../../components/views/elements/AccessibleButton";
import AccessibleTooltipButton from "../../components/views/elements/AccessibleTooltipButton";
interface IProps extends React.ComponentProps<typeof AccessibleButton> { interface IProps extends React.ComponentProps<typeof AccessibleButton> {
label?: string; label?: string;
tooltip?: string;
} }
// Semantic component for representing a role=menuitem // Semantic component for representing a role=menuitem
export const MenuItem: React.FC<IProps> = ({children, label, ...props}) => { export const MenuItem: React.FC<IProps> = ({children, label, tooltip, ...props}) => {
const ariaLabel = props["aria-label"] || label; const ariaLabel = props["aria-label"] || label;
if (tooltip) {
return <AccessibleTooltipButton {...props} role="menuitem" tabIndex={-1} aria-label={ariaLabel} title={tooltip}>
{ children }
</AccessibleTooltipButton>;
}
return ( return (
<AccessibleButton {...props} role="menuitem" tabIndex={-1} aria-label={ariaLabel}> <AccessibleButton {...props} role="menuitem" tabIndex={-1} aria-label={ariaLabel}>
{ children } { children }

View file

@ -155,6 +155,7 @@ export default class UserProvider extends AutocompleteProvider {
const currentUserId = MatrixClientPeg.get().credentials.userId; const currentUserId = MatrixClientPeg.get().credentials.userId;
this.users = this.room.getJoinedMembers().filter(({userId}) => userId !== currentUserId); this.users = this.room.getJoinedMembers().filter(({userId}) => userId !== currentUserId);
this.users = this.users.concat(this.room.getMembersWithMembership("invite"));
this.users = sortBy(this.users, (member) => 1E20 - lastSpoken[member.userId] || 1E20); this.users = sortBy(this.users, (member) => 1E20 - lastSpoken[member.userId] || 1E20);

View file

@ -76,6 +76,7 @@ export interface IProps extends IPosition {
hasBackground?: boolean; hasBackground?: boolean;
// whether this context menu should be focus managed. If false it must handle itself // whether this context menu should be focus managed. If false it must handle itself
managed?: boolean; managed?: boolean;
wrapperClassName?: string;
// Function to be called on menu close // Function to be called on menu close
onFinished(); onFinished();
@ -299,7 +300,7 @@ export class ContextMenu extends React.PureComponent<IProps, IState> {
// such that it does not leave the (padded) window. // such that it does not leave the (padded) window.
if (contextMenuRect) { if (contextMenuRect) {
const padding = 10; const padding = 10;
adjusted = Math.min(position.top, document.body.clientHeight - contextMenuRect.height + padding); adjusted = Math.min(position.top, document.body.clientHeight - contextMenuRect.height - padding);
} }
position.top = adjusted; position.top = adjusted;
@ -365,7 +366,7 @@ export class ContextMenu extends React.PureComponent<IProps, IState> {
return ( return (
<div <div
className="mx_ContextualMenu_wrapper" className={classNames("mx_ContextualMenu_wrapper", this.props.wrapperClassName)}
style={{...position, ...wrapperStyle}} style={{...position, ...wrapperStyle}}
onKeyDown={this.onKeyDown} onKeyDown={this.onKeyDown}
onContextMenu={this.onContextMenuPreventBubbling} onContextMenu={this.onContextMenuPreventBubbling}
@ -390,7 +391,7 @@ export class ContextMenu extends React.PureComponent<IProps, IState> {
} }
// Placement method for <ContextMenu /> to position context menu to right of elementRect with chevronOffset // Placement method for <ContextMenu /> to position context menu to right of elementRect with chevronOffset
export const toRightOf = (elementRect: DOMRect, chevronOffset = 12) => { export const toRightOf = (elementRect: Pick<DOMRect, "right" | "top" | "height">, chevronOffset = 12) => {
const left = elementRect.right + window.pageXOffset + 3; const left = elementRect.right + window.pageXOffset + 3;
let top = elementRect.top + (elementRect.height / 2) + window.pageYOffset; let top = elementRect.top + (elementRect.height / 2) + window.pageYOffset;
top -= chevronOffset + 8; // where 8 is half the height of the chevron top -= chevronOffset + 8; // where 8 is half the height of the chevron

View file

@ -45,7 +45,7 @@ class FilePanel extends React.Component {
}; };
onRoomTimeline = (ev, room, toStartOfTimeline, removed, data) => { onRoomTimeline = (ev, room, toStartOfTimeline, removed, data) => {
if (room.roomId !== this.props.roomId) return; if (room?.roomId !== this.props?.roomId) return;
if (toStartOfTimeline || !data || !data.liveEvent || ev.isRedacted()) return; if (toStartOfTimeline || !data || !data.liveEvent || ev.isRedacted()) return;
if (ev.isBeingDecrypted()) { if (ev.isBeingDecrypted()) {

View file

@ -0,0 +1,56 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import {
IconizedContextMenuOption,
IconizedContextMenuOptionList,
} from "../views/context_menus/IconizedContextMenu";
import { _t } from "../../languageHandler";
import { HostSignupStore } from "../../stores/HostSignupStore";
import SdkConfig from "../../SdkConfig";
interface IProps {}
interface IState {}
export default class HostSignupAction extends React.PureComponent<IProps, IState> {
private openDialog = async () => {
await HostSignupStore.instance.setHostSignupActive(true);
}
public render(): React.ReactNode {
const hostSignupConfig = SdkConfig.get().hostSignup;
if (!hostSignupConfig?.brand) {
return null;
}
return (
<IconizedContextMenuOptionList>
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconHosting"
label={_t(
"Upgrade to %(hostSignupBrand)s",
{
hostSignupBrand: hostSignupConfig.brand,
},
)}
onClick={this.openDialog}
/>
</IconizedContextMenuOptionList>
);
}
}

View file

@ -177,7 +177,14 @@ export default class InteractiveAuthComponent extends React.Component {
stageState: stageState, stageState: stageState,
errorText: stageState.error, errorText: stageState.error,
}, () => { }, () => {
if (oldStage != stageType) this._setFocus(); if (oldStage !== stageType) {
this._setFocus();
} else if (
!stageState.error && this._stageComponent.current &&
this._stageComponent.current.attemptFailed
) {
this._stageComponent.current.attemptFailed();
}
}); });
}; };

Some files were not shown because too many files have changed in this diff Show more