From 39e9bffe1fdd3722c5279a7478f43d236b04d039 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 30 Jan 2020 20:03:26 +0000 Subject: [PATCH 1/8] Make encryption events into bubble-style tiles Factors out the verification tile stuff into encryption event styles so we can reuse them betwen all the encryption event tiles. Also makes the event list summary stuff even more gnarly by putting the encryption event tile above the group. We really need to refactor that. :/ Fixes https://github.com/vector-im/riot-web/issues/12006 --- res/css/_components.scss | 2 +- ...nRequest.scss => _common_CryptoEvent.scss} | 38 ++++++++++--------- src/components/structures/MessagePanel.js | 17 ++++++++- .../messages/MKeyVerificationConclusion.js | 10 ++--- .../views/messages/MKeyVerificationRequest.js | 16 ++++---- src/components/views/rooms/EventTile.js | 6 ++- src/i18n/strings/en_EN.json | 4 ++ 7 files changed, 58 insertions(+), 35 deletions(-) rename res/css/views/messages/{_MKeyVerificationRequest.scss => _common_CryptoEvent.scss} (61%) diff --git a/res/css/_components.scss b/res/css/_components.scss index 22c9b73dca..bc636eb3c6 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -128,7 +128,6 @@ @import "./views/messages/_MEmoteBody.scss"; @import "./views/messages/_MFileBody.scss"; @import "./views/messages/_MImageBody.scss"; -@import "./views/messages/_MKeyVerificationRequest.scss"; @import "./views/messages/_MNoticeBody.scss"; @import "./views/messages/_MStickerBody.scss"; @import "./views/messages/_MTextBody.scss"; @@ -143,6 +142,7 @@ @import "./views/messages/_TextualEvent.scss"; @import "./views/messages/_UnknownBody.scss"; @import "./views/messages/_ViewSourceEvent.scss"; +@import "./views/messages/_common_CryptoEvent.scss"; @import "./views/right_panel/_EncryptionInfo.scss"; @import "./views/right_panel/_UserInfo.scss"; @import "./views/right_panel/_VerificationPanel.scss"; diff --git a/res/css/views/messages/_MKeyVerificationRequest.scss b/res/css/views/messages/_common_CryptoEvent.scss similarity index 61% rename from res/css/views/messages/_MKeyVerificationRequest.scss rename to res/css/views/messages/_common_CryptoEvent.scss index ee20751083..98e1e97e39 100644 --- a/res/css/views/messages/_MKeyVerificationRequest.scss +++ b/res/css/views/messages/_common_CryptoEvent.scss @@ -1,5 +1,5 @@ /* -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019, 2020 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,60 +14,62 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_KeyVerification { +.mx_cryptoEvent { display: grid; grid-template-columns: 24px minmax(0, 1fr) min-content; - &.mx_KeyVerification_icon::after { + &.mx_cryptoEvent_icon::after { grid-column: 1; grid-row: 1 / 3; - width: 12px; + width: 16px; height: 16px; content: ""; - mask-image: url("$(res)/img/e2e/normal.svg"); - mask-repeat: no-repeat; - mask-size: 100%; + background-image: url("$(res)/img/e2e/normal.svg"); + background-repeat: no-repeat; + background-size: 100%; margin-top: 4px; - background-color: $primary-fg-color; } - &.mx_KeyVerification_icon_verified::after { - mask-image: url("$(res)/img/e2e/verified.svg"); - background-color: $accent-color; + &.mx_cryptoEvent_icon_verified::after { + background-image: url("$(res)/img/e2e/verified.svg"); } - .mx_KeyVerification_title, .mx_KeyVerification_subtitle, .mx_KeyVerification_state { + &.mx_cryptoEvent_icon_warning::after { + background-image: url("$(res)/img/e2e/warning.svg"); + } + + .mx_cryptoEvent_title, .mx_cryptoEvent_subtitle, .mx_cryptoEvent_state { overflow-wrap: break-word; } - .mx_KeyVerification_title { + .mx_cryptoEvent_title { font-weight: 600; font-size: 15px; grid-column: 2; grid-row: 1; } - .mx_KeyVerification_subtitle { + .mx_cryptoEvent_subtitle { grid-column: 2; grid-row: 2; } - .mx_KeyVerification_state, .mx_KeyVerification_subtitle { + .mx_cryptoEvent_state, .mx_cryptoEvent_subtitle { font-size: 12px; } - .mx_KeyVerification_state, .mx_KeyVerification_buttons { + .mx_cryptoEvent_state, .mx_cryptoEvent_buttons { grid-column: 3; grid-row: 1 / 3; } - .mx_KeyVerification_buttons { + .mx_cryptoEvent_buttons { align-items: center; display: flex; } - .mx_KeyVerification_state { + .mx_cryptoEvent_state { width: 130px; padding: 10px 20px; margin: auto 0; diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 4ad75eb700..a13278cf68 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -465,6 +465,12 @@ export default class MessagePanel extends React.Component { } return false; }; + // events that we include in the group but then eject out and place + // above the group. + const shouldEject = (ev) => { + if (ev.getType() === "m.room.encryption") return true; + return false; + }; if (mxEv.getType() === "m.room.create") { let summaryReadMarker = null; const ts1 = mxEv.getTs(); @@ -484,6 +490,7 @@ export default class MessagePanel extends React.Component { } const summarisedEvents = []; // Don't add m.room.create here as we don't want it inside the summary + const ejectedEvents = []; for (;i + 1 < this.props.events.length; i++) { const collapsedMxEv = this.props.events[i + 1]; @@ -501,7 +508,11 @@ export default class MessagePanel extends React.Component { // If RM event is in the summary, mark it as such and the RM will be appended after the summary. summaryReadMarker = summaryReadMarker || this._readMarkerForEvent(collapsedMxEv.getId()); - summarisedEvents.push(collapsedMxEv); + if (shouldEject(collapsedMxEv)) { + ejectedEvents.push(collapsedMxEv); + } else { + summarisedEvents.push(collapsedMxEv); + } } // At this point, i = the index of the last event in the summary sequence @@ -513,6 +524,10 @@ export default class MessagePanel extends React.Component { return this._getTilesForEvent(e, e, e === lastShownEvent); }).reduce((a, b) => a.concat(b), []); + for (const ejected of ejectedEvents) { + ret.push(...this._getTilesForEvent(mxEv, ejected, last)); + } + // Get sender profile from the latest event in the summary as the m.room.create doesn't contain one const ev = this.props.events[i]; ret.push( -
{title}
-
{subtitle}
+
{title}
+
{subtitle}
); } diff --git a/src/components/views/messages/MKeyVerificationRequest.js b/src/components/views/messages/MKeyVerificationRequest.js index ebe5c9adf7..ab02b2e5ad 100644 --- a/src/components/views/messages/MKeyVerificationRequest.js +++ b/src/components/views/messages/MKeyVerificationRequest.js @@ -1,5 +1,5 @@ /* -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019, 2020 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -125,30 +125,30 @@ export default class MKeyVerificationRequest extends React.Component { } else { stateLabel = this._cancelledLabel(request.cancellingUserId); } - stateNode = (
{stateLabel}
); + stateNode = (
{stateLabel}
); } if (!request.initiatedByMe) { const name = getNameForEventRoom(request.requestingUserId, mxEvent.getRoomId()); - title = (
{ + title = (
{ _t("%(name)s wants to verify", {name})}
); - subtitle = (
{ + subtitle = (
{ userLabelForEventRoom(request.requestingUserId, mxEvent.getRoomId())}
); if (request.requested && !request.observeOnly) { - stateNode = (
+ stateNode = (
); } } else { // request sent by us - title = (
{ + title = (
{ _t("You sent a verification request")}
); - subtitle = (
{ + subtitle = (
{ userLabelForEventRoom(request.receivingUserId, mxEvent.getRoomId())}
); } if (title) { - return (
+ return (
{title} {subtitle} {stateNode} diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index a8e9c7bf81..bba2310281 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -40,12 +40,14 @@ const eventTileTypes = { 'm.sticker': 'messages.MessageEvent', 'm.key.verification.cancel': 'messages.MKeyVerificationConclusion', 'm.key.verification.done': 'messages.MKeyVerificationConclusion', + 'm.room.encryption': 'messages.EncryptionEvent', 'm.call.invite': 'messages.TextualEvent', 'm.call.answer': 'messages.TextualEvent', 'm.call.hangup': 'messages.TextualEvent', }; const stateEventTileTypes = { + 'm.room.encryption': 'messages.EncryptionEvent', 'm.room.aliases': 'messages.TextualEvent', // 'm.room.aliases': 'messages.RoomAliasesEvent', // too complex 'm.room.canonical_alias': 'messages.TextualEvent', @@ -55,7 +57,6 @@ const stateEventTileTypes = { 'm.room.avatar': 'messages.RoomAvatarEvent', 'm.room.third_party_invite': 'messages.TextualEvent', 'm.room.history_visibility': 'messages.TextualEvent', - 'm.room.encryption': 'messages.TextualEvent', 'm.room.topic': 'messages.TextualEvent', 'm.room.power_levels': 'messages.TextualEvent', 'm.room.pinned_events': 'messages.TextualEvent', @@ -600,7 +601,8 @@ export default createReactClass({ // Info messages are basically information about commands processed on a room const isBubbleMessage = eventType.startsWith("m.key.verification") || - (eventType === "m.room.message" && msgtype && msgtype.startsWith("m.key.verification")); + (eventType === "m.room.message" && msgtype && msgtype.startsWith("m.key.verification")) || + (eventType === "m.room.encryption"); let isInfoMessage = ( !isBubbleMessage && eventType !== 'm.room.message' && eventType !== 'm.sticker' && eventType !== 'm.room.create' diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 6e58a76283..036d8c92f2 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1194,6 +1194,10 @@ "Today": "Today", "Yesterday": "Yesterday", "View Source": "View Source", + "Encryption enabled": "Encryption enabled", + "Messages in this room are end-to-end encrypted. Learn more & verify this user in their user profile.": "Messages in this room are end-to-end encrypted. Learn more & verify this user in their user profile.", + "Encryption not enabled": "Encryption not enabled", + "The encryption used by this room isn't supported": "The encryption used by this room isn't supported", "Error decrypting audio": "Error decrypting audio", "React": "React", "Reply": "Reply", From 5447a04f8e6f8d3a6ba85b3df180e753e4586a52 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 30 Jan 2020 20:15:15 +0000 Subject: [PATCH 2/8] oops, forgot the component --- .../views/messages/EncryptionEvent.js | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/components/views/messages/EncryptionEvent.js diff --git a/src/components/views/messages/EncryptionEvent.js b/src/components/views/messages/EncryptionEvent.js new file mode 100644 index 0000000000..b7c13c0a05 --- /dev/null +++ b/src/components/views/messages/EncryptionEvent.js @@ -0,0 +1,61 @@ +/* +Copyright 2020 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import PropTypes from 'prop-types'; +import {MatrixClientPeg} from '../../../MatrixClientPeg'; +import * as sdk from '../../../index'; +import { _t } from '../../../languageHandler'; +import {getNameForEventRoom, userLabelForEventRoom} + from '../../../utils/KeyVerificationStateObserver'; +import dis from "../../../dispatcher"; +import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases"; + +export default class EncryptionEvent extends React.Component { + render() { + const {mxEvent} = this.props; + + let body; + let classes = "mx_EventTile_bubble mx_cryptoEvent mx_cryptoEvent_icon"; + if (mxEvent.getContent().algorithm === 'm.megolm.v1.aes-sha2') { + body =
+
{_t("Encryption enabled")}
+
+ {_t( + "Messages in this room are end-to-end encrypted. " + + "Learn more & verify this user in their user profile.", + )} +
+
; + } else { + body =
+
{_t("Encryption not enabled")}
+
{_t("The encryption used by this room isn't supported")}
+
; + classes += " mx_cryptoEvent_icon_warning"; + } + + return (
+ {body} +
); + return null; + } +} + +EncryptionEvent.propTypes = { + /* the MatrixEvent to show */ + mxEvent: PropTypes.object.isRequired, +}; From 2332cdb5d8452e598675b395c96547c82dd32c45 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 30 Jan 2020 20:47:46 +0000 Subject: [PATCH 3/8] full stop Co-Authored-By: J. Ryan Stinnett --- src/components/views/messages/EncryptionEvent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/messages/EncryptionEvent.js b/src/components/views/messages/EncryptionEvent.js index b7c13c0a05..a475666e88 100644 --- a/src/components/views/messages/EncryptionEvent.js +++ b/src/components/views/messages/EncryptionEvent.js @@ -43,7 +43,7 @@ export default class EncryptionEvent extends React.Component { } else { body =
{_t("Encryption not enabled")}
-
{_t("The encryption used by this room isn't supported")}
+
{_t("The encryption used by this room isn't supported.")}
; classes += " mx_cryptoEvent_icon_warning"; } From 5760e713188738d64386a861690f44f666891cea Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 30 Jan 2020 20:50:06 +0000 Subject: [PATCH 4/8] i18n --- src/i18n/strings/en_EN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 036d8c92f2..ddafdb391b 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1197,7 +1197,7 @@ "Encryption enabled": "Encryption enabled", "Messages in this room are end-to-end encrypted. Learn more & verify this user in their user profile.": "Messages in this room are end-to-end encrypted. Learn more & verify this user in their user profile.", "Encryption not enabled": "Encryption not enabled", - "The encryption used by this room isn't supported": "The encryption used by this room isn't supported", + "The encryption used by this room isn't supported.": "The encryption used by this room isn't supported.", "Error decrypting audio": "Error decrypting audio", "React": "React", "Reply": "Reply", From f84795ebaeefe3cf44d858ca9c2ffb25094371c3 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 30 Jan 2020 20:50:12 +0000 Subject: [PATCH 5/8] Remove now unused code from TextForEvent --- src/TextForEvent.js | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/TextForEvent.js b/src/TextForEvent.js index cdfea45ad7..d4003058c8 100644 --- a/src/TextForEvent.js +++ b/src/TextForEvent.js @@ -442,23 +442,6 @@ function textForHistoryVisibilityEvent(event) { } } -function textForEncryptionEvent(event) { - const senderName = event.sender ? event.sender.name : event.getSender(); - if (event.getContent().algorithm === "m.megolm.v1.aes-sha2") { - return _t('%(senderName)s turned on end-to-end encryption.', { - senderName, - }); - } - return _t( - '%(senderName)s turned on end-to-end encryption ' + - '(unrecognised algorithm %(algorithm)s).', - { - senderName, - algorithm: event.getContent().algorithm, - }, - ); -} - // Currently will only display a change if a user's power level is changed function textForPowerEvent(event) { const senderName = event.sender ? event.sender.name : event.getSender(); @@ -636,7 +619,6 @@ const stateHandlers = { 'm.room.member': textForMemberEvent, 'm.room.third_party_invite': textForThreePidInviteEvent, 'm.room.history_visibility': textForHistoryVisibilityEvent, - 'm.room.encryption': textForEncryptionEvent, 'm.room.power_levels': textForPowerEvent, 'm.room.pinned_events': textForPinnedEvent, 'm.room.server_acl': textForServerACLEvent, From d56de6a67375ab1d9bd2517b38e8dd44d1de4f82 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 30 Jan 2020 20:52:37 +0000 Subject: [PATCH 6/8] i18n again --- src/i18n/strings/en_EN.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index ddafdb391b..29ea10728a 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -259,8 +259,6 @@ "%(senderName)s made future room history visible to all room members.": "%(senderName)s made future room history visible to all room members.", "%(senderName)s made future room history visible to anyone.": "%(senderName)s made future room history visible to anyone.", "%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s made future room history visible to unknown (%(visibility)s).", - "%(senderName)s turned on end-to-end encryption.": "%(senderName)s turned on end-to-end encryption.", - "%(senderName)s turned on end-to-end encryption (unrecognised algorithm %(algorithm)s).": "%(senderName)s turned on end-to-end encryption (unrecognised algorithm %(algorithm)s).", "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s", "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s changed the power level of %(powerLevelDiffText)s.", "%(senderName)s changed the pinned messages for the room.": "%(senderName)s changed the pinned messages for the room.", From 58d16fea7db4e0ad23da962825779cde192d3489 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 30 Jan 2020 20:59:27 +0000 Subject: [PATCH 7/8] lint --- src/components/views/messages/EncryptionEvent.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/components/views/messages/EncryptionEvent.js b/src/components/views/messages/EncryptionEvent.js index a475666e88..e161d050f1 100644 --- a/src/components/views/messages/EncryptionEvent.js +++ b/src/components/views/messages/EncryptionEvent.js @@ -16,13 +16,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; -import {MatrixClientPeg} from '../../../MatrixClientPeg'; -import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; -import {getNameForEventRoom, userLabelForEventRoom} - from '../../../utils/KeyVerificationStateObserver'; -import dis from "../../../dispatcher"; -import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases"; export default class EncryptionEvent extends React.Component { render() { @@ -51,7 +45,6 @@ export default class EncryptionEvent extends React.Component { return (
{body}
); - return null; } } From d0ab37ac50658c12a0a388f1cd20f66fe7c2fd49 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 31 Jan 2020 09:58:21 +0000 Subject: [PATCH 8/8] Only say the room is encrypted if it actually is --- src/components/views/messages/EncryptionEvent.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/views/messages/EncryptionEvent.js b/src/components/views/messages/EncryptionEvent.js index e161d050f1..ab0f3fde2e 100644 --- a/src/components/views/messages/EncryptionEvent.js +++ b/src/components/views/messages/EncryptionEvent.js @@ -17,6 +17,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; import { _t } from '../../../languageHandler'; +import { MatrixClientPeg } from '../../../MatrixClientPeg'; export default class EncryptionEvent extends React.Component { render() { @@ -24,7 +25,10 @@ export default class EncryptionEvent extends React.Component { let body; let classes = "mx_EventTile_bubble mx_cryptoEvent mx_cryptoEvent_icon"; - if (mxEvent.getContent().algorithm === 'm.megolm.v1.aes-sha2') { + if ( + mxEvent.getContent().algorithm === 'm.megolm.v1.aes-sha2' && + MatrixClientPeg.get().isRoomEncrypted(mxEvent.getRoomId()) + ) { body =
{_t("Encryption enabled")}