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

Commit 7335b35

Browse files
robintownturt2live
andauthored
Avoid looking up settings during timeline rendering (#8313)
* Avoid showHiddenEventsInTimeline lookups * Avoid MSC3531 feature lookups * Test that showHiddenEventsInTimeline doesn't get looked up while rendering * Fix code review nits Co-authored-by: Travis Ralston <[email protected]>
1 parent f27386e commit 7335b35

20 files changed

+120
-54
lines changed

src/Unread.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export function eventTriggersUnreadCount(ev: MatrixEvent): boolean {
4848
}
4949

5050
if (ev.isRedacted()) return false;
51-
return haveRendererForEvent(ev);
51+
return haveRendererForEvent(ev, false /* hidden messages should never trigger unread counts anyways */);
5252
}
5353

5454
export function doesRoomHaveUnreadMessages(room: Room): boolean {

src/components/structures/MessagePanel.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
242242
// displayed event in the current render cycle.
243243
private readReceiptsByUserId: Record<string, IReadReceiptForUser> = {};
244244

245-
private readonly showHiddenEventsInTimeline: boolean;
245+
private readonly _showHiddenEvents: boolean;
246246
private readonly threadsEnabled: boolean;
247247
private isMounted = false;
248248

@@ -270,7 +270,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
270270
// Cache these settings on mount since Settings is expensive to query,
271271
// and we check this in a hot code path. This is also cached in our
272272
// RoomContext, however we still need a fallback for roomless MessagePanels.
273-
this.showHiddenEventsInTimeline = SettingsStore.getValue("showHiddenEventsInTimeline");
273+
this._showHiddenEvents = SettingsStore.getValue("showHiddenEventsInTimeline");
274274
this.threadsEnabled = SettingsStore.getValue("feature_thread");
275275

276276
this.showTypingNotificationsWatcherRef =
@@ -465,7 +465,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
465465
};
466466

467467
public get showHiddenEvents(): boolean {
468-
return this.context?.showHiddenEventsInTimeline ?? this.showHiddenEventsInTimeline;
468+
return this.context?.showHiddenEvents ?? this._showHiddenEvents;
469469
}
470470

471471
// TODO: Implement granular (per-room) hide options
@@ -748,7 +748,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
748748
const willWantDateSeparator = this.wantsDateSeparator(mxEv, nextEv.getDate() || new Date());
749749
lastInSection = willWantDateSeparator ||
750750
mxEv.getSender() !== nextEv.getSender() ||
751-
getEventDisplayInfo(nextEv).isInfoMessage ||
751+
getEventDisplayInfo(nextEv, this.showHiddenEvents).isInfoMessage ||
752752
!shouldFormContinuation(
753753
mxEv, nextEv, this.showHiddenEvents, this.threadsEnabled, this.context.timelineRenderingType,
754754
);

src/components/structures/RoomView.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ export interface IRoomState {
199199
showTwelveHourTimestamps: boolean;
200200
readMarkerInViewThresholdMs: number;
201201
readMarkerOutOfViewThresholdMs: number;
202-
showHiddenEventsInTimeline: boolean;
202+
showHiddenEvents: boolean;
203203
showReadReceipts: boolean;
204204
showRedactions: boolean;
205205
showJoinLeaves: boolean;
@@ -271,7 +271,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
271271
showTwelveHourTimestamps: SettingsStore.getValue("showTwelveHourTimestamps"),
272272
readMarkerInViewThresholdMs: SettingsStore.getValue("readMarkerInViewThresholdMs"),
273273
readMarkerOutOfViewThresholdMs: SettingsStore.getValue("readMarkerOutOfViewThresholdMs"),
274-
showHiddenEventsInTimeline: SettingsStore.getValue("showHiddenEventsInTimeline"),
274+
showHiddenEvents: SettingsStore.getValue("showHiddenEventsInTimeline"),
275275
showReadReceipts: true,
276276
showRedactions: true,
277277
showJoinLeaves: true,
@@ -328,7 +328,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
328328
this.setState({ readMarkerOutOfViewThresholdMs: value as number }),
329329
),
330330
SettingsStore.watchSetting("showHiddenEventsInTimeline", null, (...[,,, value]) =>
331-
this.setState({ showHiddenEventsInTimeline: value as boolean }),
331+
this.setState({ showHiddenEvents: value as boolean }),
332332
),
333333
];
334334
}
@@ -1480,7 +1480,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
14801480
continue;
14811481
}
14821482

