Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 0f15cee

Browse files
authored
Merge pull request #6205 from robintown/text-for-event-perf
2 parents aaa9040 + 092fdf5 commit 0f15cee

File tree

8 files changed

+91
-54
lines changed

8 files changed

+91
-54
lines changed

src/TextForEvent.tsx

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
16-
1716
import React from 'react';
1817
import { MatrixClientPeg } from './MatrixClientPeg';
1918
import { _t } from './languageHandler';
@@ -32,7 +31,7 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event";
3231
// any text to display at all. For this reason they return deferred values
3332
// to avoid the expense of looking up translations when they're not needed.
3433

35-
function textForMemberEvent(ev: MatrixEvent): () => string | null {
34+
function textForMemberEvent(ev: MatrixEvent, allowJSX: boolean, showHiddenEvents?: boolean): () => string | null {
3635
// XXX: SYJS-16 "sender is sometimes null for join messages"
3736
const senderName = ev.sender ? ev.sender.name : ev.getSender();
3837
const targetName = ev.target ? ev.target.name : ev.getStateKey();
@@ -84,7 +83,7 @@ function textForMemberEvent(ev: MatrixEvent): () => string | null {
8483
return () => _t('%(senderName)s changed their profile picture', { senderName });
8584
} else if (!prevContent.avatar_url && content.avatar_url) {
8685
return () => _t('%(senderName)s set a profile picture', { senderName });
87-
} else if (SettingsStore.getValue("showHiddenEventsInTimeline")) {
86+
} else if (showHiddenEvents ?? SettingsStore.getValue("showHiddenEventsInTimeline")) {
8887
// This is a null rejoin, it will only be visible if using 'show hidden events' (labs)
8988
return () => _t("%(senderName)s made no change", { senderName });
9089
} else {
@@ -319,15 +318,15 @@ function textForCanonicalAliasEvent(ev: MatrixEvent): () => string | null {
319318
});
320319
}
321320

322-
function textForCallAnswerEvent(event): () => string | null {
321+
function textForCallAnswerEvent(event: MatrixEvent): () => string | null {
323322
return () => {
324323
const senderName = event.sender ? event.sender.name : _t('Someone');
325324
const supported = MatrixClientPeg.get().supportsVoip() ? '' : _t('(not supported by this browser)');
326325
return _t('%(senderName)s answered the call.', { senderName }) + ' ' + supported;
327326
};
328327
}
329328

330-
function textForCallHangupEvent(event): () => string | null {
329+
function textForCallHangupEvent(event: MatrixEvent): () => string | null {
331330
const getSenderName = () => event.sender ? event.sender.name : _t('Someone');
332331
const eventContent = event.getContent();
333332
let getReason = () => "";
@@ -364,14 +363,14 @@ function textForCallHangupEvent(event): () => string | null {
364363
return () => _t('%(senderName)s ended the call.', { senderName: getSenderName() }) + ' ' + getReason();
365364
}
366365

367-
function textForCallRejectEvent(event): () => string | null {
366+
function textForCallRejectEvent(event: MatrixEvent): () => string | null {
368367
return () => {
369368
const senderName = event.sender ? event.sender.name : _t('Someone');
370369
return _t('%(senderName)s declined the call.', { senderName });
371370
};
372371
}
373372

374-
function textForCallInviteEvent(event): () => string | null {
373+
function textForCallInviteEvent(event: MatrixEvent): () => string | null {
375374
const getSenderName = () => event.sender ? event.sender.name : _t('Someone');
376375
// FIXME: Find a better way to determine this from the event?
377376
let isVoice = true;
@@ -403,7 +402,7 @@ function textForCallInviteEvent(event): () => string | null {
403402
}
404403
}
405404

406-
function textForThreePidInviteEvent(event): () => string | null {
405+
function textForThreePidInviteEvent(event: MatrixEvent): () => string | null {
407406
const senderName = event.sender ? event.sender.name : event.getSender();
408407

409408
if (!isValid3pidInvite(event)) {
@@ -419,7 +418,7 @@ function textForThreePidInviteEvent(event): () => string | null {
419418
});
420419
}
421420

422-
function textForHistoryVisibilityEvent(event): () => string | null {
421+
function textForHistoryVisibilityEvent(event: MatrixEvent): () => string | null {
423422
const senderName = event.sender ? event.sender.name : event.getSender();
424423
switch (event.getContent().history_visibility) {
425424
case 'invited':
@@ -441,7 +440,7 @@ function textForHistoryVisibilityEvent(event): () => string | null {
441440
}
442441

443442
// Currently will only display a change if a user's power level is changed
444-
function textForPowerEvent(event): () => string | null {
443+
function textForPowerEvent(event: MatrixEvent): () => string | null {
445444
const senderName = event.sender ? event.sender.name : event.getSender();
446445
if (!event.getPrevContent() || !event.getPrevContent().users ||
447446
!event.getContent() || !event.getContent().users) {
@@ -523,7 +522,7 @@ function textForPinnedEvent(event: MatrixEvent, allowJSX: boolean): () => string
523522
return () => _t("%(senderName)s changed the pinned messages for the room.", { senderName });
524523
}
525524

526-
function textForWidgetEvent(event): () => string | null {
525+
function textForWidgetEvent(event: MatrixEvent): () => string | null {
527526
const senderName = event.getSender();
528527
const { name: prevName, type: prevType, url: prevUrl } = event.getPrevContent();
529528
const { name, type, url } = event.getContent() || {};
@@ -553,12 +552,12 @@ function textForWidgetEvent(event): () => string | null {
553552
}
554553
}
555554

556-
function textForWidgetLayoutEvent(event): () => string | null {
555+
function textForWidgetLayoutEvent(event: MatrixEvent): () => string | null {
557556
const senderName = event.sender?.name || event.getSender();
558557
return () => _t("%(senderName)s has updated the widget layout", { senderName });
559558
}
560559

561-
function textForMjolnirEvent(event): () => string | null {
560+
function textForMjolnirEvent(event: MatrixEvent): () => string | null {
562561
const senderName = event.getSender();
563562
const { entity: prevEntity } = event.getPrevContent();
564563
const { entity, recommendation, reason } = event.getContent();
@@ -646,7 +645,9 @@ function textForMjolnirEvent(event): () => string | null {
646645
}
647646

648647
interface IHandlers {
649-
[type: string]: (ev: MatrixEvent, allowJSX?: boolean) => (() => string | JSX.Element | null);
648+
[type: string]:
649+
(ev: MatrixEvent, allowJSX: boolean, showHiddenEvents?: boolean) =>
650+
(() => string | JSX.Element | null);
650651
}
651652

652653
const handlers: IHandlers = {
@@ -682,14 +683,27 @@ for (const evType of ALL_RULE_TYPES) {
682683
stateHandlers[evType] = textForMjolnirEvent;
683684
}
684685

685-
export function hasText(ev: MatrixEvent): boolean {
686+
/**
687+
* Determines whether the given event has text to display.
688+
* @param ev The event
689+
* @param showHiddenEvents An optional cached setting value for showHiddenEventsInTimeline
690+
* to avoid hitting the settings store
691+
*/
692+
export function hasText(ev: MatrixEvent, showHiddenEvents?: boolean): boolean {
686693
const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()];
687-
return Boolean(handler?.(ev));
694+
return Boolean(handler?.(ev, false, showHiddenEvents));
688695
}
689696

697+
/**
698+
* Gets the textual content of the given event.
699+
* @param ev The event
700+
* @param allowJSX Whether to output rich JSX content
701+
* @param showHiddenEvents An optional cached setting value for showHiddenEventsInTimeline
702+
* to avoid hitting the settings store
703+
*/
690704
export function textForEvent(ev: MatrixEvent): string;
691-
export function textForEvent(ev: MatrixEvent, allowJSX: true): string | JSX.Element;
692-
export function textForEvent(ev: MatrixEvent, allowJSX = false): string | JSX.Element {
705+
export function textForEvent(ev: MatrixEvent, allowJSX: true, showHiddenEvents?: boolean): string | JSX.Element;
706+
export function textForEvent(ev: MatrixEvent, allowJSX = false, showHiddenEvents?: boolean): string | JSX.Element {
693707
const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()];
694-
return handler?.(ev, allowJSX)?.() || '';
708+
return handler?.(ev, allowJSX, showHiddenEvents)?.() || '';
695709
}

src/components/structures/MessagePanel.tsx

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ const membershipTypes = [EventType.RoomMember, EventType.RoomThirdPartyInvite, E
5454

5555
// check if there is a previous event and it has the same sender as this event
5656
// and the types are the same/is in continuedTypes and the time between them is <= CONTINUATION_MAX_INTERVAL
57-
function shouldFormContinuation(prevEvent: MatrixEvent, mxEvent: MatrixEvent): boolean {
57+
function shouldFormContinuation(
58+
prevEvent: MatrixEvent,
59+
mxEvent: MatrixEvent,
60+
showHiddenEvents: boolean,
61+
): boolean {
5862
// sanity check inputs
5963
if (!prevEvent || !prevEvent.sender || !mxEvent.sender) return false;
6064
// check if within the max continuation period
@@ -74,7 +78,7 @@ function shouldFormContinuation(prevEvent: MatrixEvent, mxEvent: MatrixEvent): b
7478
mxEvent.sender.getMxcAvatarUrl() !== prevEvent.sender.getMxcAvatarUrl()) return false;
7579

7680
// if we don't have tile for previous event then it was shown by showHiddenEvents and has no SenderProfile
77-
if (!haveTileForEvent(prevEvent)) return false;
81+
if (!haveTileForEvent(prevEvent, showHiddenEvents)) return false;
7882

7983
return true;
8084
}
@@ -239,7 +243,8 @@ export default class MessagePanel extends React.Component<IProps, IState> {
239243
};
240244

241245
// Cache hidden events setting on mount since Settings is expensive to
242-
// query, and we check this in a hot code path.
246+
// query, and we check this in a hot code path. This is also cached in
247+
// our RoomContext, however we still need a fallback for roomless MessagePanels.
243248
this.showHiddenEventsInTimeline = SettingsStore.getValue("showHiddenEventsInTimeline");
244249

245250
this.showTypingNotificationsWatcherRef =
@@ -399,17 +404,21 @@ export default class MessagePanel extends React.Component<IProps, IState> {
399404
return !this.isMounted;
400405
};
401406

407+
private get showHiddenEvents(): boolean {
408+
return this.context?.showHiddenEventsInTimeline ?? this.showHiddenEventsInTimeline;
409+
}
410+
402411
// TODO: Implement granular (per-room) hide options
403412
public shouldShowEvent(mxEv: MatrixEvent): boolean {
404413
if (MatrixClientPeg.get().isUserIgnored(mxEv.getSender())) {
405414
return false; // ignored = no show (only happens if the ignore happens after an event was received)
406415
}
407416

408-
if (this.showHiddenEventsInTimeline) {
417+
if (this.showHiddenEvents) {
409418
return true;
410419
}
411420

412-
if (!haveTileForEvent(mxEv)) {
421+
if (!haveTileForEvent(mxEv, this.showHiddenEvents)) {
413422
return false; // no tile = no show
414423
}
415424

@@ -569,7 +578,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
569578

570579
if (grouper) {
571580
if (grouper.shouldGroup(mxEv)) {
572-
grouper.add(mxEv);
581+
grouper.add(mxEv, this.showHiddenEvents);
573582
continue;
574583
} else {
575584
// not part of group, so get the group tiles, close the
@@ -649,7 +658,8 @@ export default class MessagePanel extends React.Component<IProps, IState> {
649658
}
650659

651660
// is this a continuation of the previous message?
652-
const continuation = !wantsDateSeparator && shouldFormContinuation(prevEvent, mxEv);
661+
const continuation = !wantsDateSeparator &&
662+
shouldFormContinuation(prevEvent, mxEv, this.showHiddenEvents);
653663

654664
const eventId = mxEv.getId();
655665
const highlight = (eventId === this.props.highlightedEventId);
@@ -946,7 +956,7 @@ abstract class BaseGrouper {
946956
}
947957

948958
public abstract shouldGroup(ev: MatrixEvent): boolean;
949-
public abstract add(ev: MatrixEvent): void;
959+
public abstract add(ev: MatrixEvent, showHiddenEvents?: boolean): void;
950960
public abstract getTiles(): ReactNode[];
951961
public abstract getNewPrevEvent(): MatrixEvent;
952962
}
@@ -1200,10 +1210,10 @@ class MemberGrouper extends BaseGrouper {
12001210
return membershipTypes.includes(ev.getType() as EventType);
12011211
}
12021212

1203-
public add(ev: MatrixEvent): void {
1213+
public add(ev: MatrixEvent, showHiddenEvents?: boolean): void {
12041214
if (ev.getType() === EventType.RoomMember) {
12051215
// We can ignore any events that don't actually have a message to display
1206-
if (!hasText(ev)) return;
1216+
if (!hasText(ev, showHiddenEvents)) return;
12071217
}
12081218
this.readMarker = this.readMarker || this.panel.readMarkerForEvent(
12091219
ev.getId(),

src/components/structures/RoomView.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ export interface IState {
166166
canReply: boolean;
167167
layout: Layout;
168168
lowBandwidth: boolean;
169+
showHiddenEventsInTimeline: boolean;
169170
showReadReceipts: boolean;
170171
showRedactions: boolean;
171172
showJoinLeaves: boolean;
@@ -230,6 +231,7 @@ export default class RoomView extends React.Component<IProps, IState> {
230231
canReply: false,
231232
layout: SettingsStore.getValue("layout"),
232233
lowBandwidth: SettingsStore.getValue("lowBandwidth"),
234+
showHiddenEventsInTimeline: SettingsStore.getValue("showHiddenEventsInTimeline"),
233235
showReadReceipts: true,
234236
showRedactions: true,
235237
showJoinLeaves: true,
@@ -267,6 +269,9 @@ export default class RoomView extends React.Component<IProps, IState> {
267269
SettingsStore.watchSetting("lowBandwidth", null, () =>
268270
this.setState({ lowBandwidth: SettingsStore.getValue("lowBandwidth") }),
269271
),
272+
SettingsStore.watchSetting("showHiddenEventsInTimeline", null, () =>
273+
this.setState({ showHiddenEventsInTimeline: SettingsStore.getValue("showHiddenEventsInTimeline") }),
274+
),
270275
];
271276
}
272277

@@ -1388,7 +1393,7 @@ export default class RoomView extends React.Component<IProps, IState> {
13881393
continue;
13891394
}
13901395

1391-
if (!haveTileForEvent(mxEv)) {
1396+
if (!haveTileForEvent(mxEv, this.state.showHiddenEventsInTimeline)) {
13921397
// XXX: can this ever happen? It will make the result count
13931398
// not match the displayed count.
13941399
continue;

src/components/structures/TimelinePanel.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1337,7 +1337,8 @@ class TimelinePanel extends React.Component<IProps, IState> {
13371337

13381338
const shouldIgnore = !!ev.status || // local echo
13391339
(ignoreOwn && ev.getSender() === myUserId); // own message
1340-
const isWithoutTile = !haveTileForEvent(ev) || shouldHideEvent(ev, this.context);
1340+
const isWithoutTile = !haveTileForEvent(ev, this.context?.showHiddenEventsInTimeline) ||
1341+
shouldHideEvent(ev, this.context);
13411342

13421343
if (isWithoutTile || !node) {
13431344
// don't start counting if the event should be ignored,

src/components/views/messages/TextualEvent.tsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
import React from 'react';
18-
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
17+
import React from "react";
18+
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
1919

20+
import RoomContext from "../../../contexts/RoomContext";
2021
import * as TextForEvent from "../../../TextForEvent";
2122
import { replaceableComponent } from "../../../utils/replaceableComponent";
2223

@@ -26,11 +27,11 @@ interface IProps {
2627

2728
@replaceableComponent("views.messages.TextualEvent")
2829
export default class TextualEvent extends React.Component<IProps> {
29-
render() {
30-
const text = TextForEvent.textForEvent(this.props.mxEvent, true);
31-
if (!text || (text as string).length === 0) return null;
32-
return (
33-
<div className="mx_TextualEvent">{ text }</div>
34-
);
30+
static contextType = RoomContext;
31+
32+
public render() {
33+
const text = TextForEvent.textForEvent(this.props.mxEvent, true, this.context?.showHiddenEventsInTimeline);
34+
if (!text) return null;
35+
return <div className="mx_TextualEvent">{ text }</div>;
3536
}
3637
}

src/components/views/rooms/EventTile.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,7 +1160,7 @@ function isMessageEvent(ev) {
11601160
return (messageTypes.includes(ev.getType()));
11611161
}
11621162

1163-
export function haveTileForEvent(e) {
1163+
export function haveTileForEvent(e: MatrixEvent, showHiddenEvents?: boolean) {
11641164
// Only messages have a tile (black-rectangle) if redacted
11651165
if (e.isRedacted() && !isMessageEvent(e)) return false;
11661166

@@ -1170,7 +1170,7 @@ export function haveTileForEvent(e) {
11701170
const handler = getHandlerTile(e);
11711171
if (handler === undefined) return false;
11721172
if (handler === 'messages.TextualEvent') {
1173-
return hasText(e);
1173+
return hasText(e, showHiddenEvents);
11741174
} else if (handler === 'messages.RoomCreate') {
11751175
return Boolean(e.getContent()['predecessor']);
11761176
} else {

0 commit comments

Comments
 (0)