diff --git a/src/components/structures/MessagePanel.tsx b/src/components/structures/MessagePanel.tsx index 66757fe0d6..4fe5fd239a 100644 --- a/src/components/structures/MessagePanel.tsx +++ b/src/components/structures/MessagePanel.tsx @@ -462,17 +462,17 @@ export default class MessagePanel extends React.Component { return !this.isMounted; }; - private get showHiddenEvents(): boolean { + public get showHiddenEvents(): boolean { return this.context?.showHiddenEventsInTimeline ?? this.showHiddenEventsInTimeline; } // TODO: Implement granular (per-room) hide options - public shouldShowEvent(mxEv: MatrixEvent): boolean { + public shouldShowEvent(mxEv: MatrixEvent, forceHideEvents = false): boolean { if (MatrixClientPeg.get().isUserIgnored(mxEv.getSender())) { return false; // ignored = no show (only happens if the ignore happens after an event was received) } - if (this.showHiddenEvents) { + if (this.showHiddenEvents && !forceHideEvents) { return true; } @@ -1403,5 +1403,91 @@ 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( +
  • , + ); + } + + const key = "hiddeneventlistsummary-" + ( + this.prevEvent ? this.events[0].getId() : "initial" + ); + + const senders = new Set(); + 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( + , + ); + + 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]; +const groupers = [CreationGrouper, MemberGrouper, RedactionGrouper, HiddenEventGrouper]; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 04ccade393..89818efe9b 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -3047,6 +3047,8 @@ "%(creator)s created and configured the room.": "%(creator)s created and configured the room.", "%(count)s messages deleted.|other": "%(count)s messages deleted.", "%(count)s messages deleted.|one": "%(count)s message deleted.", + "%(count)s hidden messages.|other": "%(count)s hidden messages.", + "%(count)s hidden messages.|one": "%(count)s hidden message.", "Your Communities": "Your Communities", "Did you know: you can use communities to filter your %(brand)s experience!": "Did you know: you can use communities to filter your %(brand)s experience!", "You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.": "You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.",