From 98f82d0d3346e78951b8d9db2e2fdaae332d6a03 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 19 Oct 2021 10:56:25 +0100 Subject: [PATCH 001/268] Upgrade matrix-js-sdk to 15.0.0-rc.1 --- package.json | 2 +- yarn.lock | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index c3cd054db8..3613406677 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "katex": "^0.12.0", "linkifyjs": "^2.1.9", "lodash": "^4.17.20", - "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", + "matrix-js-sdk": "15.0.0-rc.1", "matrix-widget-api": "^0.1.0-beta.16", "minimist": "^1.2.5", "opus-recorder": "^8.0.3", diff --git a/yarn.lock b/yarn.lock index 0404e9b6b7..c68babda16 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5864,9 +5864,10 @@ mathml-tag-names@^2.1.3: resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== -"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop": - version "14.0.1" - resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/e7b41fadd0d6eda7423a369c99ec7b94afd48d5d" +matrix-js-sdk@15.0.0-rc.1: + version "15.0.0-rc.1" + resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-15.0.0-rc.1.tgz#c44e04ba2017eb8e14c8034c2be83cbad8504e01" + integrity sha512-BI5ImtPCaOBsS5UETxTYyiL7ePlzfUZAn6c07+QPir6FhD8NeCaW772nWAWRjZNYVWq85UkGRVbvPeVWQvUtAA== dependencies: "@babel/runtime" "^7.12.5" another-json "^0.2.0" From 98067f345020d2b5ebb09436a6530dd2a905ce42 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 19 Oct 2021 10:58:42 +0100 Subject: [PATCH 002/268] Prepare changelog for v3.33.0-rc.1 --- CHANGELOG.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f133398724..9b8a71b053 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,45 @@ +Changes in [3.33.0-rc.1](https://github.com/vector-im/element-desktop/releases/tag/v3.33.0-rc.1) (2021-10-19) +============================================================================================================= + +## ✨ Features + * Swap order of private space creation and tweak copy ([\#6967](https://github.com/matrix-org/matrix-react-sdk/pull/6967)). Fixes vector-im/element-web#18768 and vector-im/element-web#18768. + * Add spacing to Room settings - Notifications subsection ([\#6962](https://github.com/matrix-org/matrix-react-sdk/pull/6962)). Contributed by [CicadaCinema](https://github.com/CicadaCinema). + * Convert the "Cryptography" settings panel to an HTML to assist screen reader users. ([\#6968](https://github.com/matrix-org/matrix-react-sdk/pull/6968)). Contributed by [andybalaam](https://github.com/andybalaam). + * Use HTML tables for some tabular user interface areas, to assist with screen reader use ([\#6955](https://github.com/matrix-org/matrix-react-sdk/pull/6955)). Contributed by [andybalaam](https://github.com/andybalaam). + * Fix space invite edge cases ([\#6884](https://github.com/matrix-org/matrix-react-sdk/pull/6884)). Fixes vector-im/element-web#19010 vector-im/element-web#17345 and vector-im/element-web#19010. + * Allow options to cascade kicks/bans throughout spaces ([\#6829](https://github.com/matrix-org/matrix-react-sdk/pull/6829)). Fixes vector-im/element-web#18969 and vector-im/element-web#18969. + * Make public space alias field mandatory again ([\#6921](https://github.com/matrix-org/matrix-react-sdk/pull/6921)). Fixes vector-im/element-web#19003 and vector-im/element-web#19003. + * Add progress bar to restricted room upgrade dialog ([\#6919](https://github.com/matrix-org/matrix-react-sdk/pull/6919)). Fixes vector-im/element-web#19146 and vector-im/element-web#19146. + * Add customisation point for visibility of invites and room creation ([\#6922](https://github.com/matrix-org/matrix-react-sdk/pull/6922)). Fixes vector-im/element-web#19331 and vector-im/element-web#19331. + * Inhibit `Unable to get validated threepid` error during UIA ([\#6928](https://github.com/matrix-org/matrix-react-sdk/pull/6928)). Fixes vector-im/element-web#18883 and vector-im/element-web#18883. + * Tweak room list skeleton UI height and behaviour ([\#6926](https://github.com/matrix-org/matrix-react-sdk/pull/6926)). Fixes vector-im/element-web#18231 vector-im/element-web#16581 and vector-im/element-web#18231. + * If public room creation fails, retry without publishing it ([\#6872](https://github.com/matrix-org/matrix-react-sdk/pull/6872)). Fixes vector-im/element-web#19194 and vector-im/element-web#19194. Contributed by [AndrewFerr](https://github.com/AndrewFerr). + * Iterate invite your teammates to Space view ([\#6925](https://github.com/matrix-org/matrix-react-sdk/pull/6925)). Fixes vector-im/element-web#18772 and vector-im/element-web#18772. + * Make placeholder more grey when no input ([\#6840](https://github.com/matrix-org/matrix-react-sdk/pull/6840)). Fixes vector-im/element-web#17243 and vector-im/element-web#17243. Contributed by [wlach](https://github.com/wlach). + * Respect tombstones in locally known rooms for Space children ([\#6906](https://github.com/matrix-org/matrix-react-sdk/pull/6906)). Fixes vector-im/element-web#19246 vector-im/element-web#19256 and vector-im/element-web#19246. + * Improve emoji shortcodes generated from annotations ([\#6907](https://github.com/matrix-org/matrix-react-sdk/pull/6907)). Fixes vector-im/element-web#19304 and vector-im/element-web#19304. + * Hide kick & ban options in UserInfo when looking at own profile ([\#6911](https://github.com/matrix-org/matrix-react-sdk/pull/6911)). Fixes vector-im/element-web#19066 and vector-im/element-web#19066. + * Add progress bar to Community to Space migration tool ([\#6887](https://github.com/matrix-org/matrix-react-sdk/pull/6887)). Fixes vector-im/element-web#19216 and vector-im/element-web#19216. + +## 🐛 Bug Fixes + * Fix leave space cancel button exploding ([\#6966](https://github.com/matrix-org/matrix-react-sdk/pull/6966)). + * Fix edge case behaviour of the space join spinner for guests ([\#6972](https://github.com/matrix-org/matrix-react-sdk/pull/6972)). Fixes vector-im/element-web#19359 and vector-im/element-web#19359. + * Convert emoticon to emoji at the end of a line on send even if the cursor isn't there ([\#6965](https://github.com/matrix-org/matrix-react-sdk/pull/6965)). Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Fix text overflows button on Home page ([\#6898](https://github.com/matrix-org/matrix-react-sdk/pull/6898)). Fixes vector-im/element-web#19180 and vector-im/element-web#19180. Contributed by [oliver-pham](https://github.com/oliver-pham). + * Space Room View should react to join rule changes down /sync ([\#6945](https://github.com/matrix-org/matrix-react-sdk/pull/6945)). Fixes vector-im/element-web#19390 and vector-im/element-web#19390. + * Hide leave section button if user isn't in the room e.g peeking ([\#6920](https://github.com/matrix-org/matrix-react-sdk/pull/6920)). Fixes vector-im/element-web#17410 and vector-im/element-web#17410. + * Fix bug where room list would get stuck showing no rooms ([\#6939](https://github.com/matrix-org/matrix-react-sdk/pull/6939)). Fixes vector-im/element-web#19373 and vector-im/element-web#19373. + * Update room settings dialog title when room name changes ([\#6916](https://github.com/matrix-org/matrix-react-sdk/pull/6916)). Fixes vector-im/element-web#17480 and vector-im/element-web#17480. Contributed by [psrpinto](https://github.com/psrpinto). + * Fix editing losing emote-ness and rainbow-ness of messages ([\#6931](https://github.com/matrix-org/matrix-react-sdk/pull/6931)). Fixes vector-im/element-web#19350 and vector-im/element-web#19350. + * Remove semicolon from notifications panel ([\#6930](https://github.com/matrix-org/matrix-react-sdk/pull/6930)). Contributed by [robintown](https://github.com/robintown). + * Prevent profile image in left panel's backdrop from being selected ([\#6924](https://github.com/matrix-org/matrix-react-sdk/pull/6924)). Contributed by [rom4nik](https://github.com/rom4nik). + * Validate that the phone number verification field is filled before allowing user to submit ([\#6918](https://github.com/matrix-org/matrix-react-sdk/pull/6918)). Fixes vector-im/element-web#19316 and vector-im/element-web#19316. Contributed by [VFermat](https://github.com/VFermat). + * Updated how save button becomes disabled in room settings to listen for all fields instead of the most recent ([\#6917](https://github.com/matrix-org/matrix-react-sdk/pull/6917)). Contributed by [LoganArnett](https://github.com/LoganArnett). + * Use FocusLock around ContextMenus to simplify focus management ([\#6311](https://github.com/matrix-org/matrix-react-sdk/pull/6311)). Fixes vector-im/element-web#19259 and vector-im/element-web#19259. + * Fix space hierarchy pagination ([\#6908](https://github.com/matrix-org/matrix-react-sdk/pull/6908)). Fixes vector-im/element-web#19276 and vector-im/element-web#19276. + * Fix spaces keyboard shortcuts not working for last space ([\#6909](https://github.com/matrix-org/matrix-react-sdk/pull/6909)). Fixes vector-im/element-web#19255 and vector-im/element-web#19255. + * Use fallback avatar only for DMs with 2 people. ([\#6895](https://github.com/matrix-org/matrix-react-sdk/pull/6895)). Fixes vector-im/element-web#18747 and vector-im/element-web#18747. Contributed by [andybalaam](https://github.com/andybalaam). + Changes in [3.32.1](https://github.com/vector-im/element-desktop/releases/tag/v3.32.1) (2021-10-12) =================================================================================================== From 10ed592a55097dacffaea6be2faafd4ff16d7499 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 19 Oct 2021 10:58:43 +0100 Subject: [PATCH 003/268] v3.33.0-rc.1 --- package.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 3613406677..66053c0361 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.32.1", + "version": "3.33.0-rc.1", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { @@ -25,7 +25,7 @@ "bin": { "reskindex": "scripts/reskindex.js" }, - "main": "./src/index.ts", + "main": "./lib/index.ts", "matrix_src_main": "./src/index.ts", "matrix_lib_main": "./lib/index.ts", "matrix_lib_typings": "./lib/index.d.ts", @@ -220,5 +220,6 @@ "coverageReporters": [ "text" ] - } + }, + "typings": "./lib/index.d.ts" } From b16af66fe2d71997308d87ad07dc96f29062a595 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 19 Oct 2021 18:43:29 +0100 Subject: [PATCH 004/268] Fix conflicting CSS on syntax highlighted blocks (#6991) Fixes https://github.com/vector-im/element-web/issues/19445 --- res/css/views/rooms/_EventTile.scss | 4 ---- src/components/views/messages/TextualBody.tsx | 7 +++++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 540b7d89af..ea53cc4f13 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -589,10 +589,6 @@ $hover-select-border: 4px; padding: 0 10px; } -.mx_EventTile_content .markdown-body .hljs { - display: inline !important; -} - /* // actually, removing the Italic TTF provides // better results seemingly diff --git a/src/components/views/messages/TextualBody.tsx b/src/components/views/messages/TextualBody.tsx index 941c269991..b8e068ed75 100644 --- a/src/components/views/messages/TextualBody.tsx +++ b/src/components/views/messages/TextualBody.tsx @@ -232,7 +232,6 @@ export default class TextualBody extends React.Component { ); return; } - console.log('highlighting'); let advertisedLang; for (const cl of code.className.split(/\s+/)) { @@ -258,7 +257,11 @@ export default class TextualBody extends React.Component { // User has language detection enabled and the code is within a pre // we only auto-highlight if the code block is in a pre), so highlight // the block with auto-highlighting enabled. - highlight.highlightElement(code); + // We pass highlightjs the text to highlight rather than letting it + // work on the DOM with highlightElement because that also adds CSS + // classes to the pre/code element that we don't want (the CSS + // conflicts with our own). + code.innerHTML = highlight.highlightAuto(code.textContent).value; } } From 35c0e18b24364689efe52444fe76b364771a1f57 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Wed, 20 Oct 2021 08:39:35 +0100 Subject: [PATCH 005/268] Prepare changelog for v3.33.0-rc.2 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b8a71b053..675e0ef359 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +Changes in [3.33.0-rc.2](https://github.com/vector-im/element-desktop/releases/tag/v3.33.0-rc.2) (2021-10-20) +============================================================================================================= + +## 🐛 Bug Fixes + * Fix conflicting CSS on syntax highlighted blocks ([\#6991](https://github.com/matrix-org/matrix-react-sdk/pull/6991)). Fixes vector-im/element-web#19445 + Changes in [3.33.0-rc.1](https://github.com/vector-im/element-desktop/releases/tag/v3.33.0-rc.1) (2021-10-19) ============================================================================================================= From 4cf286b276dcf00ca70c497b881a736915a25216 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Wed, 20 Oct 2021 08:39:36 +0100 Subject: [PATCH 006/268] v3.33.0-rc.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 66053c0361..09627121e1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.33.0-rc.1", + "version": "3.33.0-rc.2", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { From 3c8a698e2dd35fbabf63e4b82211310385f37c4c Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Mon, 25 Oct 2021 11:27:30 +0100 Subject: [PATCH 007/268] Upgrade matrix-js-sdk to 15.0.0 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 09627121e1..bb1b5e0d0f 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "katex": "^0.12.0", "linkifyjs": "^2.1.9", "lodash": "^4.17.20", - "matrix-js-sdk": "15.0.0-rc.1", + "matrix-js-sdk": "15.0.0", "matrix-widget-api": "^0.1.0-beta.16", "minimist": "^1.2.5", "opus-recorder": "^8.0.3", diff --git a/yarn.lock b/yarn.lock index c68babda16..c327869cc2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5864,10 +5864,10 @@ mathml-tag-names@^2.1.3: resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== -matrix-js-sdk@15.0.0-rc.1: - version "15.0.0-rc.1" - resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-15.0.0-rc.1.tgz#c44e04ba2017eb8e14c8034c2be83cbad8504e01" - integrity sha512-BI5ImtPCaOBsS5UETxTYyiL7ePlzfUZAn6c07+QPir6FhD8NeCaW772nWAWRjZNYVWq85UkGRVbvPeVWQvUtAA== +matrix-js-sdk@15.0.0: + version "15.0.0" + resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-15.0.0.tgz#b28975074d7a938770d8f40ad0088bb476a6bdcf" + integrity sha512-IgpmPY3DVD3Oj+TRIWsxZCTnKU2J8iVry5a7qIXelsroCxcV5/FKs/C3kphv+nFF0afMBdLLwj+n3Hlf1tdjuA== dependencies: "@babel/runtime" "^7.12.5" another-json "^0.2.0" From 515ba194b63efd248a9da1e118761f046b372c6c Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Mon, 25 Oct 2021 11:30:23 +0100 Subject: [PATCH 008/268] Prepare changelog for v3.33.0 --- CHANGELOG.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 675e0ef359..e6fa6c3c80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,45 @@ +Changes in [3.33.0](https://github.com/vector-im/element-desktop/releases/tag/v3.33.0) (2021-10-25) +=================================================================================================== + +## ✨ Features + * Convert the "Cryptography" settings panel to an HTML table to assist screen reader users. ([\#6968](https://github.com/matrix-org/matrix-react-sdk/pull/6968)). Contributed by [andybalaam](https://github.com/andybalaam). + * Swap order of private space creation and tweak copy ([\#6967](https://github.com/matrix-org/matrix-react-sdk/pull/6967)). Fixes vector-im/element-web#18768 and vector-im/element-web#18768. + * Add spacing to Room settings - Notifications subsection ([\#6962](https://github.com/matrix-org/matrix-react-sdk/pull/6962)). Contributed by [CicadaCinema](https://github.com/CicadaCinema). + * Use HTML tables for some tabular user interface areas, to assist with screen reader use ([\#6955](https://github.com/matrix-org/matrix-react-sdk/pull/6955)). Contributed by [andybalaam](https://github.com/andybalaam). + * Fix space invite edge cases ([\#6884](https://github.com/matrix-org/matrix-react-sdk/pull/6884)). Fixes vector-im/element-web#19010 vector-im/element-web#17345 and vector-im/element-web#19010. + * Allow options to cascade kicks/bans throughout spaces ([\#6829](https://github.com/matrix-org/matrix-react-sdk/pull/6829)). Fixes vector-im/element-web#18969 and vector-im/element-web#18969. + * Make public space alias field mandatory again ([\#6921](https://github.com/matrix-org/matrix-react-sdk/pull/6921)). Fixes vector-im/element-web#19003 and vector-im/element-web#19003. + * Add progress bar to restricted room upgrade dialog ([\#6919](https://github.com/matrix-org/matrix-react-sdk/pull/6919)). Fixes vector-im/element-web#19146 and vector-im/element-web#19146. + * Add customisation point for visibility of invites and room creation ([\#6922](https://github.com/matrix-org/matrix-react-sdk/pull/6922)). Fixes vector-im/element-web#19331 and vector-im/element-web#19331. + * Inhibit `Unable to get validated threepid` error during UIA ([\#6928](https://github.com/matrix-org/matrix-react-sdk/pull/6928)). Fixes vector-im/element-web#18883 and vector-im/element-web#18883. + * Tweak room list skeleton UI height and behaviour ([\#6926](https://github.com/matrix-org/matrix-react-sdk/pull/6926)). Fixes vector-im/element-web#18231 vector-im/element-web#16581 and vector-im/element-web#18231. + * If public room creation fails, retry without publishing it ([\#6872](https://github.com/matrix-org/matrix-react-sdk/pull/6872)). Fixes vector-im/element-web#19194 and vector-im/element-web#19194. Contributed by [AndrewFerr](https://github.com/AndrewFerr). + * Iterate invite your teammates to Space view ([\#6925](https://github.com/matrix-org/matrix-react-sdk/pull/6925)). Fixes vector-im/element-web#18772 and vector-im/element-web#18772. + * Make placeholder more grey when no input ([\#6840](https://github.com/matrix-org/matrix-react-sdk/pull/6840)). Fixes vector-im/element-web#17243 and vector-im/element-web#17243. Contributed by [wlach](https://github.com/wlach). + * Respect tombstones in locally known rooms for Space children ([\#6906](https://github.com/matrix-org/matrix-react-sdk/pull/6906)). Fixes vector-im/element-web#19246 vector-im/element-web#19256 and vector-im/element-web#19246. + * Improve emoji shortcodes generated from annotations ([\#6907](https://github.com/matrix-org/matrix-react-sdk/pull/6907)). Fixes vector-im/element-web#19304 and vector-im/element-web#19304. + * Hide kick & ban options in UserInfo when looking at own profile ([\#6911](https://github.com/matrix-org/matrix-react-sdk/pull/6911)). Fixes vector-im/element-web#19066 and vector-im/element-web#19066. + * Add progress bar to Community to Space migration tool ([\#6887](https://github.com/matrix-org/matrix-react-sdk/pull/6887)). Fixes vector-im/element-web#19216 and vector-im/element-web#19216. + +## 🐛 Bug Fixes + * Fix leave space cancel button exploding ([\#6966](https://github.com/matrix-org/matrix-react-sdk/pull/6966)). + * Fix edge case behaviour of the space join spinner for guests ([\#6972](https://github.com/matrix-org/matrix-react-sdk/pull/6972)). Fixes vector-im/element-web#19359 and vector-im/element-web#19359. + * Convert emoticon to emoji at the end of a line on send even if the cursor isn't there ([\#6965](https://github.com/matrix-org/matrix-react-sdk/pull/6965)). Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Fix text overflows button on Home page ([\#6898](https://github.com/matrix-org/matrix-react-sdk/pull/6898)). Fixes vector-im/element-web#19180 and vector-im/element-web#19180. Contributed by [oliver-pham](https://github.com/oliver-pham). + * Space Room View should react to join rule changes down /sync ([\#6945](https://github.com/matrix-org/matrix-react-sdk/pull/6945)). Fixes vector-im/element-web#19390 and vector-im/element-web#19390. + * Hide leave section button if user isn't in the room e.g peeking ([\#6920](https://github.com/matrix-org/matrix-react-sdk/pull/6920)). Fixes vector-im/element-web#17410 and vector-im/element-web#17410. + * Fix bug where room list would get stuck showing no rooms ([\#6939](https://github.com/matrix-org/matrix-react-sdk/pull/6939)). Fixes vector-im/element-web#19373 and vector-im/element-web#19373. + * Update room settings dialog title when room name changes ([\#6916](https://github.com/matrix-org/matrix-react-sdk/pull/6916)). Fixes vector-im/element-web#17480 and vector-im/element-web#17480. Contributed by [psrpinto](https://github.com/psrpinto). + * Fix editing losing emote-ness and rainbow-ness of messages ([\#6931](https://github.com/matrix-org/matrix-react-sdk/pull/6931)). Fixes vector-im/element-web#19350 and vector-im/element-web#19350. + * Remove semicolon from notifications panel ([\#6930](https://github.com/matrix-org/matrix-react-sdk/pull/6930)). Contributed by [robintown](https://github.com/robintown). + * Prevent profile image in left panel's backdrop from being selected ([\#6924](https://github.com/matrix-org/matrix-react-sdk/pull/6924)). Contributed by [rom4nik](https://github.com/rom4nik). + * Validate that the phone number verification field is filled before allowing user to submit ([\#6918](https://github.com/matrix-org/matrix-react-sdk/pull/6918)). Fixes vector-im/element-web#19316 and vector-im/element-web#19316. Contributed by [VFermat](https://github.com/VFermat). + * Updated how save button becomes disabled in room settings to listen for all fields instead of the most recent ([\#6917](https://github.com/matrix-org/matrix-react-sdk/pull/6917)). Contributed by [LoganArnett](https://github.com/LoganArnett). + * Use FocusLock around ContextMenus to simplify focus management ([\#6311](https://github.com/matrix-org/matrix-react-sdk/pull/6311)). Fixes vector-im/element-web#19259 and vector-im/element-web#19259. + * Fix space hierarchy pagination ([\#6908](https://github.com/matrix-org/matrix-react-sdk/pull/6908)). Fixes vector-im/element-web#19276 and vector-im/element-web#19276. + * Fix spaces keyboard shortcuts not working for last space ([\#6909](https://github.com/matrix-org/matrix-react-sdk/pull/6909)). Fixes vector-im/element-web#19255 and vector-im/element-web#19255. + * Use fallback avatar only for DMs with 2 people. ([\#6895](https://github.com/matrix-org/matrix-react-sdk/pull/6895)). Fixes vector-im/element-web#18747 and vector-im/element-web#18747. Contributed by [andybalaam](https://github.com/andybalaam). + Changes in [3.33.0-rc.2](https://github.com/vector-im/element-desktop/releases/tag/v3.33.0-rc.2) (2021-10-20) ============================================================================================================= From 914188796eb91de39ae75811b1af56fc5ec03bcd Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Mon, 25 Oct 2021 11:30:24 +0100 Subject: [PATCH 009/268] v3.33.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bb1b5e0d0f..5d63e0a8b2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.33.0-rc.2", + "version": "3.33.0", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { From d0bb6e0657650bebfd90791d47443d80313ed551 Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Fri, 29 Oct 2021 09:11:55 +0100 Subject: [PATCH 010/268] Unit tests for getEffectiveTheme (#7051) * Unit tests for getEffectiveTheme * Unit tests for choosing high-contrast theme --- test/settings/watchers/ThemeWatcher-test.tsx | 198 +++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 test/settings/watchers/ThemeWatcher-test.tsx diff --git a/test/settings/watchers/ThemeWatcher-test.tsx b/test/settings/watchers/ThemeWatcher-test.tsx new file mode 100644 index 0000000000..c97ba13a33 --- /dev/null +++ b/test/settings/watchers/ThemeWatcher-test.tsx @@ -0,0 +1,198 @@ +/* +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 SettingsStore from '../../../src/settings/SettingsStore'; +import ThemeWatcher from '../../../src/settings/watchers/ThemeWatcher'; +import { SettingLevel } from '../../../src/settings/SettingLevel'; + +function makeMatchMedia(values: any) { + class FakeMediaQueryList { + matches: false; + media: null; + onchange: null; + addListener() {} + removeListener() {} + addEventListener() {} + removeEventListener() {} + dispatchEvent() { return true; } + + constructor(query: string) { + this.matches = values[query]; + } + } + + return function matchMedia(query: string) { + return new FakeMediaQueryList(query); + }; +} + +function makeGetValue(values: any) { + return function getValue( + settingName: string, + _roomId: string = null, + _excludeDefault = false, + ): T { + return values[settingName]; + }; +} + +function makeGetValueAt(values: any) { + return function getValueAt( + _level: SettingLevel, + settingName: string, + _roomId: string = null, + _explicit = false, + _excludeDefault = false, + ): any { + return values[settingName]; + }; +} + +describe('ThemeWatcher', function() { + it('should choose a light theme by default', () => { + // Given no system settings + global.matchMedia = makeMatchMedia({}); + + // Then getEffectiveTheme returns light + const themeWatcher = new ThemeWatcher(); + expect(themeWatcher.getEffectiveTheme()).toBe("light"); + }); + + it('should choose default theme if system settings are inconclusive', () => { + // Given no system settings but we asked to use them + global.matchMedia = makeMatchMedia({}); + SettingsStore.getValue = makeGetValue({ + "use_system_theme": true, + "theme": "light", + }); + + // Then getEffectiveTheme returns light + const themeWatcher = new ThemeWatcher(); + expect(themeWatcher.getEffectiveTheme()).toBe("light"); + }); + + it('should choose a dark theme if that is selected', () => { + // Given system says light high contrast but theme is set to dark + global.matchMedia = makeMatchMedia({ + "(prefers-contrast: more)": true, + "(prefers-color-scheme: light)": true, + }); + SettingsStore.getValueAt = makeGetValueAt({ "theme": "dark" }); + + // Then getEffectiveTheme returns dark + const themeWatcher = new ThemeWatcher(); + expect(themeWatcher.getEffectiveTheme()).toBe("dark"); + }); + + it('should choose a light theme if that is selected', () => { + // Given system settings say dark high contrast but theme set to light + global.matchMedia = makeMatchMedia({ + "(prefers-contrast: more)": true, + "(prefers-color-scheme: dark)": true, + }); + SettingsStore.getValueAt = makeGetValueAt({ "theme": "light" }); + + // Then getEffectiveTheme returns light + const themeWatcher = new ThemeWatcher(); + expect(themeWatcher.getEffectiveTheme()).toBe("light"); + }); + + it('should choose a light-high-contrast theme if that is selected', () => { + // Given system settings say dark and theme set to light-high-contrast + global.matchMedia = makeMatchMedia({ "(prefers-color-scheme: dark)": true }); + SettingsStore.getValueAt = makeGetValueAt({ "theme": "light-high-contrast" }); + + // Then getEffectiveTheme returns light-high-contrast + const themeWatcher = new ThemeWatcher(); + expect(themeWatcher.getEffectiveTheme()).toBe("light-high-contrast"); + }); + + it('should choose a light theme if system prefers it (via default)', () => { + // Given system prefers lightness, even though we did not + // click "Use system theme" or choose a theme explicitly + global.matchMedia = makeMatchMedia({ "(prefers-color-scheme: light)": true }); + SettingsStore.getValueAt = makeGetValueAt({}); + SettingsStore.getValue = makeGetValue({ "use_system_theme": true }); + + // Then getEffectiveTheme returns light + const themeWatcher = new ThemeWatcher(); + expect(themeWatcher.getEffectiveTheme()).toBe("light"); + }); + + it('should choose a dark theme if system prefers it (via default)', () => { + // Given system prefers darkness, even though we did not + // click "Use system theme" or choose a theme explicitly + global.matchMedia = makeMatchMedia({ "(prefers-color-scheme: dark)": true }); + SettingsStore.getValueAt = makeGetValueAt({}); + SettingsStore.getValue = makeGetValue({ "use_system_theme": true }); + + // Then getEffectiveTheme returns dark + const themeWatcher = new ThemeWatcher(); + expect(themeWatcher.getEffectiveTheme()).toBe("dark"); + }); + + it('should choose a light theme if system prefers it (explicit)', () => { + // Given system prefers lightness + global.matchMedia = makeMatchMedia({ "(prefers-color-scheme: light)": true }); + SettingsStore.getValueAt = makeGetValueAt({ "use_system_theme": true }); + SettingsStore.getValue = makeGetValue({ "use_system_theme": true }); + + // Then getEffectiveTheme returns light + const themeWatcher = new ThemeWatcher(); + expect(themeWatcher.getEffectiveTheme()).toBe("light"); + }); + + it('should choose a dark theme if system prefers it (explicit)', () => { + // Given system prefers darkness + global.matchMedia = makeMatchMedia({ "(prefers-color-scheme: dark)": true }); + SettingsStore.getValueAt = makeGetValueAt({ "use_system_theme": true }); + SettingsStore.getValue = makeGetValue({ "use_system_theme": true }); + + // Then getEffectiveTheme returns dark + const themeWatcher = new ThemeWatcher(); + expect(themeWatcher.getEffectiveTheme()).toBe("dark"); + }); + + it('should choose a high-contrast theme if system prefers it', () => { + // Given system prefers high contrast and light + global.matchMedia = makeMatchMedia({ + "(prefers-contrast: more)": true, + "(prefers-color-scheme: light)": true, + }); + SettingsStore.getValueAt = makeGetValueAt({ "use_system_theme": true }); + SettingsStore.getValue = makeGetValue({ "use_system_theme": true }); + + // Then getEffectiveTheme returns light-high-contrast + const themeWatcher = new ThemeWatcher(); + expect(themeWatcher.getEffectiveTheme()).toBe("light-high-contrast"); + }); + + it('should not choose a high-contrast theme if not available', () => { + // Given system prefers high contrast and dark, but we don't (yet) + // have a high-contrast dark theme + global.matchMedia = makeMatchMedia({ + "(prefers-contrast: more)": true, + "(prefers-color-scheme: dark)": true, + }); + SettingsStore.getValueAt = makeGetValueAt({ "use_system_theme": true }); + SettingsStore.getValue = makeGetValue({ "use_system_theme": true }); + + // Then getEffectiveTheme returns dark + const themeWatcher = new ThemeWatcher(); + expect(themeWatcher.getEffectiveTheme()).toBe("dark"); + }); +}); + From 3defb863b35b707cfe719b334030a6c66a252261 Mon Sep 17 00:00:00 2001 From: James Salter Date: Fri, 29 Oct 2021 09:34:25 +0100 Subject: [PATCH 011/268] Automatic error reporting (#7046) * Enable sentry global handlers if automaticErrorReporting is on * Pass the exception through on session restore error Passing the exception object itself through to the BugReportDialog means a stack trace can be correctly recorded in Sentry --- src/Lifecycle.ts | 5 +++- .../dialogs/SessionRestoreErrorDialog.tsx | 6 ++-- .../tabs/user/LabsUserSettingsTab.tsx | 1 + src/i18n/strings/en_EN.json | 1 + src/sentry.ts | 30 +++++++++++++------ src/settings/Settings.tsx | 6 ++++ 6 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/Lifecycle.ts b/src/Lifecycle.ts index d351c46180..d53bd39c95 100644 --- a/src/Lifecycle.ts +++ b/src/Lifecycle.ts @@ -59,6 +59,7 @@ import SessionRestoreErrorDialog from "./components/views/dialogs/SessionRestore import StorageEvictedDialog from "./components/views/dialogs/StorageEvictedDialog"; import { logger } from "matrix-js-sdk/src/logger"; +import { setSentryUser } from "./sentry"; const HOMESERVER_URL_KEY = "mx_hs_url"; const ID_SERVER_URL_KEY = "mx_is_url"; @@ -455,7 +456,7 @@ async function handleLoadSessionFailure(e: Error): Promise { logger.error("Unable to load session", e); const modal = Modal.createTrackedDialog('Session Restore Error', '', SessionRestoreErrorDialog, { - error: e.message, + error: e, }); const [success] = await modal.finished; @@ -582,6 +583,8 @@ async function doSetLoggedIn( PosthogAnalytics.instance.updateAnonymityFromSettings(credentials.userId); + setSentryUser(credentials.userId); + const client = MatrixClientPeg.get(); if (credentials.freshLogin && SettingsStore.getValue("feature_dehydration")) { diff --git a/src/components/views/dialogs/SessionRestoreErrorDialog.tsx b/src/components/views/dialogs/SessionRestoreErrorDialog.tsx index b36dbf548e..d8f2b93af0 100644 --- a/src/components/views/dialogs/SessionRestoreErrorDialog.tsx +++ b/src/components/views/dialogs/SessionRestoreErrorDialog.tsx @@ -28,13 +28,15 @@ import DialogButtons from "../elements/DialogButtons"; import { IDialogProps } from "./IDialogProps"; interface IProps extends IDialogProps { - error: string; + error: Error; } @replaceableComponent("views.dialogs.SessionRestoreErrorDialog") export default class SessionRestoreErrorDialog extends React.Component { private sendBugReport = (): void => { - Modal.createTrackedDialog('Session Restore Error', 'Send Bug Report Dialog', BugReportDialog, {}); + Modal.createTrackedDialog('Session Restore Error', 'Send Bug Report Dialog', BugReportDialog, { + error: this.props.error, + }); }; private onClearStorageClick = (): void => { diff --git a/src/components/views/settings/tabs/user/LabsUserSettingsTab.tsx b/src/components/views/settings/tabs/user/LabsUserSettingsTab.tsx index d05a11483c..4b8c8f8c40 100644 --- a/src/components/views/settings/tabs/user/LabsUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/LabsUserSettingsTab.tsx @@ -92,6 +92,7 @@ export default class LabsUserSettingsTab extends React.Component<{}, IState> { + { hiddenReadReceipts } ; } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 1fffc04696..178d3214fb 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -890,6 +890,7 @@ "Display Communities instead of Spaces": "Display Communities instead of Spaces", "Temporarily show communities instead of Spaces for this session. Support for this will be removed in the near future. This will reload Element.": "Temporarily show communities instead of Spaces for this session. Support for this will be removed in the near future. This will reload Element.", "Developer mode": "Developer mode", + "Automatically send debug logs on any error": "Automatically send debug logs on any error", "Collecting app version information": "Collecting app version information", "Collecting logs": "Collecting logs", "Uploading logs": "Uploading logs", diff --git a/src/sentry.ts b/src/sentry.ts index 88abada17a..db90d9c3fa 100644 --- a/src/sentry.ts +++ b/src/sentry.ts @@ -192,6 +192,11 @@ export async function sendSentryReport(userText: string, issueUrl: string, error } } +export function setSentryUser(mxid: string): void { + if (!SdkConfig.get().sentry || !SettingsStore.getValue("automaticErrorReporting")) return; + Sentry.setUser({ username: mxid }); +} + interface ISentryConfig { dsn: string; environment?: string; @@ -199,21 +204,28 @@ interface ISentryConfig { export async function initSentry(sentryConfig: ISentryConfig): Promise { if (!sentryConfig) return; + // Only enable Integrations.GlobalHandlers, which hooks uncaught exceptions, if automaticErrorReporting is true + const integrations = [ + new Sentry.Integrations.InboundFilters(), + new Sentry.Integrations.FunctionToString(), + new Sentry.Integrations.Breadcrumbs(), + new Sentry.Integrations.UserAgent(), + new Sentry.Integrations.Dedupe(), + ]; + + if (SettingsStore.getValue("automaticErrorReporting")) { + integrations.push(new Sentry.Integrations.GlobalHandlers( + { onerror: false, onunhandledrejection: true })); + integrations.push(new Sentry.Integrations.TryCatch()); + } + Sentry.init({ dsn: sentryConfig.dsn, release: process.env.VERSION, environment: sentryConfig.environment, defaultIntegrations: false, autoSessionTracking: false, - integrations: [ - // specifically disable Integrations.GlobalHandlers, which hooks uncaught exceptions - we don't - // want to capture those at this stage, just explicit rageshakes - new Sentry.Integrations.InboundFilters(), - new Sentry.Integrations.FunctionToString(), - new Sentry.Integrations.Breadcrumbs(), - new Sentry.Integrations.UserAgent(), - new Sentry.Integrations.Dedupe(), - ], + integrations, // Set to 1.0 which is reasonable if we're only submitting Rageshakes; will need to be set < 1.0 // if we collect more frequently. tracesSampleRate: 1.0, diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index ab3b164517..5adfeb4545 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -762,6 +762,12 @@ export const SETTINGS: {[setting: string]: ISetting} = { supportedLevels: LEVELS_ACCOUNT_SETTINGS, default: false, }, + "automaticErrorReporting": { + displayName: _td("Automatically send debug logs on any error"), + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + default: false, + controller: new ReloadOnChangeController(), + }, [UIFeature.RoomHistorySettings]: { supportedLevels: LEVELS_UI_FEATURE, default: true, From 98edc467e06d36c05f51c090a463e7bcb2534283 Mon Sep 17 00:00:00 2001 From: Nolan Darilek Date: Fri, 29 Oct 2021 07:30:05 -0500 Subject: [PATCH 012/268] Give each room directory entry the `listitem` role to correspond with the containing `list`. (#7035) --- res/css/structures/_RoomDirectory.scss | 9 ++++++-- src/components/structures/RoomDirectory.tsx | 23 ++++++++++----------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/res/css/structures/_RoomDirectory.scss b/res/css/structures/_RoomDirectory.scss index b6219da9e4..0137db7ebf 100644 --- a/res/css/structures/_RoomDirectory.scss +++ b/res/css/structures/_RoomDirectory.scss @@ -121,7 +121,7 @@ limitations under the License. vertical-align: text-top; margin-right: 2px; content: ""; - mask: url('$(res)/img/feather-customised/user.svg'); + mask: url("$(res)/img/feather-customised/user.svg"); mask-repeat: no-repeat; mask-position: center; // scale it down and make the size slightly bigger (16 instead of 14px) @@ -132,7 +132,8 @@ limitations under the License. } } -.mx_RoomDirectory_join, .mx_RoomDirectory_preview { +.mx_RoomDirectory_join, +.mx_RoomDirectory_preview { align-self: center; white-space: nowrap; } @@ -220,3 +221,7 @@ limitations under the License. margin-top: 5px; } } + +.mx_RoomDirectory_listItem { + display: contents; +} diff --git a/src/components/structures/RoomDirectory.tsx b/src/components/structures/RoomDirectory.tsx index 56e6b2dfb8..9c5a97578c 100644 --- a/src/components/structures/RoomDirectory.tsx +++ b/src/components/structures/RoomDirectory.tsx @@ -589,9 +589,12 @@ export default class RoomDirectory extends React.Component { if (room.avatar_url) avatarUrl = mediaFromMxc(room.avatar_url).getSquareThumbnailHttp(32); // We use onMouseDown instead of onClick, so that we can avoid text getting selected - return [ + return
this.onRoomClicked(room, ev)} className="mx_RoomDirectory_roomAvatar" > @@ -603,9 +606,8 @@ export default class RoomDirectory extends React.Component { idName={name} url={avatarUrl} /> -
, +
this.onRoomClicked(room, ev)} className="mx_RoomDirectory_roomDescription" > @@ -626,30 +628,27 @@ export default class RoomDirectory extends React.Component { > { getDisplayAliasForRoom(room) }
- , +
this.onRoomClicked(room, ev)} className="mx_RoomDirectory_roomMemberCount" > { room.num_joined_members } -
, +
this.onRoomClicked(room, ev)} // cancel onMouseDown otherwise shift-clicking highlights text className="mx_RoomDirectory_preview" > { previewButton } -
, +
this.onRoomClicked(room, ev)} className="mx_RoomDirectory_join" > { joinOrViewButton } -
, - ]; + + ; } private stringLooksLikeId(s: string, fieldType: IFieldType) { From ea54ea89d45eb7c24039e1e5711e338999c79c5a Mon Sep 17 00:00:00 2001 From: Nolan Darilek Date: Fri, 29 Oct 2021 10:20:10 -0500 Subject: [PATCH 013/268] Make message separator more accessible. (#7056) --- src/components/views/messages/DateSeparator.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/messages/DateSeparator.tsx b/src/components/views/messages/DateSeparator.tsx index d66fcbf118..b20319e800 100644 --- a/src/components/views/messages/DateSeparator.tsx +++ b/src/components/views/messages/DateSeparator.tsx @@ -65,9 +65,9 @@ export default class DateSeparator extends React.Component { render() { // ARIA treats
s as separators, here we abuse them slightly so manually treat this entire thing as one // tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers - return

+ return


-
{ this.getLabel() }
+

; } From d88b8efd199eb302df084d820cfaaca0ea6e2d72 Mon Sep 17 00:00:00 2001 From: Faye Duxovni Date: Fri, 29 Oct 2021 18:11:39 -0400 Subject: [PATCH 014/268] Improve device list in Security & Privacy settings (#7004) Overhaul the device list in the "Security and Privacy" settings tab to include device trust status, provide buttons for verifying unverified devices, and improve overall usability and style. This should now be the primary interface for checking and changing the trust status of your own devices, rather than looking at your own user profile in the right panel. --- res/css/views/elements/_AccessibleButton.scss | 40 +++ res/css/views/settings/_DevicesPanel.scss | 81 ++++-- .../structures/auth/SetupEncryptionBody.tsx | 12 +- .../views/settings/DevicesPanel.tsx | 240 +++++++++++++++--- .../views/settings/DevicesPanelEntry.tsx | 180 ++++++++++--- .../tabs/user/SecurityUserSettingsTab.tsx | 16 +- src/i18n/strings/en_EN.json | 43 ++-- src/toasts/BulkUnverifiedSessionsToast.ts | 7 +- 8 files changed, 489 insertions(+), 130 deletions(-) diff --git a/res/css/views/elements/_AccessibleButton.scss b/res/css/views/elements/_AccessibleButton.scss index 7bc47a3c98..bb006b16da 100644 --- a/res/css/views/elements/_AccessibleButton.scss +++ b/res/css/views/elements/_AccessibleButton.scss @@ -115,3 +115,43 @@ limitations under the License. .mx_AccessibleButton_kind_link_sm.mx_AccessibleButton_disabled { opacity: 0.4; } + +.mx_AccessibleButton_hasKind.mx_AccessibleButton_kind_confirm_sm { + background-color: $button-primary-bg-color; + + &::before { + mask-image: url('$(res)/img/feather-customised/check.svg'); + } +} + +.mx_AccessibleButton_hasKind.mx_AccessibleButton_kind_cancel_sm { + background-color: $button-danger-bg-color; + + &::before { + mask-image: url('$(res)/img/feather-customised/x.svg'); + } +} + +.mx_AccessibleButton_kind_confirm_sm, +.mx_AccessibleButton_kind_cancel_sm { + padding: 0px; + width: 16px; + height: 16px; + border-radius: 100%; + position: relative; + display: block; + + &::before { + content: ""; + display: block; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + background-color: #ffffff; + mask-repeat: no-repeat; + mask-position: center; + mask-size: 80%; + } +} diff --git a/res/css/views/settings/_DevicesPanel.scss b/res/css/views/settings/_DevicesPanel.scss index 7d6db7bc96..1732688cc8 100644 --- a/res/css/views/settings/_DevicesPanel.scss +++ b/res/css/views/settings/_DevicesPanel.scss @@ -15,42 +15,81 @@ limitations under the License. */ .mx_DevicesPanel { - table-layout: fixed; - // Normally the panel is 880px, however this can easily overflow the container. - // TODO: Fix the table to not be squishy width: auto; max-width: 880px; - border-spacing: 10px; + + hr { + opacity: 0.2; + border: none; + border-bottom: 1px solid $primary-content; + } } .mx_DevicesPanel_header { - font-weight: bold; + display: flex; + align-items: center; + margin-block: 10px; + + .mx_DevicesPanel_header_title { + font-size: $font-18px; + font-weight: 600; + color: $primary-content; + } + + .mx_DevicesPanel_selectButton { + padding-top: 9px; + } + + .mx_E2EIcon { + width: 24px; + height: 24px; + margin-left: 0; + margin-right: 5px; + } } -.mx_DevicesPanel_header .mx_DevicesPanel_deviceButtons { - height: 48px; // make this tall so the table doesn't move down when the delete button appears - width: 20%; +.mx_DevicesPanel_deleteButton { + margin-top: 10px; } -.mx_DevicesPanel_header th { - padding: 0px; - text-align: left; - vertical-align: middle; +.mx_DevicesPanel_device { + display: flex; + align-items: flex-start; + margin-block: 10px; + min-height: 35px; } -.mx_DevicesPanel_header .mx_DevicesPanel_deviceName { - width: 50%; +.mx_DevicesPanel_icon, .mx_DevicesPanel_checkbox { + margin-left: 9px; + margin-top: 2px; } -.mx_DevicesPanel_header .mx_DevicesPanel_deviceLastSeen { - width: 30%; +.mx_DevicesPanel_deviceInfo { + flex-grow: 1; } -.mx_DevicesPanel_device td { - vertical-align: baseline; - padding: 0px; +.mx_DevicesPanel_deviceName { + color: $primary-content; } -.mx_DevicesPanel_myDevice { - font-weight: bold; +.mx_DevicesPanel_lastSeen { + font-size: $font-12px; +} + +.mx_DevicesPanel_deviceButtons { + flex-shrink: 0; + display: flex; + align-items: center; + gap: 9px; +} + +.mx_DevicesPanel_renameForm { + display: flex; + align-items: center; + gap: 5px; + + .mx_Field_input { + width: 240px; + margin: 0; + } } diff --git a/src/components/structures/auth/SetupEncryptionBody.tsx b/src/components/structures/auth/SetupEncryptionBody.tsx index e2b1aebcfd..32cd1d83ce 100644 --- a/src/components/structures/auth/SetupEncryptionBody.tsx +++ b/src/components/structures/auth/SetupEncryptionBody.tsx @@ -39,7 +39,7 @@ function keyHasPassphrase(keyInfo: ISecretStorageKeyInfo): boolean { } interface IProps { - onFinished: (boolean) => void; + onFinished: () => void; } interface IState { @@ -70,7 +70,7 @@ export default class SetupEncryptionBody extends React.Component private onStoreUpdate = () => { const store = SetupEncryptionStore.sharedInstance(); if (store.phase === Phase.Finished) { - this.props.onFinished(true); + this.props.onFinished(); return; } this.setState({ @@ -97,13 +97,16 @@ export default class SetupEncryptionBody extends React.Component const userId = cli.getUserId(); const requestPromise = cli.requestVerification(userId); - this.props.onFinished(true); + // We need to call onFinished now to close this dialog, and + // again later to signal that the verification is complete. + this.props.onFinished(); Modal.createTrackedDialog('New Session Verification', 'Starting dialog', VerificationRequestDialog, { verificationRequestPromise: requestPromise, member: cli.getUser(userId), onFinished: async () => { const request = await requestPromise; request.cancel(); + this.props.onFinished(); }, }); }; @@ -125,6 +128,7 @@ export default class SetupEncryptionBody extends React.Component }; private onResetConfirmClick = () => { + this.props.onFinished(); const store = SetupEncryptionStore.sharedInstance(); store.resetConfirm(); }; @@ -140,7 +144,7 @@ export default class SetupEncryptionBody extends React.Component }; private onEncryptionPanelClose = () => { - this.props.onFinished(false); + this.props.onFinished(); }; public render() { diff --git a/src/components/views/settings/DevicesPanel.tsx b/src/components/views/settings/DevicesPanel.tsx index c2dc924694..957583ab9c 100644 --- a/src/components/views/settings/DevicesPanel.tsx +++ b/src/components/views/settings/DevicesPanel.tsx @@ -27,6 +27,7 @@ import InteractiveAuthDialog from "../dialogs/InteractiveAuthDialog"; import DevicesPanelEntry from "./DevicesPanelEntry"; import Spinner from "../elements/Spinner"; import AccessibleButton from "../elements/AccessibleButton"; +import { CrossSigningInfo } from "matrix-js-sdk/src/crypto/CrossSigning"; import { logger } from "matrix-js-sdk/src/logger"; @@ -36,6 +37,7 @@ interface IProps { interface IState { devices: IMyDevice[]; + crossSigningInfo?: CrossSigningInfo; deviceLoadError?: string; selectedDevices: string[]; deleting?: boolean; @@ -51,6 +53,7 @@ export default class DevicesPanel extends React.Component { devices: [], selectedDevices: [], }; + this.loadDevices = this.loadDevices.bind(this); } public componentDidMount(): void { @@ -62,20 +65,34 @@ export default class DevicesPanel extends React.Component { } private loadDevices(): void { - MatrixClientPeg.get().getDevices().then( + const cli = MatrixClientPeg.get(); + cli.getDevices().then( (resp) => { if (this.unmounted) { return; } - this.setState({ devices: resp.devices || [] }); + + const crossSigningInfo = cli.getStoredCrossSigningForUser(cli.getUserId()); + this.setState((state, props) => { + const deviceIds = resp.devices.map((device) => device.device_id); + const selectedDevices = state.selectedDevices.filter( + (deviceId) => deviceIds.includes(deviceId), + ); + return { + devices: resp.devices || [], + selectedDevices, + crossSigningInfo: crossSigningInfo, + }; + }); + console.log(this.state); }, (error) => { if (this.unmounted) { return; } let errtxt; if (error.httpStatus == 404) { // 404 probably means the HS doesn't yet support the API. - errtxt = _t("Your homeserver does not support session management."); + errtxt = _t("Your homeserver does not support device management."); } else { logger.error("Error loading sessions:", error); - errtxt = _t("Unable to load session list"); + errtxt = _t("Unable to load device list"); } this.setState({ deviceLoadError: errtxt }); }, @@ -98,6 +115,22 @@ export default class DevicesPanel extends React.Component { return (idA < idB) ? -1 : (idA > idB) ? 1 : 0; } + private isDeviceVerified(device: IMyDevice): boolean | null { + try { + const cli = MatrixClientPeg.get(); + const deviceInfo = cli.getStoredDevice(cli.getUserId(), device.device_id); + return this.state.crossSigningInfo.checkDeviceTrust( + this.state.crossSigningInfo, + deviceInfo, + false, + true, + ).isCrossSigningVerified(); + } catch (e) { + console.error("Error getting device cross-signing info", e); + return null; + } + } + private onDeviceSelectionToggled = (device: IMyDevice): void => { if (this.unmounted) { return; } @@ -117,7 +150,40 @@ export default class DevicesPanel extends React.Component { }); }; + private selectAll = (devices: IMyDevice[]): void => { + this.setState((state, props) => { + const selectedDevices = state.selectedDevices.slice(); + + for (const device of devices) { + const deviceId = device.device_id; + if (!selectedDevices.includes(deviceId)) { + selectedDevices.push(deviceId); + } + } + + return { selectedDevices }; + }); + }; + + private deselectAll = (devices: IMyDevice[]): void => { + this.setState((state, props) => { + const selectedDevices = state.selectedDevices.slice(); + + for (const device of devices) { + const deviceId = device.device_id; + const i = selectedDevices.indexOf(deviceId); + if (i !== -1) { + selectedDevices.splice(i, 1); + } + } + + return { selectedDevices }; + }); + }; + private onDeleteClick = (): void => { + if (this.state.selectedDevices.length === 0) { return; } + this.setState({ deleting: true, }); @@ -135,18 +201,18 @@ export default class DevicesPanel extends React.Component { const dialogAesthetics = { [SSOAuthEntry.PHASE_PREAUTH]: { title: _t("Use Single Sign On to continue"), - body: _t("Confirm deleting these sessions by using Single Sign On to prove your identity.", { + body: _t("Confirm logging out these devices by using Single Sign On to prove your identity.", { count: numDevices, }), continueText: _t("Single Sign On"), continueKind: "primary", }, [SSOAuthEntry.PHASE_POSTAUTH]: { - title: _t("Confirm deleting these sessions"), - body: _t("Click the button below to confirm deleting these sessions.", { + title: _t("Confirm signing out these devices"), + body: _t("Click the button below to confirm signing out these devices.", { count: numDevices, }), - continueText: _t("Delete sessions", { count: numDevices }), + continueText: _t("Sign out devices", { count: numDevices }), continueKind: "danger", }, }; @@ -174,34 +240,46 @@ export default class DevicesPanel extends React.Component { private makeDeleteRequest(auth?: any): Promise { return MatrixClientPeg.get().deleteMultipleDevices(this.state.selectedDevices, auth).then( () => { - // Remove the deleted devices from `devices`, reset selection to [] + // Reset selection to [], update device list this.setState({ - devices: this.state.devices.filter( - (d) => !this.state.selectedDevices.includes(d.device_id), - ), selectedDevices: [], }); + this.loadDevices(); }, ); } private renderDevice = (device: IMyDevice): JSX.Element => { + const myDeviceId = MatrixClientPeg.get().getDeviceId(); + const myDevice = this.state.devices.find((device) => (device.device_id === myDeviceId)); + + const isOwnDevice = device.device_id === myDeviceId; + + // If our own device is unverified, it can't verify other + // devices, it can only request verification for itself + const canBeVerified = (myDevice && this.isDeviceVerified(myDevice)) || isOwnDevice; + return ; }; public render(): JSX.Element { + const loadError = ( +
+ { this.state.deviceLoadError } +
+ ); + if (this.state.deviceLoadError !== undefined) { - const classes = classNames(this.props.className, "error"); - return ( -
- { this.state.deviceLoadError } -
- ); + return loadError; } const devices = this.state.devices; @@ -210,31 +288,121 @@ export default class DevicesPanel extends React.Component { return ; } - devices.sort(this.deviceCompare); + const myDeviceId = MatrixClientPeg.get().getDeviceId(); + const myDevice = devices.find((device) => (device.device_id === myDeviceId)); + if (!myDevice) { + return loadError; + } + + const otherDevices = devices.filter((device) => (device.device_id !== myDeviceId)); + otherDevices.sort(this.deviceCompare); + + const verifiedDevices = []; + const unverifiedDevices = []; + const nonCryptoDevices = []; + for (const device of otherDevices) { + const verified = this.isDeviceVerified(device); + if (verified === true) { + verifiedDevices.push(device); + } else if (verified === false) { + unverifiedDevices.push(device); + } else { + nonCryptoDevices.push(device); + } + } + + const section = (trustIcon: JSX.Element, title: string, deviceList: IMyDevice[]): JSX.Element => { + if (deviceList.length === 0) { + return ; + } + + let selectButton: JSX.Element; + if (deviceList.length > 1) { + const anySelected = deviceList.some((device) => this.state.selectedDevices.includes(device.device_id)); + const buttonAction = anySelected ? + () => { this.deselectAll(deviceList); } : + () => { this.selectAll(deviceList); }; + const buttonText = anySelected ? _t("Deselect all") : _t("Select all"); + selectButton =
+ + { buttonText } + +
; + } + + return +
+
+
+ { trustIcon } +
+
+ { title } +
+ { selectButton } +
+ { deviceList.map(this.renderDevice) } +
; + }; + + const verifiedDevicesSection = section( + , + _t("Verified devices"), + verifiedDevices, + ); + + const unverifiedDevicesSection = section( + , + _t("Unverified devices"), + unverifiedDevices, + ); + + const nonCryptoDevicesSection = section( + , + _t("Devices without encryption support"), + nonCryptoDevices, + ); const deleteButton = this.state.deleting ? : - - { _t("Delete %(count)s sessions", { count: this.state.selectedDevices.length }) } + + { _t("Sign out %(count)s selected devices", { count: this.state.selectedDevices.length }) } ; + const otherDevicesSection = (otherDevices.length > 0) ? + + { verifiedDevicesSection } + { unverifiedDevicesSection } + { nonCryptoDevicesSection } + { deleteButton } + : + +
+
+ { _t("You aren't signed into any other devices.") } +
+
; + const classes = classNames(this.props.className, "mx_DevicesPanel"); return ( - - - - - - - - - - - { devices.map(this.renderDevice) } - -
{ _t("ID") }{ _t("Public Name") }{ _t("Last seen") } - { this.state.selectedDevices.length > 0 ? deleteButton : null } -
+
+
+
+ { _t("This device") } +
+
+ { this.renderDevice(myDevice) } + { otherDevicesSection } +
); } } diff --git a/src/components/views/settings/DevicesPanelEntry.tsx b/src/components/views/settings/DevicesPanelEntry.tsx index 6d73e1fe86..1af0bac425 100644 --- a/src/components/views/settings/DevicesPanelEntry.tsx +++ b/src/components/views/settings/DevicesPanelEntry.tsx @@ -22,34 +22,98 @@ import { MatrixClientPeg } from '../../../MatrixClientPeg'; import { formatDate } from '../../../DateUtils'; import StyledCheckbox from '../elements/StyledCheckbox'; import { replaceableComponent } from "../../../utils/replaceableComponent"; -import EditableTextContainer from "../elements/EditableTextContainer"; +import AccessibleButton from "../elements/AccessibleButton"; +import Field from "../elements/Field"; +import TextWithTooltip from "../elements/TextWithTooltip"; +import Modal from "../../../Modal"; +import SetupEncryptionDialog from '../dialogs/security/SetupEncryptionDialog'; +import VerificationRequestDialog from '../../views/dialogs/VerificationRequestDialog'; +import LogoutDialog from '../dialogs/LogoutDialog'; import { logger } from "matrix-js-sdk/src/logger"; interface IProps { - device?: IMyDevice; - onDeviceToggled?: (device: IMyDevice) => void; - selected?: boolean; + device: IMyDevice; + isOwnDevice: boolean; + verified: boolean | null; + canBeVerified: boolean; + onDeviceChange: () => void; + onDeviceToggled: (device: IMyDevice) => void; + selected: boolean; +} + +interface IState { + renaming: boolean; + displayName: string; } @replaceableComponent("views.settings.DevicesPanelEntry") -export default class DevicesPanelEntry extends React.Component { - public static defaultProps = { - onDeviceToggled: () => {}, +export default class DevicesPanelEntry extends React.Component { + constructor(props: IProps) { + super(props); + this.state = { + renaming: false, + displayName: props.device.display_name, + }; + } + + private onDeviceToggled = (): void => { + this.props.onDeviceToggled(this.props.device); }; - private onDisplayNameChanged = (value: string): Promise<{}> => { - const device = this.props.device; - return MatrixClientPeg.get().setDeviceDetails(device.device_id, { - display_name: value, + private onRename = (): void => { + this.setState({ renaming: true }); + }; + + private onChangeDisplayName = (ev: React.ChangeEvent): void => { + this.setState({ + displayName: ev.target.value, + }); + }; + + private onRenameSubmit = async () => { + this.setState({ renaming: false }); + await MatrixClientPeg.get().setDeviceDetails(this.props.device.device_id, { + display_name: this.state.displayName, }).catch((e) => { logger.error("Error setting session display name", e); throw new Error(_t("Failed to set display name")); }); + this.props.onDeviceChange(); }; - private onDeviceToggled = (): void => { - this.props.onDeviceToggled(this.props.device); + private onRenameCancel = (): void => { + this.setState({ renaming: false }); + }; + + private onOwnDeviceSignOut = (): void => { + Modal.createTrackedDialog('Logout from device list', '', LogoutDialog, + /* props= */{}, /* className= */null, + /* isPriority= */false, /* isStatic= */true); + }; + + private verify = async () => { + if (this.props.isOwnDevice) { + Modal.createTrackedDialog("Verify session", "Verify session", SetupEncryptionDialog, { + onFinished: this.props.onDeviceChange, + }); + } else { + const cli = MatrixClientPeg.get(); + const userId = cli.getUserId(); + const verificationRequestPromise = cli.requestVerification( + userId, + [this.props.device.device_id], + ); + Modal.createTrackedDialog('New Session Verification', 'Starting dialog', VerificationRequestDialog, { + verificationRequestPromise, + member: cli.getUser(userId), + onFinished: async () => { + const request = await verificationRequestPromise; + request.cancel(); + this.props.onDeviceChange(); + }, + }); + } }; public render(): JSX.Element { @@ -57,34 +121,78 @@ export default class DevicesPanelEntry extends React.Component { let lastSeen = ""; if (device.last_seen_ts) { - const lastSeenDate = formatDate(new Date(device.last_seen_ts)); - lastSeen = device.last_seen_ip + " @ " + - lastSeenDate.toLocaleString(); + const lastSeenDate = new Date(device.last_seen_ts); + lastSeen = _t("Last seen %(date)s at %(ip)s", { + date: formatDate(lastSeenDate), + ip: device.last_seen_ip, + }); } - let myDeviceClass = ''; - if (device.device_id === MatrixClientPeg.get().getDeviceId()) { - myDeviceClass = " mx_DevicesPanel_myDevice"; + const myDeviceClass = this.props.isOwnDevice ? " mx_DevicesPanel_myDevice" : ''; + + let iconClass = ''; + let verifyButton: JSX.Element; + if (this.props.verified !== null) { + iconClass = this.props.verified ? "mx_E2EIcon_verified" : "mx_E2EIcon_warning"; + if (!this.props.verified && this.props.canBeVerified) { + verifyButton = + { _t("Verify") } + ; + } } + let signOutButton: JSX.Element; + if (this.props.isOwnDevice) { + signOutButton = + { _t("Sign Out") } + ; + } + + const left = this.props.isOwnDevice ? +
+ +
: +
+ +
; + + const buttons = this.state.renaming ? +
+ + + + : + + { signOutButton } + { verifyButton } + + { _t("Rename") } + + ; + return ( - - - { device.device_id } - - - - - - { lastSeen } - - - - - +
+ { left } +
+
+ + { device.display_name } + +
+
+ { lastSeen } +
+
+
+ { buttons } +
+
); } } diff --git a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.tsx b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.tsx index b9753d9c86..d2ab697e8f 100644 --- a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.tsx @@ -326,23 +326,15 @@ export default class SecurityUserSettingsTab extends React.Component { warning } -
{ _t("Where you’re logged in") }
+
{ _t("Where you’re signed in") }
{ _t( - "Manage the names of and sign out of your sessions below or " + - "verify them in your User Profile.", {}, - { - a: sub => - { sub } - , - }, + "Manage your signed-in devices below. " + + "A device's name is visible to people you communicate with.", ) } -
- { _t("A session's public name is visible to people you communicate with") } - -
+
{ _t("Encryption") }
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 178d3214fb..a76796b93f 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1138,22 +1138,30 @@ "Cryptography": "Cryptography", "Session ID:": "Session ID:", "Session key:": "Session key:", - "Your homeserver does not support session management.": "Your homeserver does not support session management.", - "Unable to load session list": "Unable to load session list", - "Confirm deleting these sessions by using Single Sign On to prove your identity.|other": "Confirm deleting these sessions by using Single Sign On to prove your identity.", - "Confirm deleting these sessions by using Single Sign On to prove your identity.|one": "Confirm deleting this session by using Single Sign On to prove your identity.", - "Confirm deleting these sessions": "Confirm deleting these sessions", - "Click the button below to confirm deleting these sessions.|other": "Click the button below to confirm deleting these sessions.", - "Click the button below to confirm deleting these sessions.|one": "Click the button below to confirm deleting this session.", - "Delete sessions|other": "Delete sessions", - "Delete sessions|one": "Delete session", + "Your homeserver does not support device management.": "Your homeserver does not support device management.", + "Unable to load device list": "Unable to load device list", + "Confirm logging out these devices by using Single Sign On to prove your identity.|other": "Confirm logging out these devices by using Single Sign On to prove your identity.", + "Confirm logging out these devices by using Single Sign On to prove your identity.|one": "Confirm logging out this device by using Single Sign On to prove your identity.", + "Confirm signing out these devices": "Confirm signing out these devices", + "Click the button below to confirm signing out these devices.|other": "Click the button below to confirm signing out these devices.", + "Click the button below to confirm signing out these devices.|one": "Click the button below to confirm signing out this device.", + "Sign out devices|other": "Sign out devices", + "Sign out devices|one": "Sign out device", "Authentication": "Authentication", - "Delete %(count)s sessions|other": "Delete %(count)s sessions", - "Delete %(count)s sessions|one": "Delete %(count)s session", - "ID": "ID", - "Public Name": "Public Name", - "Last seen": "Last seen", + "Deselect all": "Deselect all", + "Select all": "Select all", + "Verified devices": "Verified devices", + "Unverified devices": "Unverified devices", + "Devices without encryption support": "Devices without encryption support", + "Sign out %(count)s selected devices|other": "Sign out %(count)s selected devices", + "Sign out %(count)s selected devices|one": "Sign out %(count)s selected device", + "You aren't signed into any other devices.": "You aren't signed into any other devices.", + "This device": "This device", "Failed to set display name": "Failed to set display name", + "Last seen %(date)s at %(ip)s": "Last seen %(date)s at %(ip)s", + "Sign Out": "Sign Out", + "Display Name": "Display Name", + "Rename": "Rename", "Encryption": "Encryption", "Individually verify each session used by a user to mark it as trusted, not trusting cross-signed devices.": "Individually verify each session used by a user to mark it as trusted, not trusting cross-signed devices.", "Securely cache encrypted messages locally for them to appear in search results, using %(size)s to store messages from %(rooms)s rooms.|other": "Securely cache encrypted messages locally for them to appear in search results, using %(size)s to store messages from %(rooms)s rooms.", @@ -1217,7 +1225,6 @@ "The operation could not be completed": "The operation could not be completed", "Upgrade to your own domain": "Upgrade to your own domain", "Profile": "Profile", - "Display Name": "Display Name", "Profile picture": "Profile picture", "Save": "Save", "Delete Backup": "Delete Backup", @@ -1416,9 +1423,8 @@ "%(brand)s collects anonymous analytics to allow us to improve the application.": "%(brand)s collects anonymous analytics to allow us to improve the application.", "Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.": "Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.", "Learn more about how we use analytics.": "Learn more about how we use analytics.", - "Where you’re logged in": "Where you’re logged in", - "Manage the names of and sign out of your sessions below or verify them in your User Profile.": "Manage the names of and sign out of your sessions below or verify them in your User Profile.", - "A session's public name is visible to people you communicate with": "A session's public name is visible to people you communicate with", + "Where you’re signed in": "Where you’re signed in", + "Manage your signed-in devices below. A device's name is visible to people you communicate with.": "Manage your signed-in devices below. A device's name is visible to people you communicate with.", "Default Device": "Default Device", "No media permissions": "No media permissions", "You may need to manually permit %(brand)s to access your microphone/webcam": "You may need to manually permit %(brand)s to access your microphone/webcam", @@ -1859,6 +1865,7 @@ "Room settings": "Room settings", "Trusted": "Trusted", "Not trusted": "Not trusted", + "Unable to load session list": "Unable to load session list", "%(count)s verified sessions|other": "%(count)s verified sessions", "%(count)s verified sessions|one": "1 verified session", "Hide verified sessions": "Hide verified sessions", diff --git a/src/toasts/BulkUnverifiedSessionsToast.ts b/src/toasts/BulkUnverifiedSessionsToast.ts index e063f72fe0..0a35a91345 100644 --- a/src/toasts/BulkUnverifiedSessionsToast.ts +++ b/src/toasts/BulkUnverifiedSessionsToast.ts @@ -16,10 +16,11 @@ limitations under the License. import { _t } from '../languageHandler'; import dis from "../dispatcher/dispatcher"; -import { MatrixClientPeg } from '../MatrixClientPeg'; import DeviceListener from '../DeviceListener'; import GenericToast from "../components/views/toasts/GenericToast"; import ToastStore from "../stores/ToastStore"; +import { Action } from "../dispatcher/actions"; +import { UserTab } from "../components/views/dialogs/UserSettingsDialog"; const TOAST_KEY = "reviewsessions"; @@ -28,8 +29,8 @@ export const showToast = (deviceIds: Set) => { DeviceListener.sharedInstance().dismissUnverifiedSessions(deviceIds); dis.dispatch({ - action: 'view_user_info', - userId: MatrixClientPeg.get().getUserId(), + action: Action.ViewUserSettings, + initialTabId: UserTab.Security, }); }; From 71244f3b3c6f83c7fbb3e10ba2b8408bc1b3d06c Mon Sep 17 00:00:00 2001 From: Faye Duxovni Date: Fri, 29 Oct 2021 21:57:32 -0400 Subject: [PATCH 015/268] Add more checkbox styles (#7058) Add a "kind" param for StyledCheckbox, allowing designers to choose different styles of checkbox as needed. In addition to the preexisting default kind (now called Solid), there's an Outline kind with a green checkmark and a transparent fill. This is used in the device trust view, since the default checkbox style looks too much like the green "verified" shield and it's awkward to have those next to each other. --- res/css/views/elements/_StyledCheckbox.scss | 43 +++++++++++++------ .../views/elements/StyledCheckbox.tsx | 21 +++++++-- .../views/settings/DevicesPanelEntry.tsx | 3 +- .../FontScalingPanel-test.tsx.snap | 6 +-- 4 files changed, 52 insertions(+), 21 deletions(-) diff --git a/res/css/views/elements/_StyledCheckbox.scss b/res/css/views/elements/_StyledCheckbox.scss index 1467474b05..398214b9b0 100644 --- a/res/css/views/elements/_StyledCheckbox.scss +++ b/res/css/views/elements/_StyledCheckbox.scss @@ -48,22 +48,20 @@ limitations under the License. box-sizing: border-box; border-radius: $border-radius; - img { + .mx_Checkbox_checkmark { display: none; height: 100%; width: 100%; - filter: invert(100%); + mask-image: url('$(res)/img/feather-customised/check.svg'); + mask-position: center; + mask-size: 100%; + mask-repeat: no-repeat; } } - &:checked + label > .mx_Checkbox_background { - background: $accent-color; - border-color: $accent-color; - - img { - display: block; - } + &:checked + label > .mx_Checkbox_background .mx_Checkbox_checkmark { + display: block; } & + label > *:not(.mx_Checkbox_background) { @@ -75,11 +73,6 @@ limitations under the License. cursor: not-allowed; } - &:checked:disabled + label > .mx_Checkbox_background { - background-color: $accent-color; - border-color: $accent-color; - } - &.focus-visible { & + label .mx_Checkbox_background { @mixin unreal-focus; @@ -87,3 +80,25 @@ limitations under the License. } } } + +.mx_Checkbox.mx_Checkbox_kind_solid input[type=checkbox] { + & + label > .mx_Checkbox_background .mx_Checkbox_checkmark { + background: #ffffff; + } + + &:checked + label > .mx_Checkbox_background { + background: $accent-color; + border-color: $accent-color; + } +} + +.mx_Checkbox.mx_Checkbox_kind_outline input[type=checkbox] { + & + label > .mx_Checkbox_background .mx_Checkbox_checkmark { + background: $accent-color; + } + + &:checked + label > .mx_Checkbox_background { + background: transparent; + border-color: $accent-color; + } +} diff --git a/src/components/views/elements/StyledCheckbox.tsx b/src/components/views/elements/StyledCheckbox.tsx index b609f7159e..05272f515d 100644 --- a/src/components/views/elements/StyledCheckbox.tsx +++ b/src/components/views/elements/StyledCheckbox.tsx @@ -17,8 +17,15 @@ limitations under the License. import React from "react"; import { randomString } from "matrix-js-sdk/src/randomstring"; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import classnames from 'classnames'; + +export enum CheckboxStyle { + Solid = "solid", + Outline = "outline", +} interface IProps extends React.InputHTMLAttributes { + kind?: CheckboxStyle; } interface IState { @@ -40,13 +47,21 @@ export default class StyledCheckbox extends React.PureComponent public render() { /* eslint @typescript-eslint/no-unused-vars: ["error", { "ignoreRestSiblings": true }] */ - const { children, className, ...otherProps } = this.props; - return + const { children, className, kind = CheckboxStyle.Solid, ...otherProps } = this.props; + const newClassName = classnames( + "mx_Checkbox", + className, + { + "mx_Checkbox_hasKind": kind, + [`mx_Checkbox_kind_${kind}`]: kind, + }, + ); + return