Switch to an internal Map for previews

This means we're abusing the AsyncStoreWithClient to get access to a lifecycle, but overall that seems like a minor crime compared to the time spend abusing the store's state as a map.

With thousands of rooms shown, we can save on average 743ms per preview. The new preview time is 0.12ms on average.
pull/21833/head
Travis Ralston 2020-07-22 10:50:54 -06:00
parent 67fd6e6122
commit d593d24aea
1 changed files with 13 additions and 12 deletions

View File

@ -26,6 +26,7 @@ import { CallAnswerEventPreview } from "./previews/CallAnswerEventPreview";
import { CallHangupEvent } from "./previews/CallHangupEvent"; import { CallHangupEvent } from "./previews/CallHangupEvent";
import { StickerEventPreview } from "./previews/StickerEventPreview"; import { StickerEventPreview } from "./previews/StickerEventPreview";
import { ReactionEventPreview } from "./previews/ReactionEventPreview"; import { ReactionEventPreview } from "./previews/ReactionEventPreview";
import { UPDATE_EVENT } from "../AsyncStore";
const PREVIEWS = { const PREVIEWS = {
'm.room.message': { 'm.room.message': {
@ -62,12 +63,15 @@ type TAG_ANY = "im.vector.any";
const TAG_ANY: TAG_ANY = "im.vector.any"; const TAG_ANY: TAG_ANY = "im.vector.any";
interface IState { interface IState {
[roomId: string]: Map<TagID | TAG_ANY, string | null>; // null indicates the preview is empty / irrelevant // Empty because we don't actually use the state
} }
export class MessagePreviewStore extends AsyncStoreWithClient<IState> { export class MessagePreviewStore extends AsyncStoreWithClient<IState> {
private static internalInstance = new MessagePreviewStore(); private static internalInstance = new MessagePreviewStore();
// null indicates the preview is empty / irrelevant
private previews = new Map<string, Map<TagID|TAG_ANY, string|null>>();
private constructor() { private constructor() {
super(defaultDispatcher, {}); super(defaultDispatcher, {});
} }
@ -85,10 +89,9 @@ export class MessagePreviewStore extends AsyncStoreWithClient<IState> {
public getPreviewForRoom(room: Room, inTagId: TagID): string { public getPreviewForRoom(room: Room, inTagId: TagID): string {
if (!room) return null; // invalid room, just return nothing if (!room) return null; // invalid room, just return nothing
const val = this.state[room.roomId]; if (!this.previews.has(room.roomId)) this.generatePreview(room, inTagId);
if (!val) this.generatePreview(room, inTagId);
const previews = this.state[room.roomId]; const previews = this.previews.get(room.roomId);
if (!previews) return null; if (!previews) return null;
if (!previews.has(inTagId)) { if (!previews.has(inTagId)) {
@ -101,11 +104,10 @@ export class MessagePreviewStore extends AsyncStoreWithClient<IState> {
const events = room.timeline; const events = room.timeline;
if (!events) return; // should only happen in tests if (!events) return; // should only happen in tests
let map = this.state[room.roomId]; let map = this.previews.get(room.roomId);
if (!map) { if (!map) {
map = new Map<TagID | TAG_ANY, string | null>(); map = new Map<TagID | TAG_ANY, string | null>();
this.previews.set(room.roomId, map);
// We set the state later with the map, so no need to send an update now
} }
// Set the tags so we know what to generate // Set the tags so we know what to generate
@ -141,16 +143,15 @@ export class MessagePreviewStore extends AsyncStoreWithClient<IState> {
} }
if (changed) { if (changed) {
// Update state for good measure - causes emit for update // We've muted the underlying Map, so just emit that we've changed.
// noinspection JSIgnoredPromiseFromCall - the AsyncStore handles concurrent calls this.emit(UPDATE_EVENT, this);
this.updateState({[room.roomId]: map});
} }
return; // we're done return; // we're done
} }
// At this point, we didn't generate a preview so clear it // At this point, we didn't generate a preview so clear it
// noinspection JSIgnoredPromiseFromCall - the AsyncStore handles concurrent calls this.previews.delete(room.roomId);
this.updateState({[room.roomId]: null}); this.emit(UPDATE_EVENT, this);
} }
protected async onAction(payload: ActionPayload) { protected async onAction(payload: ActionPayload) {