1483-
if (!haveRendererForEvent(mxEv, this.state.showHiddenEventsInTimeline)) {
1483+
if (!haveRendererForEvent(mxEv, this.state.showHiddenEvents)) {
14841484
// XXX: can this ever happen? It will make the result count
14851485
// not match the displayed count.
14861486
continue;

src/components/structures/ThreadPanel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ const ThreadPanel: React.FC<IProps> = ({
250250
<RoomContext.Provider value={{
251251
...roomContext,
252252
timelineRenderingType: TimelineRenderingType.ThreadsList,
253-
showHiddenEventsInTimeline: true,
253+
showHiddenEvents: true,
254254
narrow,
255255
}}>
256256
<BaseCard

src/components/structures/TimelinePanel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1511,7 +1511,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
15111511

15121512
const shouldIgnore = !!ev.status || // local echo
15131513
(ignoreOwn && ev.getSender() === myUserId); // own message
1514-
const isWithoutTile = !haveRendererForEvent(ev, this.context?.showHiddenEventsInTimeline) ||
1514+
const isWithoutTile = !haveRendererForEvent(ev, this.context?.showHiddenEvents) ||
15151515
shouldHideEvent(ev, this.context);
15161516

15171517
if (isWithoutTile || !node) {

src/components/views/messages/TextualEvent.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export default class TextualEvent extends React.Component<IProps> {
2828
static contextType = RoomContext;
2929

3030
public render() {
31-
const text = TextForEvent.textForEvent(this.props.mxEvent, true, this.context?.showHiddenEventsInTimeline);
31+
const text = TextForEvent.textForEvent(this.props.mxEvent, true, this.context?.showHiddenEvents);
3232
if (!text) return null;
3333
return <div className="mx_TextualEvent">{ text }</div>;
3434
}

src/components/views/rooms/EventTile.tsx

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,21 +1214,24 @@ export class UnwrappedEventTile extends React.Component<IProps, IState> {
12141214
msgOption = readAvatars;
12151215
}
12161216

1217-
const replyChain =
1218-
(haveRendererForEvent(this.props.mxEvent) && shouldDisplayReply(this.props.mxEvent))
1219-
? <ReplyChain
1220-
parentEv={this.props.mxEvent}
1221-
onHeightChanged={this.props.onHeightChanged}
1222-
ref={this.replyChain}
1223-
forExport={this.props.forExport}
1224-
permalinkCreator={this.props.permalinkCreator}
1225-
layout={this.props.layout}
1226-
alwaysShowTimestamps={this.props.alwaysShowTimestamps || this.state.hover}
1227-
isQuoteExpanded={isQuoteExpanded}
1228-
setQuoteExpanded={this.setQuoteExpanded}
1229-
getRelationsForEvent={this.props.getRelationsForEvent}
1230-
/>
1231-
: null;
1217+
let replyChain;
1218+
if (
1219+
haveRendererForEvent(this.props.mxEvent, this.context.showHiddenEvents) &&
1220+
shouldDisplayReply(this.props.mxEvent)
1221+
) {
1222+
replyChain = <ReplyChain
1223+
parentEv={this.props.mxEvent}
1224+
onHeightChanged={this.props.onHeightChanged}
1225+
ref={this.replyChain}
1226+
forExport={this.props.forExport}
1227+
permalinkCreator={this.props.permalinkCreator}
1228+
layout={this.props.layout}
1229+
alwaysShowTimestamps={this.props.alwaysShowTimestamps || this.state.hover}
1230+
isQuoteExpanded={isQuoteExpanded}
1231+
setQuoteExpanded={this.setQuoteExpanded}
1232+
getRelationsForEvent={this.props.getRelationsForEvent}
1233+
/>;
1234+
}
12321235

12331236
const isOwnEvent = this.props.mxEvent?.sender?.userId === MatrixClientPeg.get().getUserId();
12341237

@@ -1267,7 +1270,7 @@ export class UnwrappedEventTile extends React.Component<IProps, IState> {
12671270
highlightLink: this.props.highlightLink,
12681271
onHeightChanged: this.props.onHeightChanged,
12691272
permalinkCreator: this.props.permalinkCreator,
1270-
}) }
1273+
}, this.context.showHiddenEvents) }
12711274
</div>,
12721275
]);
12731276
}
@@ -1309,7 +1312,7 @@ export class UnwrappedEventTile extends React.Component<IProps, IState> {
13091312
highlightLink: this.props.highlightLink,
13101313
onHeightChanged: this.props.onHeightChanged,
13111314
permalinkCreator: this.props.permalinkCreator,
1312-
}) }
1315+
}, this.context.showHiddenEvents) }
13131316
{ actionBar }
13141317
<a href={permalink} onClick={this.onPermalinkClicked}>
13151318
{ timestamp }
@@ -1395,7 +1398,7 @@ export class UnwrappedEventTile extends React.Component<IProps, IState> {
13951398
highlightLink: this.props.highlightLink,
13961399
onHeightChanged: this.props.onHeightChanged,
13971400
permalinkCreator: this.props.permalinkCreator,
1398-
}) }
1401+
}, this.context.showHiddenEvents) }
13991402
</div>,
14001403
<a
14011404
className="mx_EventTile_senderDetailsLink"
@@ -1448,7 +1451,7 @@ export class UnwrappedEventTile extends React.Component<IProps, IState> {
14481451
highlightLink: this.props.highlightLink,
14491452
onHeightChanged: this.props.onHeightChanged,
14501453
permalinkCreator: this.props.permalinkCreator,
1451-
}) }
1454+
}, this.context.showHiddenEvents) }
14521455
{ keyRequestInfo }
14531456
{ actionBar }
14541457
{ this.props.layout === Layout.IRC && <>

src/components/views/rooms/ReplyTile.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,9 @@ export default class ReplyTile extends React.PureComponent<IProps> {
110110
const msgType = mxEvent.getContent().msgtype;
111111
const evType = mxEvent.getType() as EventType;
112112

113-
const { hasRenderer, isInfoMessage, isSeeingThroughMessageHiddenForModeration } = getEventDisplayInfo(mxEvent);
113+
const {
114+
hasRenderer, isInfoMessage, isSeeingThroughMessageHiddenForModeration,
115+
} = getEventDisplayInfo(mxEvent, false /* Replies are never hidden, so this should be fine */);
114116
// This shouldn't happen: the caller should check we support this type
115117
// before trying to instantiate us
116118
if (!hasRenderer) {
@@ -177,7 +179,7 @@ export default class ReplyTile extends React.PureComponent<IProps> {
177179
highlightLink: this.props.highlightLink,
178180
onHeightChanged: this.props.onHeightChanged,
179181
permalinkCreator: this.props.permalinkCreator,
180-
}) }
182+
}, false /* showHiddenEvents shouldn't be relevant */) }
181183
</a>
182184
</div>
183185
);

