From e16f5115276466256c223502a04eb2ee451ca868 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Fri, 24 Apr 2020 15:07:39 +0100
Subject: [PATCH 1/9] Redesign redactions
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
res/css/_components.scss | 2 +-
.../{_UnknownBody.scss => _RedactedBody.scss} | 23 +++++++++-
res/css/views/rooms/_EventTile.scss | 28 ------------
res/img/feather-customised/trash (custom).svg | 7 +++
.../views/messages/EditHistoryMessage.js | 4 +-
src/components/views/messages/MessageEvent.js | 5 +--
.../views/messages/RedactedBody.tsx | 45 +++++++++++++++++++
src/components/views/messages/UnknownBody.js | 40 -----------------
src/i18n/strings/en_EN.json | 5 +--
9 files changed, 80 insertions(+), 79 deletions(-)
rename res/css/views/messages/{_UnknownBody.scss => _RedactedBody.scss} (52%)
create mode 100644 res/img/feather-customised/trash (custom).svg
create mode 100644 src/components/views/messages/RedactedBody.tsx
delete mode 100644 src/components/views/messages/UnknownBody.js
diff --git a/res/css/_components.scss b/res/css/_components.scss
index 0ba2b609e8..0344074369 100644
--- a/res/css/_components.scss
+++ b/res/css/_components.scss
@@ -140,10 +140,10 @@
@import "./views/messages/_ReactionsRow.scss";
@import "./views/messages/_ReactionsRowButton.scss";
@import "./views/messages/_ReactionsRowButtonTooltip.scss";
+@import "./views/messages/_RedactedBody.scss";
@import "./views/messages/_RoomAvatarEvent.scss";
@import "./views/messages/_SenderProfile.scss";
@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";
diff --git a/res/css/views/messages/_UnknownBody.scss b/res/css/views/messages/_RedactedBody.scss
similarity index 52%
rename from res/css/views/messages/_UnknownBody.scss
rename to res/css/views/messages/_RedactedBody.scss
index 9036e12bf0..c0001db5d3 100644
--- a/res/css/views/messages/_UnknownBody.scss
+++ b/res/css/views/messages/_RedactedBody.scss
@@ -1,5 +1,6 @@
/*
-Copyright 2015, 2016 OpenMarket Ltd
+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
@@ -11,6 +12,24 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-.mx_UnknownBody {
+.mx_RedactedBody {
white-space: pre-wrap;
+ color: $muted-fg-color;
+
+ padding-left: 16px;
+ position: relative;
+
+ &::before {
+ height: 14px;
+ width: 14px;
+ background-color: $muted-fg-color;
+ mask-image: url('$(res)/img/feather-customised/trash (custom).svg');
+ mask-repeat: no-repeat;
+ mask-position: center;
+ mask-size: contain;
+ content: '';
+ position: absolute;
+ top: 2px;
+ left: 0;
+ }
}
diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss
index 0dc60226b8..3f5f039005 100644
--- a/res/css/views/rooms/_EventTile.scss
+++ b/res/css/views/rooms/_EventTile.scss
@@ -242,34 +242,6 @@ limitations under the License.
color: $event-notsent-color;
}
-.mx_EventTile_redacted .mx_EventTile_line .mx_UnknownBody,
-.mx_EventTile_redacted .mx_EventTile_reply .mx_UnknownBody {
- --lozenge-color: $event-redacted-fg-color;
- --lozenge-border-color: $event-redacted-border-color;
- display: block;
- height: 22px;
- width: 250px;
- border-radius: 11px;
- background:
- repeating-linear-gradient(
- -45deg,
- var(--lozenge-color),
- var(--lozenge-color) 3px,
- transparent 3px,
- transparent 6px
- );
- box-shadow: 0px 0px 3px var(--lozenge-border-color) inset;
-}
-
-.mx_EventTile_sending.mx_EventTile_redacted .mx_UnknownBody {
- opacity: 0.4;
-}
-
-div.mx_EventTile_notSent.mx_EventTile_redacted .mx_UnknownBody {
- --lozenge-color: $event-notsent-color;
- --lozenge-border-color: $event-notsent-color;
-}
-
.mx_EventTile_contextual {
opacity: 0.4;
}
diff --git a/res/img/feather-customised/trash (custom).svg b/res/img/feather-customised/trash (custom).svg
new file mode 100644
index 0000000000..dc1985ddb2
--- /dev/null
+++ b/res/img/feather-customised/trash (custom).svg
@@ -0,0 +1,7 @@
+
diff --git a/src/components/views/messages/EditHistoryMessage.js b/src/components/views/messages/EditHistoryMessage.js
index 679a8f7471..e0ca23d244 100644
--- a/src/components/views/messages/EditHistoryMessage.js
+++ b/src/components/views/messages/EditHistoryMessage.js
@@ -26,6 +26,7 @@ import * as sdk from '../../../index';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import Modal from '../../../Modal';
import classNames from 'classnames';
+import RedactedBody from "./RedactedBody";
function getReplacedContent(event) {
const originalContent = event.getOriginalContent();
@@ -132,8 +133,7 @@ export default class EditHistoryMessage extends React.PureComponent {
const content = getReplacedContent(mxEvent);
let contentContainer;
if (mxEvent.isRedacted()) {
- const UnknownBody = sdk.getComponent('messages.UnknownBody');
- contentContainer = ;
+ contentContainer = ;
} else {
let contentElements;
if (this.props.previousEdit) {
diff --git a/src/components/views/messages/MessageEvent.js b/src/components/views/messages/MessageEvent.js
index beba986104..f8bd23cbe3 100644
--- a/src/components/views/messages/MessageEvent.js
+++ b/src/components/views/messages/MessageEvent.js
@@ -20,6 +20,7 @@ import createReactClass from 'create-react-class';
import * as sdk from '../../../index';
import SettingsStore from "../../../settings/SettingsStore";
import {Mjolnir} from "../../../mjolnir/Mjolnir";
+import RedactedBody from "./RedactedBody";
export default createReactClass({
displayName: 'MessageEvent',
@@ -61,8 +62,6 @@ export default createReactClass({
},
render: function() {
- const UnknownBody = sdk.getComponent('messages.UnknownBody');
-
const bodyTypes = {
'm.text': sdk.getComponent('messages.TextualBody'),
'm.notice': sdk.getComponent('messages.TextualBody'),
@@ -79,7 +78,7 @@ export default createReactClass({
const content = this.props.mxEvent.getContent();
const type = this.props.mxEvent.getType();
const msgtype = content.msgtype;
- let BodyType = UnknownBody;
+ let BodyType = RedactedBody;
if (!this.props.mxEvent.isRedacted()) {
// only resolve BodyType if event is not redacted
if (type && evTypes[type]) {
diff --git a/src/components/views/messages/RedactedBody.tsx b/src/components/views/messages/RedactedBody.tsx
new file mode 100644
index 0000000000..f219e3bd91
--- /dev/null
+++ b/src/components/views/messages/RedactedBody.tsx
@@ -0,0 +1,45 @@
+/*
+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, {useContext} from "react";
+import {MatrixClient} from "matrix-js-sdk/src/client";
+import {MatrixEvent} from "matrix-js-sdk/src/models/event";
+import { _t } from "../../../languageHandler";
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
+
+interface IProps {
+ mxEvent: MatrixEvent;
+}
+
+const RedactedBody = React.forwardRef(({mxEvent}, ref) => {
+ const cli: MatrixClient = useContext(MatrixClientContext);
+
+ let text = _t("Message deleted");
+ const redactedBecauseUserId = mxEvent.getUnsigned().redacted_because.sender;
+ if (redactedBecauseUserId !== cli.getUserId()) {
+ const room = cli.getRoom(mxEvent.getRoomId());
+ const sender = room && room.getMember(redactedBecauseUserId);
+ text = _t("Message deleted by %(user)s", { user: sender.name || redactedBecauseUserId });
+ }
+
+ return (
+
+ { text }
+
+ );
+});
+
+export default RedactedBody;
diff --git a/src/components/views/messages/UnknownBody.js b/src/components/views/messages/UnknownBody.js
deleted file mode 100644
index 2a19f324e8..0000000000
--- a/src/components/views/messages/UnknownBody.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
-Copyright 2015, 2016 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-import React from 'react';
-import createReactClass from 'create-react-class';
-import { _t } from '../../../languageHandler';
-
-export default createReactClass({
- displayName: 'UnknownBody',
-
- render: function() {
- let tooltip = _t("Removed or unknown message type");
- if (this.props.mxEvent.isRedacted()) {
- const redactedBecauseUserId = this.props.mxEvent.getUnsigned().redacted_because.sender;
- tooltip = redactedBecauseUserId ?
- _t("Message removed by %(userId)s", { userId: redactedBecauseUserId }) :
- _t("Message removed");
- }
-
- const text = this.props.mxEvent.getContent().body;
- return (
-
- { text }
-
- );
- },
-});
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 3eac055054..356e80afcf 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -1328,6 +1328,8 @@
"Reactions": "Reactions",
" reacted with %(content)s": " reacted with %(content)s",
"reacted with %(shortName)s": "reacted with %(shortName)s",
+ "Message deleted": "Message deleted",
+ "Message deleted by %(user)s": "Message deleted by %(user)s",
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s changed the avatar for %(roomName)s",
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s removed the room avatar.",
"%(senderDisplayName)s changed the room avatar to ": "%(senderDisplayName)s changed the room avatar to ",
@@ -1341,9 +1343,6 @@
"edited": "edited",
"Can't load this message": "Can't load this message",
"Submit logs": "Submit logs",
- "Removed or unknown message type": "Removed or unknown message type",
- "Message removed by %(userId)s": "Message removed by %(userId)s",
- "Message removed": "Message removed",
"Failed to load group members": "Failed to load group members",
"Filter community members": "Filter community members",
"Invite to this community": "Invite to this community",
From d3b0e008c1b80acecd537c728c20cb9894d9f550 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Fri, 24 Apr 2020 15:39:23 +0100
Subject: [PATCH 2/9] first draft of Redaction ELS
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
res/css/views/messages/_RedactedBody.scss | 1 +
src/components/structures/MessagePanel.js | 100 +++++++++++++++++-
.../elements/RedactionEventListSummary.tsx | 81 ++++++++++++++
.../views/messages/RedactedBody.tsx | 2 +-
src/i18n/strings/en_EN.json | 4 +-
5 files changed, 185 insertions(+), 3 deletions(-)
create mode 100644 src/components/views/elements/RedactionEventListSummary.tsx
diff --git a/res/css/views/messages/_RedactedBody.scss b/res/css/views/messages/_RedactedBody.scss
index c0001db5d3..c0e5be2c89 100644
--- a/res/css/views/messages/_RedactedBody.scss
+++ b/res/css/views/messages/_RedactedBody.scss
@@ -15,6 +15,7 @@ limitations under the License.
.mx_RedactedBody {
white-space: pre-wrap;
color: $muted-fg-color;
+ vertical-align: middle;
padding-left: 16px;
position: relative;
diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js
index 6fbfdb504b..30c139d440 100644
--- a/src/components/structures/MessagePanel.js
+++ b/src/components/structures/MessagePanel.js
@@ -29,6 +29,7 @@ import SettingsStore from '../../settings/SettingsStore';
import {_t} from "../../languageHandler";
import {haveTileForEvent} from "../views/rooms/EventTile";
import {textForEvent} from "../../TextForEvent";
+import RedactionEventListSummary from "../views/elements/RedactionEventListSummary";
const CONTINUATION_MAX_INTERVAL = 5 * 60 * 1000; // 5 minutes
const continuedTypes = ['m.sticker', 'm.room.message'];
@@ -1062,5 +1063,102 @@ class MemberGrouper {
}
}
+// Wrap consecutive redactions by the same user in a ListSummary, ignore if redacted
+class RedactionGrouper {
+ static canStartGroup = function(panel, ev) {
+ return panel._shouldShowEvent(ev) && ev.isRedacted();
+ }
+
+ constructor(panel, ev, prevEvent, lastShownEvent) {
+ this.panel = panel;
+ this.readMarker = panel._readMarkerForEvent(
+ ev.getId(),
+ ev === lastShownEvent,
+ );
+ this.events = [ev];
+ this.prevEvent = prevEvent;
+ this.lastShownEvent = lastShownEvent;
+ }
+
+ shouldGroup(ev) {
+ if (this.panel._wantsDateSeparator(this.events[0], ev.getDate())) return false;
+ if (ev.getType() === "m.room.redaction") return true; // for show-hidden-events users
+ return ev.isRedacted() && ev.sender === this.events[0].sender &&
+ ev.getUnsigned().redacted_because.sender === this.events[0].getUnsigned().redacted_because.sender;
+ }
+
+ add(ev) {
+ if (ev.getType() === "m.room.redaction") return; // for show-hidden-events users
+ this.readMarker = this.readMarker || this.panel._readMarkerForEvent(
+ ev.getId(),
+ ev === this.lastShownEvent,
+ );
+ this.events.push(ev);
+ }
+
+ getTiles() {
+ // If we don't have any events to group, don't even try to group them. The logic
+ // below assumes that we have a group of events to deal with, but we might not if
+ // the events we were supposed to group were redacted.
+ if (!this.events || !this.events.length) return [];
+
+ const DateSeparator = sdk.getComponent('messages.DateSeparator');
+
+ const panel = this.panel;
+ const lastShownEvent = this.lastShownEvent;
+ const ret = [];
+
+ if (panel._wantsDateSeparator(this.prevEvent, this.events[0].getDate())) {
+ const ts = this.events[0].getTs();
+ ret.push(
+
,
+ );
+ }
+
+ // Ensure that the key of the MemberEventListSummary does not change with new
+ // member events. This will prevent it from being re-created unnecessarily, and
+ // instead will allow new props to be provided. In turn, the shouldComponentUpdate
+ // method on ELS can be used to prevent unnecessary renderings.
+ const key = "redactioneventlistsummary-" + (this.prevEvent ? this.events[0].getId() : "initial");
+
+ let highlightInMels = false;
+ let eventTiles = this.events.map((e) => {
+ if (e.getId() === panel.props.highlightedEventId) {
+ highlightInMels = true;
+ }
+ // In order to prevent DateSeparators from appearing in the expanded form
+ // of MemberEventListSummary, render each member event as if the previous
+ // one was itself. This way, the timestamp of the previous event === the
+ // timestamp of the current event, and no DateSeparator is inserted.
+ return panel._getTilesForEvent(e, e, e === lastShownEvent);
+ }).reduce((a, b) => a.concat(b), []);
+
+ if (eventTiles.length === 0) {
+ eventTiles = null;
+ }
+
+ ret.push(
+
+ { eventTiles }
+ ,
+ );
+
+ if (this.readMarker) {
+ ret.push(this.readMarker);
+ }
+
+ return ret;
+ }
+
+ getNewPrevEvent() {
+ return this.events[0];
+ }
+}
+
// all the grouper classes that we use
-const groupers = [CreationGrouper, MemberGrouper];
+const groupers = [CreationGrouper, MemberGrouper, RedactionGrouper];
diff --git a/src/components/views/elements/RedactionEventListSummary.tsx b/src/components/views/elements/RedactionEventListSummary.tsx
new file mode 100644
index 0000000000..55538ca236
--- /dev/null
+++ b/src/components/views/elements/RedactionEventListSummary.tsx
@@ -0,0 +1,81 @@
+/*
+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 {MatrixClient} from "matrix-js-sdk/src/client";
+import {MatrixEvent} from "matrix-js-sdk/src/models/event";
+
+import { _t } from "../../../languageHandler";
+import * as sdk from "../../../index";
+import MatrixClientContext from "../../../contexts/MatrixClientContext";
+
+interface IProps {
+ // An array of member events to summarise
+ events: MatrixEvent[];
+ // An array of EventTiles to render when expanded
+ children: React.ReactChildren;
+ // The minimum number of events needed to trigger summarisation
+ threshold?: number;
+ // Called when the ELS expansion is toggled
+ onToggle: () => void;
+ // Whether or not to begin with state.expanded=true
+ startExpanded?: boolean;
+}
+
+export default class RedactionEventListSummary extends React.Component {
+ static displayName = "RedactionEventListSummary";
+
+ static defaultProps = {
+ threshold: 2,
+ };
+
+ static contextType = MatrixClientContext;
+
+ shouldComponentUpdate(nextProps) {
+ // Update if
+ // - The number of summarised events has changed
+ // - or if the summary is about to toggle to become collapsed
+ // - or if there are fewEvents, meaning the child eventTiles are shown as-is
+ return (
+ nextProps.events.length !== this.props.events.length ||
+ nextProps.events.length < this.props.threshold
+ );
+ }
+
+ render() {
+ const count = this.props.events.length;
+ const redactionSender = this.props.events[0].getUnsigned().redacted_because.sender;
+
+ let avatarMember = this.props.events[0].sender;
+ let summaryText = _t("%(count)s messages deleted", { count });
+ if (redactionSender !== this.context.getUserId()) {
+ const room = (this.context as MatrixClient).getRoom(redactionSender || this.props.events[0].getSender());
+ avatarMember = room && room.getMember(redactionSender);
+ const name = avatarMember ? avatarMember.name : redactionSender;
+ summaryText = _t("%(count)s messages deleted by %(name)s", { count, name });
+ }
+
+ const EventListSummary = sdk.getComponent("views.elements.EventListSummary");
+ return ;
+ }
+}
diff --git a/src/components/views/messages/RedactedBody.tsx b/src/components/views/messages/RedactedBody.tsx
index f219e3bd91..654f1622b1 100644
--- a/src/components/views/messages/RedactedBody.tsx
+++ b/src/components/views/messages/RedactedBody.tsx
@@ -32,7 +32,7 @@ const RedactedBody = React.forwardRef(({mxEvent}, ref) => {
if (redactedBecauseUserId !== cli.getUserId()) {
const room = cli.getRoom(mxEvent.getRoomId());
const sender = room && room.getMember(redactedBecauseUserId);
- text = _t("Message deleted by %(user)s", { user: sender.name || redactedBecauseUserId });
+ text = _t("Message deleted by %(name)s", { name: sender ? sender.name : redactedBecauseUserId });
}
return (
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 356e80afcf..53818d4747 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -1329,7 +1329,7 @@
" reacted with %(content)s": " reacted with %(content)s",
"reacted with %(shortName)s": "reacted with %(shortName)s",
"Message deleted": "Message deleted",
- "Message deleted by %(user)s": "Message deleted by %(user)s",
+ "Message deleted by %(name)s": "Message deleted by %(name)s",
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s changed the avatar for %(roomName)s",
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s removed the room avatar.",
"%(senderDisplayName)s changed the room avatar to ": "%(senderDisplayName)s changed the room avatar to ",
@@ -1487,6 +1487,8 @@
"%(oneUser)smade no changes %(count)s times|one": "%(oneUser)smade no changes",
"Power level": "Power level",
"Custom level": "Custom level",
+ "%(count)s messages deleted|other": "%(count)s messages deleted",
+ "%(count)s messages deleted by %(name)s|other": "%(count)s messages deleted by %(name)s",
"Unable to load event that was replied to, it either does not exist or you do not have permission to view it.": "Unable to load event that was replied to, it either does not exist or you do not have permission to view it.",
"In reply to": "In reply to",
"Room alias": "Room alias",
From a399b1018ae1aa18e815dcbccdf35222ec1571ed Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Mon, 27 Apr 2020 23:16:08 +0100
Subject: [PATCH 3/9] fix flashing `by ""`
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/views/messages/RedactedBody.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/views/messages/RedactedBody.tsx b/src/components/views/messages/RedactedBody.tsx
index 654f1622b1..e347e879a3 100644
--- a/src/components/views/messages/RedactedBody.tsx
+++ b/src/components/views/messages/RedactedBody.tsx
@@ -29,7 +29,7 @@ const RedactedBody = React.forwardRef(({mxEvent}, ref) => {
let text = _t("Message deleted");
const redactedBecauseUserId = mxEvent.getUnsigned().redacted_because.sender;
- if (redactedBecauseUserId !== cli.getUserId()) {
+ if (redactedBecauseUserId && redactedBecauseUserId !== cli.getUserId()) {
const room = cli.getRoom(mxEvent.getRoomId());
const sender = room && room.getMember(redactedBecauseUserId);
text = _t("Message deleted by %(name)s", { name: sender ? sender.name : redactedBecauseUserId });
From 83a4558d49021b58f30a47637ce69beb5d6b1fd3 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Mon, 27 Apr 2020 23:27:46 +0100
Subject: [PATCH 4/9] remove RedactedGrouper for now
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/structures/MessagePanel.js | 99 +------------------
.../views/elements/EventListSummary.js | 18 ++--
.../elements/RedactionEventListSummary.tsx | 81 ---------------
3 files changed, 13 insertions(+), 185 deletions(-)
delete mode 100644 src/components/views/elements/RedactionEventListSummary.tsx
diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js
index 30c139d440..a83529e055 100644
--- a/src/components/structures/MessagePanel.js
+++ b/src/components/structures/MessagePanel.js
@@ -1063,102 +1063,5 @@ class MemberGrouper {
}
}
-// Wrap consecutive redactions by the same user in a ListSummary, ignore if redacted
-class RedactionGrouper {
- static canStartGroup = function(panel, ev) {
- return panel._shouldShowEvent(ev) && ev.isRedacted();
- }
-
- constructor(panel, ev, prevEvent, lastShownEvent) {
- this.panel = panel;
- this.readMarker = panel._readMarkerForEvent(
- ev.getId(),
- ev === lastShownEvent,
- );
- this.events = [ev];
- this.prevEvent = prevEvent;
- this.lastShownEvent = lastShownEvent;
- }
-
- shouldGroup(ev) {
- if (this.panel._wantsDateSeparator(this.events[0], ev.getDate())) return false;
- if (ev.getType() === "m.room.redaction") return true; // for show-hidden-events users
- return ev.isRedacted() && ev.sender === this.events[0].sender &&
- ev.getUnsigned().redacted_because.sender === this.events[0].getUnsigned().redacted_because.sender;
- }
-
- add(ev) {
- if (ev.getType() === "m.room.redaction") return; // for show-hidden-events users
- this.readMarker = this.readMarker || this.panel._readMarkerForEvent(
- ev.getId(),
- ev === this.lastShownEvent,
- );
- this.events.push(ev);
- }
-
- getTiles() {
- // If we don't have any events to group, don't even try to group them. The logic
- // below assumes that we have a group of events to deal with, but we might not if
- // the events we were supposed to group were redacted.
- if (!this.events || !this.events.length) return [];
-
- const DateSeparator = sdk.getComponent('messages.DateSeparator');
-
- const panel = this.panel;
- const lastShownEvent = this.lastShownEvent;
- const ret = [];
-
- if (panel._wantsDateSeparator(this.prevEvent, this.events[0].getDate())) {
- const ts = this.events[0].getTs();
- ret.push(
-
,
- );
- }
-
- // Ensure that the key of the MemberEventListSummary does not change with new
- // member events. This will prevent it from being re-created unnecessarily, and
- // instead will allow new props to be provided. In turn, the shouldComponentUpdate
- // method on ELS can be used to prevent unnecessary renderings.
- const key = "redactioneventlistsummary-" + (this.prevEvent ? this.events[0].getId() : "initial");
-
- let highlightInMels = false;
- let eventTiles = this.events.map((e) => {
- if (e.getId() === panel.props.highlightedEventId) {
- highlightInMels = true;
- }
- // In order to prevent DateSeparators from appearing in the expanded form
- // of MemberEventListSummary, render each member event as if the previous
- // one was itself. This way, the timestamp of the previous event === the
- // timestamp of the current event, and no DateSeparator is inserted.
- return panel._getTilesForEvent(e, e, e === lastShownEvent);
- }).reduce((a, b) => a.concat(b), []);
-
- if (eventTiles.length === 0) {
- eventTiles = null;
- }
-
- ret.push(
-
- { eventTiles }
- ,
- );
-
- if (this.readMarker) {
- ret.push(this.readMarker);
- }
-
- return ret;
- }
-
- getNewPrevEvent() {
- return this.events[0];
- }
-}
-
// all the grouper classes that we use
-const groupers = [CreationGrouper, MemberGrouper, RedactionGrouper];
+const groupers = [CreationGrouper, MemberGrouper];
diff --git a/src/components/views/elements/EventListSummary.js b/src/components/views/elements/EventListSummary.js
index 79c84293c2..707b9e79b0 100644
--- a/src/components/views/elements/EventListSummary.js
+++ b/src/components/views/elements/EventListSummary.js
@@ -22,7 +22,8 @@ import {MatrixEvent, RoomMember} from "matrix-js-sdk";
import {useStateToggle} from "../../../hooks/useStateToggle";
import AccessibleButton from "./AccessibleButton";
-const EventListSummary = ({events, children, threshold=3, onToggle, startExpanded, summaryMembers=[], summaryText}) => {
+const EventListSummary = (props) => {
+ const {events, children, threshold=3, onToggle, startExpanded, summaryMembers=[], summaryText, summary} = props;
const [expanded, toggleExpanded] = useStateToggle(startExpanded);
// Whenever expanded changes call onToggle
@@ -49,6 +50,8 @@ const EventListSummary = ({events, children, threshold=3, onToggle, startExpande
+
);
};
@@ -87,10 +90,13 @@ EventListSummary.propTypes = {
// Whether or not to begin with state.expanded=true
startExpanded: PropTypes.bool,
- // The list of room members for which to show avatars next to the summary
+ // The node to render as a summary when the summary is not collapsed,
+ // alternately summaryMembers and summaryText can be provided.
+ summary: PropTypes.node,
+ // The list of room members for which to show avatars next to the summary, ignored if summary is provided
summaryMembers: PropTypes.arrayOf(PropTypes.instanceOf(RoomMember)),
- // The text to show as the summary of this event list
- summaryText: PropTypes.string.isRequired,
+ // The text to show as the summary of this event list, ignored if summary is provided
+ summaryText: PropTypes.string,
};
export default EventListSummary;
diff --git a/src/components/views/elements/RedactionEventListSummary.tsx b/src/components/views/elements/RedactionEventListSummary.tsx
deleted file mode 100644
index 55538ca236..0000000000
--- a/src/components/views/elements/RedactionEventListSummary.tsx
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
-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 {MatrixClient} from "matrix-js-sdk/src/client";
-import {MatrixEvent} from "matrix-js-sdk/src/models/event";
-
-import { _t } from "../../../languageHandler";
-import * as sdk from "../../../index";
-import MatrixClientContext from "../../../contexts/MatrixClientContext";
-
-interface IProps {
- // An array of member events to summarise
- events: MatrixEvent[];
- // An array of EventTiles to render when expanded
- children: React.ReactChildren;
- // The minimum number of events needed to trigger summarisation
- threshold?: number;
- // Called when the ELS expansion is toggled
- onToggle: () => void;
- // Whether or not to begin with state.expanded=true
- startExpanded?: boolean;
-}
-
-export default class RedactionEventListSummary extends React.Component {
- static displayName = "RedactionEventListSummary";
-
- static defaultProps = {
- threshold: 2,
- };
-
- static contextType = MatrixClientContext;
-
- shouldComponentUpdate(nextProps) {
- // Update if
- // - The number of summarised events has changed
- // - or if the summary is about to toggle to become collapsed
- // - or if there are fewEvents, meaning the child eventTiles are shown as-is
- return (
- nextProps.events.length !== this.props.events.length ||
- nextProps.events.length < this.props.threshold
- );
- }
-
- render() {
- const count = this.props.events.length;
- const redactionSender = this.props.events[0].getUnsigned().redacted_because.sender;
-
- let avatarMember = this.props.events[0].sender;
- let summaryText = _t("%(count)s messages deleted", { count });
- if (redactionSender !== this.context.getUserId()) {
- const room = (this.context as MatrixClient).getRoom(redactionSender || this.props.events[0].getSender());
- avatarMember = room && room.getMember(redactionSender);
- const name = avatarMember ? avatarMember.name : redactionSender;
- summaryText = _t("%(count)s messages deleted by %(name)s", { count, name });
- }
-
- const EventListSummary = sdk.getComponent("views.elements.EventListSummary");
- return ;
- }
-}
From c7870090f8466bff4f40ea074c36c56b68469f28 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Mon, 27 Apr 2020 23:29:38 +0100
Subject: [PATCH 5/9] clean ups
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/structures/MessagePanel.js | 1 -
src/components/views/elements/EventListSummary.js | 8 +-------
src/i18n/strings/en_EN.json | 2 --
3 files changed, 1 insertion(+), 10 deletions(-)
diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js
index a83529e055..6fbfdb504b 100644
--- a/src/components/structures/MessagePanel.js
+++ b/src/components/structures/MessagePanel.js
@@ -29,7 +29,6 @@ import SettingsStore from '../../settings/SettingsStore';
import {_t} from "../../languageHandler";
import {haveTileForEvent} from "../views/rooms/EventTile";
import {textForEvent} from "../../TextForEvent";
-import RedactionEventListSummary from "../views/elements/RedactionEventListSummary";
const CONTINUATION_MAX_INTERVAL = 5 * 60 * 1000; // 5 minutes
const continuedTypes = ['m.sticker', 'm.room.message'];
diff --git a/src/components/views/elements/EventListSummary.js b/src/components/views/elements/EventListSummary.js
index 707b9e79b0..bf0f8ea133 100644
--- a/src/components/views/elements/EventListSummary.js
+++ b/src/components/views/elements/EventListSummary.js
@@ -22,8 +22,7 @@ import {MatrixEvent, RoomMember} from "matrix-js-sdk";
import {useStateToggle} from "../../../hooks/useStateToggle";
import AccessibleButton from "./AccessibleButton";
-const EventListSummary = (props) => {
- const {events, children, threshold=3, onToggle, startExpanded, summaryMembers=[], summaryText, summary} = props;
+const EventListSummary = ({events, children, threshold=3, onToggle, startExpanded, summaryMembers=[], summaryText}) => {
const [expanded, toggleExpanded] = useStateToggle(startExpanded);
// Whenever expanded changes call onToggle
@@ -50,8 +49,6 @@ const EventListSummary = (props) => {
{ children }
;
- } else if (summary) {
- body = summary;
} else {
const avatars = summaryMembers.map((m) => );
body = (
@@ -90,9 +87,6 @@ EventListSummary.propTypes = {
// Whether or not to begin with state.expanded=true
startExpanded: PropTypes.bool,
- // The node to render as a summary when the summary is not collapsed,
- // alternately summaryMembers and summaryText can be provided.
- summary: PropTypes.node,
// The list of room members for which to show avatars next to the summary, ignored if summary is provided
summaryMembers: PropTypes.arrayOf(PropTypes.instanceOf(RoomMember)),
// The text to show as the summary of this event list, ignored if summary is provided
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index de3f88c244..8785b5e244 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -1488,8 +1488,6 @@
"%(oneUser)smade no changes %(count)s times|one": "%(oneUser)smade no changes",
"Power level": "Power level",
"Custom level": "Custom level",
- "%(count)s messages deleted|other": "%(count)s messages deleted",
- "%(count)s messages deleted by %(name)s|other": "%(count)s messages deleted by %(name)s",
"Unable to load event that was replied to, it either does not exist or you do not have permission to view it.": "Unable to load event that was replied to, it either does not exist or you do not have permission to view it.",
"In reply to": "In reply to",
"Room alias": "Room alias",
From a5830c229ec62448cb014d3552955552be0f3ec6 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Mon, 27 Apr 2020 23:30:40 +0100
Subject: [PATCH 6/9] more tidying
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/views/elements/EventListSummary.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/components/views/elements/EventListSummary.js b/src/components/views/elements/EventListSummary.js
index bf0f8ea133..5a4a6e4f5a 100644
--- a/src/components/views/elements/EventListSummary.js
+++ b/src/components/views/elements/EventListSummary.js
@@ -87,9 +87,9 @@ EventListSummary.propTypes = {
// Whether or not to begin with state.expanded=true
startExpanded: PropTypes.bool,
- // The list of room members for which to show avatars next to the summary, ignored if summary is provided
+ // The list of room members for which to show avatars next to the summary
summaryMembers: PropTypes.arrayOf(PropTypes.instanceOf(RoomMember)),
- // The text to show as the summary of this event list, ignored if summary is provided
+ // The text to show as the summary of this event list
summaryText: PropTypes.string,
};
From 3e35cffae7ffb4ab6447f75464b84df82176a84d Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Mon, 27 Apr 2020 23:53:32 +0100
Subject: [PATCH 7/9] null-guards
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/views/messages/RedactedBody.tsx | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/components/views/messages/RedactedBody.tsx b/src/components/views/messages/RedactedBody.tsx
index e347e879a3..5dada64b52 100644
--- a/src/components/views/messages/RedactedBody.tsx
+++ b/src/components/views/messages/RedactedBody.tsx
@@ -28,8 +28,9 @@ const RedactedBody = React.forwardRef(({mxEvent}, ref) => {
const cli: MatrixClient = useContext(MatrixClientContext);
let text = _t("Message deleted");
- const redactedBecauseUserId = mxEvent.getUnsigned().redacted_because.sender;
- if (redactedBecauseUserId && redactedBecauseUserId !== cli.getUserId()) {
+ const unsigned = mxEvent.getUnsigned();
+ const redactedBecauseUserId = unsigned && unsigned.redacted_because && unsigned.redacted_because.sender;
+ if (redactedBecauseUserId && redactedBecauseUserId !== mxEvent.getSender()) {
const room = cli.getRoom(mxEvent.getRoomId());
const sender = room && room.getMember(redactedBecauseUserId);
text = _t("Message deleted by %(name)s", { name: sender ? sender.name : redactedBecauseUserId });
From 7c1ac7aaffe17a7a3941cb8bda4fb822876bf1ad Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Thu, 7 May 2020 10:02:40 +0100
Subject: [PATCH 8/9] fix unrelated tautology
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
src/components/structures/MatrixChat.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx
index 05354fa5f2..3929711406 100644
--- a/src/components/structures/MatrixChat.tsx
+++ b/src/components/structures/MatrixChat.tsx
@@ -1339,7 +1339,7 @@ export default class MatrixChat extends React.PureComponent {
// this if we are not scrolled up in the view. To find out, delegate to
// the timeline panel. If the timeline panel doesn't exist, then we assume
// it is safe to reset the timeline.
- if (!this.loggedInView.current || !this.loggedInView.current) {
+ if (!this.loggedInView.current) {
return true;
}
return this.loggedInView.current.canResetTimelineInRoom(roomId);
From 4d6cd3c0504e7f4bc07e4cce95177affdc84bb66 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Thu, 7 May 2020 12:50:04 +0100
Subject: [PATCH 9/9] Update padding-left to 20px as per Nad's iteration
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
---
res/css/views/messages/_RedactedBody.scss | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/res/css/views/messages/_RedactedBody.scss b/res/css/views/messages/_RedactedBody.scss
index c0e5be2c89..9ba35d7297 100644
--- a/res/css/views/messages/_RedactedBody.scss
+++ b/res/css/views/messages/_RedactedBody.scss
@@ -17,7 +17,7 @@ limitations under the License.
color: $muted-fg-color;
vertical-align: middle;
- padding-left: 16px;
+ padding-left: 20px;
position: relative;
&::before {