Throttle re-renders for rapidly-updating messages

This commit is contained in:
trevor-signal 2023-11-01 12:49:58 -04:00 committed by GitHub
parent 9206b9984b
commit 0da867a0ef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -2,6 +2,8 @@
// SPDX-License-Identifier: AGPL-3.0-only
import cloneDeep from 'lodash/cloneDeep';
import { throttle } from 'lodash';
import LRU from 'lru-cache';
import type { MessageAttributesType } from '../model-types.d';
import type { MessageModel } from '../models/messages';
import * as Errors from '../types/errors';
@ -17,6 +19,7 @@ import { softAssert, strictAssert } from '../util/assert';
import { isStory } from '../messages/helpers';
import { getStoryDataFromMessageAttributes } from './storyLoader';
const MAX_THROTTLED_REDUX_UPDATERS = 200;
export class MessageCache {
private state = {
messages: new Map<string, MessageAttributesType>(),
@ -198,28 +201,7 @@ export class MessageCache {
this.markModelStale(nextMessageAttributes);
if (window.reduxActions) {
if (isStory(nextMessageAttributes)) {
const storyData = getStoryDataFromMessageAttributes({
...nextMessageAttributes,
});
if (!storyData) {
return;
}
window.reduxActions.stories.storyChanged(storyData);
// We don't want messageChanged to run
return;
}
window.reduxActions.conversations.messageChanged(
messageId,
nextMessageAttributes.conversationId,
nextMessageAttributes
);
}
this.throttledUpdateRedux(nextMessageAttributes);
if (skipSaveToDatabase) {
return;
@ -231,6 +213,49 @@ export class MessageCache {
);
}
private throttledReduxUpdaters = new LRU<string, typeof this.updateRedux>({
max: MAX_THROTTLED_REDUX_UPDATERS,
});
private throttledUpdateRedux(attributes: MessageAttributesType) {
let updater = this.throttledReduxUpdaters.get(attributes.id);
if (!updater) {
updater = throttle(this.updateRedux.bind(this), 200, {
leading: true,
trailing: true,
});
this.throttledReduxUpdaters.set(attributes.id, updater);
}
updater(attributes);
}
private updateRedux(attributes: MessageAttributesType) {
if (!window.reduxActions) {
return;
}
if (isStory(attributes)) {
const storyData = getStoryDataFromMessageAttributes({
...attributes,
});
if (!storyData) {
return;
}
window.reduxActions.stories.storyChanged(storyData);
// We don't want messageChanged to run
return;
}
window.reduxActions.conversations.messageChanged(
attributes.id,
attributes.conversationId,
attributes
);
}
// When you already have the message attributes from the db and want to
// ensure that they're added to the cache. The latest attributes from cache
// are returned if they exist, if not the attributes passed in are returned.