src/components/views/rooms/SearchResultTile.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export default class SearchResultTile extends React.Component<IProps> {
7878
highlights = this.props.searchHighlights;
7979
}
8080

81-
if (haveRendererForEvent(mxEv, this.context?.showHiddenEventsInTimeline)) {
81+
if (haveRendererForEvent(mxEv, this.context?.showHiddenEvents)) {
8282
// do we need a date separator since the last event?
8383
const prevEv = timeline[j - 1];
8484
// is this a continuation of the previous message?
@@ -87,7 +87,7 @@ export default class SearchResultTile extends React.Component<IProps> {
8787
shouldFormContinuation(
8888
prevEv,
8989
mxEv,
90-
this.context?.showHiddenEventsInTimeline,
90+
this.context?.showHiddenEvents,
9191
threadsEnabled,
9292
TimelineRenderingType.Search,
9393
);
@@ -102,7 +102,7 @@ export default class SearchResultTile extends React.Component<IProps> {
102102
!shouldFormContinuation(
103103
mxEv,
104104
nextEv,
105-
this.context?.showHiddenEventsInTimeline,
105+
this.context?.showHiddenEvents,
106106
threadsEnabled,
107107
TimelineRenderingType.Search,
108108
)

src/contexts/RoomContext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const RoomContext = createContext<IRoomState>({
5454
showTwelveHourTimestamps: false,
5555
readMarkerInViewThresholdMs: 3000,
5656
readMarkerOutOfViewThresholdMs: 30000,
57-
showHiddenEventsInTimeline: false,
57+
showHiddenEvents: false,
5858
showReadReceipts: true,
5959
showRedactions: true,
6060
showJoinLeaves: true,

src/events/EventTileFactory.tsx

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,19 +141,25 @@ const SINGULAR_STATE_EVENTS = new Set([
141141
* Find an event tile factory for the given conditions.
142142
* @param mxEvent The event.
143143
* @param cli The matrix client to reference when needed.
144+
* @param showHiddenEvents Whether hidden events should be shown.
144145
* @param asHiddenEv When true, treat the event as always hidden.
145146
* @returns The factory, or falsy if not possible.
146147
*/
147-
export function pickFactory(mxEvent: MatrixEvent, cli: MatrixClient, asHiddenEv?: boolean): Optional<Factory> {
148+
export function pickFactory(
149+
mxEvent: MatrixEvent,
150+
cli: MatrixClient,
151+
showHiddenEvents: boolean,
152+
asHiddenEv?: boolean,
153+
): Optional<Factory> {
148154
const evType = mxEvent.getType(); // cache this to reduce call stack execution hits
149155

150156
// Note: we avoid calling SettingsStore unless absolutely necessary - this code is on the critical path.
151157

152-
if (asHiddenEv && SettingsStore.getValue("showHiddenEventsInTimeline")) {
158+
if (asHiddenEv && showHiddenEvents) {
153159
return JSONEventFactory;
154160
}
155161

156-
const noEventFactoryFactory: (() => Optional<Factory>) = () => SettingsStore.getValue("showHiddenEventsInTimeline")
162+
const noEventFactoryFactory: (() => Optional<Factory>) = () => showHiddenEvents
157163
? JSONEventFactory
158164
: undefined; // just don't render things that we shouldn't render
159165

@@ -242,17 +248,19 @@ export function pickFactory(mxEvent: MatrixEvent, cli: MatrixClient, asHiddenEv?
242248
* Render an event as a tile
243249
* @param renderType The render type. Used to inform properties given to the eventual component.
244250
* @param props The properties to provide to the eventual component.
251+
* @param showHiddenEvents Whether hidden events should be shown.
245252
* @param cli Optional client instance to use, otherwise the default MatrixClientPeg will be used.
246253
* @returns The tile as JSX, or falsy if unable to render.
247254
*/
248255
export function renderTile(
249256
renderType: TimelineRenderingType,
250257
props: EventTileTypeProps,
258+
showHiddenEvents: boolean,
251259
cli?: MatrixClient,
252260
): Optional<JSX.Element> {
253261
cli = cli ?? MatrixClientPeg.get(); // because param defaults don't do the correct thing
254262

255-
const factory = pickFactory(props.mxEvent, cli);
263+
const factory = pickFactory(props.mxEvent, cli, showHiddenEvents);
256264
if (!factory) return undefined;
257265

258266
// Note that we split off the ones we actually care about here just to be sure that we're
@@ -316,16 +324,18 @@ export function renderTile(
316324
/**
317325
* A version of renderTile() specifically for replies.
318326
* @param props The properties to specify on the eventual object.
327+
* @param showHiddenEvents Whether hidden events should be shown.
319328
* @param cli Optional client instance to use, otherwise the default MatrixClientPeg will be used.
320329
* @returns The tile as JSX, or falsy if unable to render.
321330
*/
322331
export function renderReplyTile(
323332
props: EventTileTypeProps,
333+
showHiddenEvents: boolean,
324334
cli?: MatrixClient,
325335
): Optional<JSX.Element> {
326336
cli = cli ?? MatrixClientPeg.get(); // because param defaults don't do the correct thing
327337

328-
const factory = pickFactory(props.mxEvent, cli);
338+
const factory = pickFactory(props.mxEvent, cli, showHiddenEvents);
329339
if (!factory) return undefined;
330340

331341
// See renderTile() for why we split off so much
@@ -367,7 +377,7 @@ export function isMessageEvent(ev: MatrixEvent): boolean {
367377
return (messageTypes.includes(ev.getType() as EventType)) || M_POLL_START.matches(ev.getType());
368378
}
369379

370-
export function haveRendererForEvent(mxEvent: MatrixEvent, showHiddenEvents?: boolean): boolean {
380+
export function haveRendererForEvent(mxEvent: MatrixEvent, showHiddenEvents: boolean): boolean {
371381
// Only show "Message deleted" tile for plain message events, encrypted events,
372382
// and state events as they'll likely still contain enough keys to be relevant.
373383
if (mxEvent.isRedacted() && !mxEvent.isEncrypted() && !isMessageEvent(mxEvent) && !mxEvent.isState()) {
@@ -377,7 +387,7 @@ export function haveRendererForEvent(mxEvent: MatrixEvent, showHiddenEvents?: bo
377387
// No tile for replacement events since they update the original tile
378388
if (mxEvent.isRelation(RelationType.Replace)) return false;
379389

380-
const handler = pickFactory(mxEvent, MatrixClientPeg.get());
390+
const handler = pickFactory(mxEvent, MatrixClientPeg.get(), showHiddenEvents);
381391
if (!handler) return false;
382392
if (handler === TextualEventFactory) {
383393
return hasText(mxEvent, showHiddenEvents);

src/settings/Settings.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ export const SETTINGS: {[setting: string]: ISetting} = {
187187
"feature_msc3531_hide_messages_pending_moderation": {
188188
isFeature: true,
189189
labsGroup: LabGroup.Moderation,
190+
// Requires a reload since this setting is cached in EventUtils
191+
controller: new ReloadOnChangeController(),
190192
displayName: _td("Let moderators hide messages pending moderation."),
191193
supportedLevels: LEVELS_FEATURE,
192194
default: false,

src/utils/EventRenderingUtils.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import { haveRendererForEvent, JitsiEventFactory, JSONEventFactory, pickFactory
2525
import { MatrixClientPeg } from "../MatrixClientPeg";
2626
import { getMessageModerationState, MessageModerationState } from "./EventUtils";
2727

28-
export function getEventDisplayInfo(mxEvent: MatrixEvent, hideEvent?: boolean): {
28+
export function getEventDisplayInfo(mxEvent: MatrixEvent, showHiddenEvents: boolean, hideEvent?: boolean): {
2929
isInfoMessage: boolean;
3030
hasRenderer: boolean;
3131
isBubbleMessage: boolean;
@@ -52,7 +52,7 @@ export function getEventDisplayInfo(mxEvent: MatrixEvent, hideEvent?: boolean):
5252
}
5353

5454
// TODO: Thread a MatrixClient through to here
55-
let factory = pickFactory(mxEvent, MatrixClientPeg.get());
55+
let factory = pickFactory(mxEvent, MatrixClientPeg.get(), showHiddenEvents);
5656

5757
// Info messages are basically information about commands processed on a room
5858
let isBubbleMessage = (
@@ -92,11 +92,11 @@ export function getEventDisplayInfo(mxEvent: MatrixEvent, hideEvent?: boolean):
9292
// source tile when there's no regular tile for an event and also for
9393
// replace relations (which otherwise would display as a confusing
9494
// duplicate of the thing they are replacing).
95-
if (hideEvent || !haveRendererForEvent(mxEvent)) {
95+
if (hideEvent || !haveRendererForEvent(mxEvent, showHiddenEvents)) {
9696
// forcefully ask for a factory for a hidden event (hidden event
9797
// setting is checked internally)
9898
// TODO: Thread a MatrixClient through to here
99-
factory = pickFactory(mxEvent, MatrixClientPeg.get(), true);
99+
factory = pickFactory(mxEvent, MatrixClientPeg.get(), showHiddenEvents, true);
100100
if (factory === JSONEventFactory) {
101101
isBubbleMessage = false;
102102
// Reuse info message avatar and sender profile styling

0 commit comments

Comments
 (0)