Consolidate RedactionGrouper and HiddenEventGrouper into MELS (#7739)

This commit is contained in:
Michael Telatynski 2022-02-09 10:51:12 +00:00 committed by GitHub
parent 59cdd3edc0
commit 714136d4f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 750 additions and 887 deletions

View file

@ -20,7 +20,6 @@ import { Room } from 'matrix-js-sdk/src/models/room';
import { EventType } from 'matrix-js-sdk/src/@types/event';
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
import { Relations } from "matrix-js-sdk/src/models/relations";
import { RoomMember } from 'matrix-js-sdk/src/models/room-member';
import { logger } from 'matrix-js-sdk/src/logger';
import shouldHideEvent from '../../shouldHideEvent';
@ -41,8 +40,8 @@ import defaultDispatcher from '../../dispatcher/dispatcher';
import CallEventGrouper from "./CallEventGrouper";
import WhoIsTypingTile from '../views/rooms/WhoIsTypingTile';
import ScrollPanel, { IScrollState } from "./ScrollPanel";
import GenericEventListSummary from '../views/elements/GenericEventListSummary';
import EventListSummary from '../views/elements/EventListSummary';
import MemberEventListSummary from '../views/elements/MemberEventListSummary';
import DateSeparator from '../views/messages/DateSeparator';
import ErrorBoundary from '../views/elements/ErrorBoundary';
import ResizeNotifier from "../../utils/ResizeNotifier";
@ -651,7 +650,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
if (grouper) {
if (grouper.shouldGroup(mxEv)) {
grouper.add(mxEv, this.showHiddenEvents);
grouper.add(mxEv);
continue;
} else {
// not part of group, so get the group tiles, close the
@ -1047,7 +1046,7 @@ abstract class BaseGrouper {
}
public abstract shouldGroup(ev: MatrixEvent): boolean;
public abstract add(ev: MatrixEvent, showHiddenEvents?: boolean): void;
public abstract add(ev: MatrixEvent): void;
public abstract getTiles(): ReactNode[];
public abstract getNewPrevEvent(): MatrixEvent;
}
@ -1064,7 +1063,7 @@ abstract class BaseGrouper {
* when determining things such as whether a date separator is necessary
*/
// Wrap initial room creation events into an EventListSummary
// Wrap initial room creation events into a GenericEventListSummary
// Grouping only events sent by the same user that sent the `m.room.create` and only until
// the first non-state event or membership event which is not regarding the sender of the `m.room.create` event
class CreationGrouper extends BaseGrouper {
@ -1140,7 +1139,7 @@ class CreationGrouper extends BaseGrouper {
const eventTiles = this.events.map((e) => {
// In order to prevent DateSeparators from appearing in the expanded form
// of EventListSummary, render each member event as if the previous
// of GenericEventListSummary, 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, isGrouped);
@ -1160,7 +1159,7 @@ class CreationGrouper extends BaseGrouper {
ret.push(<NewRoomIntro key="newroomintro" />);
ret.push(
<EventListSummary
<GenericEventListSummary
key="roomcreationsummary"
events={this.events}
onToggle={panel.onHeightChanged} // Update scroll state
@ -1169,7 +1168,7 @@ class CreationGrouper extends BaseGrouper {
layout={this.layout}
>
{ eventTiles }
</EventListSummary>,
</GenericEventListSummary>,
);
if (this.readMarker) {
@ -1184,107 +1183,24 @@ class CreationGrouper extends BaseGrouper {
}
}
class RedactionGrouper extends BaseGrouper {
// Wrap consecutive grouped events in a ListSummary
class MainGrouper extends BaseGrouper {
static canStartGroup = function(panel: MessagePanel, ev: MatrixEvent): boolean {
return panel.shouldShowEvent(ev) && ev.isRedacted();
};
if (!panel.shouldShowEvent(ev)) return false;
constructor(
panel: MessagePanel,
ev: MatrixEvent,
prevEvent: MatrixEvent,
lastShownEvent: MatrixEvent,
layout: Layout,
nextEvent: MatrixEvent,
nextEventTile: MatrixEvent,
) {
super(panel, ev, prevEvent, lastShownEvent, layout, nextEvent, nextEventTile);
this.events = [ev];
}
public shouldGroup(ev: MatrixEvent): boolean {
// absorb hidden events so that they do not break up streams of messages & redaction events being grouped
if (!this.panel.shouldShowEvent(ev)) {
if (groupedEvents.includes(ev.getType() as EventType)) {
return true;
}
if (this.panel.wantsDateSeparator(this.events[0], ev.getDate())) {
return false;
}
return ev.isRedacted();
}
public add(ev: MatrixEvent): void {
this.readMarker = this.readMarker || this.panel.readMarkerForEvent(
ev.getId(),
ev === this.lastShownEvent,
);
if (!this.panel.shouldShowEvent(ev)) {
return;
}
this.events.push(ev);
}
public getTiles(): ReactNode[] {
if (!this.events || !this.events.length) return [];
const isGrouped = true;
const panel = this.panel;
const ret = [];
const lastShownEvent = this.lastShownEvent;
if (panel.wantsDateSeparator(this.prevEvent, this.events[0].getDate())) {
const ts = this.events[0].getTs();
ret.push(
<li key={ts+'~'}><DateSeparator key={ts+'~'} roomId={this.events[0].getRoomId()} ts={ts} /></li>,
);
if (ev.isRedacted()) {
return true;
}
const key = "redactioneventlistsummary-" + (
this.prevEvent ? this.events[0].getId() : "initial"
);
const senders = new Set<RoomMember>();
let eventTiles = this.events.map((e, i) => {
senders.add(e.sender);
const prevEvent = i === 0 ? this.prevEvent : this.events[i - 1];
return panel.getTilesForEvent(
prevEvent, e, e === lastShownEvent, isGrouped, this.nextEvent, this.nextEventTile);
}).reduce((a, b) => a.concat(b), []);
if (eventTiles.length === 0) {
eventTiles = null;
if (panel.showHiddenEvents && !panel.shouldShowEvent(ev, true)) {
return true;
}
ret.push(
<EventListSummary
key={key}
threshold={2}
events={this.events}
onToggle={panel.onHeightChanged} // Update scroll state
summaryMembers={Array.from(senders)}
summaryText={_t("%(count)s messages deleted.", { count: eventTiles.length })}
layout={this.layout}
>
{ eventTiles }
</EventListSummary>,
);
if (this.readMarker) {
ret.push(this.readMarker);
}
return ret;
}
public getNewPrevEvent(): MatrixEvent {
return this.events[this.events.length - 1];
}
}
// Wrap consecutive member events in a ListSummary, ignore if redacted
class MemberGrouper extends BaseGrouper {
static canStartGroup = function(panel: MessagePanel, ev: MatrixEvent): boolean {
return panel.shouldShowEvent(ev) && groupedEvents.includes(ev.getType() as EventType);
return false;
};
constructor(
@ -1293,27 +1209,43 @@ class MemberGrouper extends BaseGrouper {
public readonly prevEvent: MatrixEvent,
public readonly lastShownEvent: MatrixEvent,
protected readonly layout: Layout,
nextEvent: MatrixEvent,
nextEventTile: MatrixEvent,
) {
super(panel, event, prevEvent, lastShownEvent, layout);
super(panel, event, prevEvent, lastShownEvent, layout, nextEvent, nextEventTile);
this.events = [event];
}
public shouldGroup(ev: MatrixEvent): boolean {
if (!this.panel.shouldShowEvent(ev)) {
// absorb hidden events so that they do not break up streams of messages & redaction events being grouped
return true;
}
if (this.panel.wantsDateSeparator(this.events[0], ev.getDate())) {
return false;
}
return groupedEvents.includes(ev.getType() as EventType);
if (groupedEvents.includes(ev.getType() as EventType)) {
return true;
}
if (ev.isRedacted()) {
return true;
}
if (this.panel.showHiddenEvents && !this.panel.shouldShowEvent(ev, true)) {
return true;
}
return false;
}
public add(ev: MatrixEvent, showHiddenEvents?: boolean): void {
public add(ev: MatrixEvent): void {
if (ev.getType() === EventType.RoomMember) {
// We can ignore any events that don't actually have a message to display
if (!hasText(ev, showHiddenEvents)) return;
if (!hasText(ev, this.panel.showHiddenEvents)) return;
}
this.readMarker = this.readMarker || this.panel.readMarkerForEvent(ev.getId(), ev === this.lastShownEvent);
if (!this.panel.showHiddenEvents && !this.panel.shouldShowEvent(ev)) {
// absorb hidden events to not split the summary
return;
}
this.readMarker = this.readMarker || this.panel.readMarkerForEvent(
ev.getId(),
ev === this.lastShownEvent,
);
this.events.push(ev);
}
@ -1321,7 +1253,7 @@ class MemberGrouper extends BaseGrouper {
// 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 [];
if (!this.events?.length) return [];
const isGrouped = true;
const panel = this.panel;
@ -1335,28 +1267,26 @@ class MemberGrouper extends BaseGrouper {
);
}
// Ensure that the key of the MemberEventListSummary does not change with new
// member events. This will prevent it from being re-created unnecessarily, and
// Ensure that the key of the EventListSummary does not change with new 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 MELS can be used to prevent unnecessary renderings.
// method on ELS can be used to prevent unnecessary renderings.
//
// Whilst back-paginating with a MELS at the top of the panel, prevEvent will be null,
// so use the key "membereventlistsummary-initial". Otherwise, use the ID of the first
// Whilst back-paginating with an ELS at the top of the panel, prevEvent will be null,
// so use the key "eventlistsummary-initial". Otherwise, use the ID of the first
// membership event, which will not change during forward pagination.
const key = "membereventlistsummary-" + (
this.prevEvent ? this.events[0].getId() : "initial"
);
const key = "eventlistsummary-" + (this.prevEvent ? this.events[0].getId() : "initial");
let highlightInMels;
let highlightInSummary = false;
let eventTiles = this.events.map((e) => {
if (e.getId() === panel.props.highlightedEventId) {
highlightInMels = true;
highlightInSummary = true;
}
// In order to prevent DateSeparators from appearing in the expanded form
// of MemberEventListSummary, render each member event as if the previous
// of EventListSummary, 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, isGrouped);
return panel.getTilesForEvent(e, e, e === lastShownEvent, isGrouped, this.nextEvent, this.nextEventTile);
}).reduce((a, b) => a.concat(b), []);
if (eventTiles.length === 0) {
@ -1370,15 +1300,15 @@ class MemberGrouper extends BaseGrouper {
}
ret.push(
<MemberEventListSummary
<EventListSummary
key={key}
events={this.events}
onToggle={panel.onHeightChanged} // Update scroll state
startExpanded={highlightInMels}
startExpanded={highlightInSummary}
layout={this.layout}
>
{ eventTiles }
</MemberEventListSummary>,
</EventListSummary>,
);
if (this.readMarker) {
@ -1393,91 +1323,5 @@ class MemberGrouper extends BaseGrouper {
}
}
// Wrap consecutive hidden events in a ListSummary, ignore if redacted
class HiddenEventGrouper extends BaseGrouper {
static canStartGroup = function(panel: MessagePanel, ev: MatrixEvent): boolean {
return !panel.shouldShowEvent(ev, true) && panel.showHiddenEvents;
};
constructor(
public readonly panel: MessagePanel,
public readonly event: MatrixEvent,
public readonly prevEvent: MatrixEvent,
public readonly lastShownEvent: MatrixEvent,
protected readonly layout: Layout,
) {
super(panel, event, prevEvent, lastShownEvent, layout);
this.events = [event];
}
public shouldGroup(ev: MatrixEvent): boolean {
if (this.panel.wantsDateSeparator(this.events[0], ev.getDate())) {
return false;
}
return !this.panel.shouldShowEvent(ev, true);
}
public add(ev: MatrixEvent, showHiddenEvents?: boolean): void {
this.readMarker = this.readMarker || this.panel.readMarkerForEvent(ev.getId(), ev === this.lastShownEvent);
this.events.push(ev);
}
public getTiles(): ReactNode[] {
if (!this.events || !this.events.length) return [];
const isGrouped = true;
const panel = this.panel;
const ret = [];
const lastShownEvent = this.lastShownEvent;
if (panel.wantsDateSeparator(this.prevEvent, this.events[0].getDate())) {
const ts = this.events[0].getTs();
ret.push(
<li key={ts+'~'}><DateSeparator key={ts+'~'} roomId={this.events[0].getRoomId()} ts={ts} /></li>,
);
}
const key = "hiddeneventlistsummary-" + (
this.prevEvent ? this.events[0].getId() : "initial"
);
const senders = new Set<RoomMember>();
let eventTiles = this.events.map((e, i) => {
senders.add(e.sender);
const prevEvent = i === 0 ? this.prevEvent : this.events[i - 1];
return panel.getTilesForEvent(
prevEvent, e, e === lastShownEvent, isGrouped, this.nextEvent, this.nextEventTile);
}).reduce((a, b) => a.concat(b), []);
if (eventTiles.length === 0) {
eventTiles = null;
}
ret.push(
<EventListSummary
key={key}
threshold={2}
events={this.events}
onToggle={panel.onHeightChanged} // Update scroll state
summaryMembers={Array.from(senders)}
summaryText={_t("%(count)s hidden messages.", { count: eventTiles.length })}
layout={this.layout}
>
{ eventTiles }
</EventListSummary>,
);
if (this.readMarker) {
ret.push(this.readMarker);
}
return ret;
}
public getNewPrevEvent(): MatrixEvent {
return this.events[this.events.length - 1];
}
}
// all the grouper classes that we use
const groupers = [CreationGrouper, MemberGrouper, RedactionGrouper, HiddenEventGrouper];
const groupers = [CreationGrouper, MainGrouper];