Compare commits

...

1 Commits

Author SHA1 Message Date
Michael Telatynski
b357f5791f Refactor TimelinePanel to avoid race conditions in React 18 between state updates
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
2024-11-11 11:00:49 +00:00

View File

@@ -1217,7 +1217,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
return; return;
} }
const lastDisplayedEvent = this.state.events[lastDisplayedIndex]; 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 // the read-marker should become invisible, so that if the user scrolls
// down, they don't see it. // down, they don't see it.
@@ -1335,7 +1335,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
} }
// Update the read marker to the values we found // 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) // Send the receipts to the server immediately (don't wait for activity)
await this.sendReadReceipts(); await this.sendReadReceipts();
@@ -1866,7 +1866,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
return receiptStore?.getEventReadUpTo(myUserId, ignoreSynthesized) ?? null; 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<void> {
const roomId = this.props.timelineSet.room?.roomId; const roomId = this.props.timelineSet.room?.roomId;
// don't update the state (and cause a re-render) if there is // don't update the state (and cause a re-render) if there is
@@ -1890,12 +1890,17 @@ class TimelinePanel extends React.Component<IProps, IState> {
// Do the local echo of the RM // Do the local echo of the RM
// run the render cycle before calling the callback, so that // run the render cycle before calling the callback, so that
// getReadMarkerPosition() returns the right thing. // getReadMarkerPosition() returns the right thing.
this.setState( await new Promise<void>((resolve) => {
{ this.setState(
readMarkerEventId: eventId, {
}, readMarkerEventId: eventId,
this.props.onReadMarkerUpdated, },
); () => {
this.props.onReadMarkerUpdated?.();
resolve();
},
);
});
} }
private shouldPaginate(): boolean { private shouldPaginate(): boolean {