From f77d9b4bcb3babc433da1cd61fff4c88ac46e4b0 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 13 Nov 2024 13:39:56 +0000 Subject: [PATCH] Refactor TimelinePanel to avoid race conditions in React 18 between state updates (#28416) Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/TimelinePanel.tsx | 23 +++++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/components/structures/TimelinePanel.tsx b/src/components/structures/TimelinePanel.tsx index 68b65965f5..1063cceffd 100644 --- a/src/components/structures/TimelinePanel.tsx +++ b/src/components/structures/TimelinePanel.tsx @@ -1217,7 +1217,7 @@ class TimelinePanel extends React.Component { return; } const lastDisplayedEvent = this.state.events[lastDisplayedIndex]; - this.setReadMarker(lastDisplayedEvent.getId()!, lastDisplayedEvent.getTs()); + await this.setReadMarker(lastDisplayedEvent.getId()!, lastDisplayedEvent.getTs()); // the read-marker should become invisible, so that if the user scrolls // down, they don't see it. @@ -1335,7 +1335,7 @@ class TimelinePanel extends React.Component { } // Update the read marker to the values we found - this.setReadMarker(rmId, rmTs); + await this.setReadMarker(rmId, rmTs); // Send the receipts to the server immediately (don't wait for activity) await this.sendReadReceipts(); @@ -1866,7 +1866,7 @@ class TimelinePanel extends React.Component { return receiptStore?.getEventReadUpTo(myUserId, ignoreSynthesized) ?? null; } - private setReadMarker(eventId: string | null, eventTs?: number, inhibitSetState = false): void { + private async setReadMarker(eventId: string | null, eventTs?: number, inhibitSetState = false): Promise { const roomId = this.props.timelineSet.room?.roomId; // don't update the state (and cause a re-render) if there is @@ -1890,12 +1890,17 @@ class TimelinePanel extends React.Component { // Do the local echo of the RM // run the render cycle before calling the callback, so that // getReadMarkerPosition() returns the right thing. - this.setState( - { - readMarkerEventId: eventId, - }, - this.props.onReadMarkerUpdated, - ); + await new Promise((resolve) => { + this.setState( + { + readMarkerEventId: eventId, + }, + () => { + this.props.onReadMarkerUpdated?.(); + resolve(); + }, + ); + }); } private shouldPaginate(): boolean {