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

Commit f2249b3

Browse files
authored
Fix CallEventGrouper map building to not occur during a Render phase (#7638)
1 parent ae49084 commit f2249b3

File tree

4 files changed

+45
-23
lines changed

4 files changed

+45
-23
lines changed

src/components/structures/CallEventGrouper.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ export default class CallEventGrouper extends EventEmitter {
170170
};
171171

172172
public add(event: MatrixEvent) {
173+
if (this.events.has(event)) return; // nothing to do
173174
this.events.add(event);
174175
this.setCall();
175176
}

src/components/structures/MessagePanel.tsx

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ interface IProps {
183183

184184
hideThreadedMessages?: boolean;
185185
disableGrouping?: boolean;
186+
187+
callEventGroupers: Map<string, CallEventGrouper>;
186188
}
187189

188190
interface IState {
@@ -254,9 +256,6 @@ export default class MessagePanel extends React.Component<IProps, IState> {
254256
private readonly showTypingNotificationsWatcherRef: string;
255257
private eventTiles: Record<string, EventTile> = {};
256258

257-
// A map of <callId, CallEventGrouper>
258-
private callEventGroupers = new Map<string, CallEventGrouper>();
259-
260259
constructor(props, context) {
261260
super(props, context);
262261

@@ -650,20 +649,6 @@ export default class MessagePanel extends React.Component<IProps, IState> {
650649
const last = (mxEv === lastShownEvent);
651650
const { nextEvent, nextTile } = this.getNextEventInfo(this.props.events, i);
652651

653-
if (
654-
mxEv.getType().indexOf("m.call.") === 0 ||
655-
mxEv.getType().indexOf("org.matrix.call.") === 0
656-
) {
657-
const callId = mxEv.getContent().call_id;
658-
if (this.callEventGroupers.has(callId)) {
659-
this.callEventGroupers.get(callId).add(mxEv);
660-
} else {
661-
const callEventGrouper = new CallEventGrouper();
662-
callEventGrouper.add(mxEv);
663-
this.callEventGroupers.set(callId, callEventGrouper);
664-
}
665-
}
666-
667652
if (grouper) {
668653
if (grouper.shouldGroup(mxEv)) {
669654
grouper.add(mxEv, this.showHiddenEvents);
@@ -784,7 +769,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
784769
// it's successful: we received it.
785770
isLastSuccessful = isLastSuccessful && mxEv.getSender() === MatrixClientPeg.get().getUserId();
786771

787-
const callEventGrouper = this.callEventGroupers.get(mxEv.getContent().call_id);
772+
const callEventGrouper = this.props.callEventGroupers.get(mxEv.getContent().call_id);
788773
// use txnId as key if available so that we don't remount during sending
789774
ret.push(
790775
<TileErrorBoundary key={mxEv.getTxnId() || eventId} mxEvent={mxEv}>

src/components/structures/TimelinePanel.tsx

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks";
5151
import Spinner from "../views/elements/Spinner";
5252
import EditorStateTransfer from '../../utils/EditorStateTransfer';
5353
import ErrorDialog from '../views/dialogs/ErrorDialog';
54+
import CallEventGrouper from "./CallEventGrouper";
5455

5556
const PAGINATE_SIZE = 20;
5657
const INITIAL_SIZE = 20;
@@ -237,6 +238,9 @@ class TimelinePanel extends React.Component<IProps, IState> {
237238
private readReceiptActivityTimer: Timer;
238239
private readMarkerActivityTimer: Timer;
239240

241+
// A map of <callId, CallEventGrouper>
242+
private callEventGroupers = new Map<string, CallEventGrouper>();
243+
240244
constructor(props, context) {
241245
super(props, context);
242246

@@ -388,6 +392,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
388392
this.timelineWindow.unpaginate(count, backwards);
389393

390394
const { events, liveEvents, firstVisibleEventIndex } = this.getEvents();
395+
this.buildCallEventGroupers(events);
391396
const newState: Partial<IState> = {
392397
events,
393398
liveEvents,
@@ -449,6 +454,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
449454
debuglog("paginate complete backwards:"+backwards+"; success:"+r);
450455

451456
const { events, liveEvents, firstVisibleEventIndex } = this.getEvents();
457+
this.buildCallEventGroupers(events);
452458
const newState: Partial<IState> = {
453459
[paginatingKey]: false,
454460
[canPaginateKey]: r,
@@ -561,6 +567,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
561567
if (this.unmounted) { return; }
562568

563569
const { events, liveEvents, firstVisibleEventIndex } = this.getEvents();
570+
this.buildCallEventGroupers(events);
564571
const lastLiveEvent = liveEvents[liveEvents.length - 1];
565572

566573
const updatedState: Partial<IState> = {
@@ -1231,6 +1238,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
12311238
canForwardPaginate: false,
12321239
timelineLoading: true,
12331240
});
1241+
this.buildCallEventGroupers();
12341242
prom.then(onLoaded, onError);
12351243
}
12361244
}
@@ -1243,7 +1251,9 @@ class TimelinePanel extends React.Component<IProps, IState> {
12431251
// the results if so.
12441252
if (this.unmounted) return;
12451253

1246-
this.setState(this.getEvents());
1254+
const state = this.getEvents();
1255+
this.buildCallEventGroupers(state.events);
1256+
this.setState(state);
12471257
}
12481258

12491259
// Force refresh the timeline before threads support pending events
@@ -1512,6 +1522,27 @@ class TimelinePanel extends React.Component<IProps, IState> {
15121522
eventType: EventType | string,
15131523
) => this.props.timelineSet.getRelationsForEvent(eventId, relationType, eventType);
15141524

1525+
private buildCallEventGroupers(events?: MatrixEvent[]): void {
1526+
const oldCallEventGroupers = this.callEventGroupers;
1527+
this.callEventGroupers = new Map();
1528+
events?.forEach(ev => {
1529+
if (!ev.getType().startsWith("m.call.") && !ev.getType().startsWith("org.matrix.call.")) {
1530+
return;
1531+
}
1532+
1533+
const callId = ev.getContent().call_id;
1534+
if (!this.callEventGroupers.has(callId)) {
1535+
if (oldCallEventGroupers.has(callId)) {
1536+
// reuse the CallEventGrouper object where possible
1537+
this.callEventGroupers.set(callId, oldCallEventGroupers.get(callId));
1538+
} else {
1539+
this.callEventGroupers.set(callId, new CallEventGrouper());
1540+
}
1541+
}
1542+
this.callEventGroupers.get(callId).add(ev);
1543+
});
1544+
}
1545+
15151546
render() {
15161547
// just show a spinner while the timeline loads.
15171548
//
@@ -1596,6 +1627,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
15961627
enableFlair={SettingsStore.getValue(UIFeature.Flair)}
15971628
hideThreadedMessages={this.props.hideThreadedMessages}
15981629
disableGrouping={this.props.disableGrouping}
1630+
callEventGroupers={this.callEventGroupers}
15991631
/>
16001632
);
16011633
}

test/components/structures/MessagePanel-test.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,8 @@ const room = new Matrix.Room("!roomId:server_name");
4141

4242
// wrap MessagePanel with a component which provides the MatrixClient in the context.
4343
class WrappedMessagePanel extends React.Component {
44-
state = {
45-
resizeNotifier: new EventEmitter(),
46-
};
44+
resizeNotifier = new EventEmitter();
45+
callEventGroupers = new Map();
4746

4847
render() {
4948
const roomContext = {
@@ -60,7 +59,12 @@ class WrappedMessagePanel extends React.Component {
6059

6160
return <MatrixClientContext.Provider value={client}>
6261
<RoomContext.Provider value={roomContext}>
63-
<MessagePanel room={room} {...this.props} resizeNotifier={this.state.resizeNotifier} />
62+
<MessagePanel
63+
room={room}
64+
{...this.props}
65+
resizeNotifier={this.resizeNotifier}
66+
callEventGroupers={this.callEventGroupers}
67+
/>
6468
</RoomContext.Provider>
6569
</MatrixClientContext.Provider>;
6670
}

0 commit comments

Comments
 (0)