From fdf96980e61d70be902a1817bc2e59534d7c6aa6 Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Fri, 10 Mar 2023 11:41:25 +0000 Subject: [PATCH] Combine an event and its shouldShow info into a single object --- src/components/structures/MessagePanel.tsx | 69 +++++++++++++--------- 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/src/components/structures/MessagePanel.tsx b/src/components/structures/MessagePanel.tsx index 7dd18464eee..434b3ddd6e8 100644 --- a/src/components/structures/MessagePanel.tsx +++ b/src/components/structures/MessagePanel.tsx @@ -564,35 +564,26 @@ export default class MessagePanel extends React.Component { /** * Find the next event in the list, and the next visible event in the list. * - * @param arr - the list of events to look in - * @param shouldShow - which of these events should be shown (a cache of - * calling this.shouldShowEvent(e) for each e in arr) + * @param event - the list of events to look in and whether they are shown * @param i - where in the list we are now * * @returns { nextEvent, nextTile } * - * nextEvent is the last event in the supplied array. + * nextEvent is the event after i in the supplied array. * - * nextTile is the last event in the array that we will show a tile for. It - * is used to to determine the 'last successful' flag when rendering the - * tile. + * nextTile is the first event in the array after i that we will show a tile + * for. It is used to to determine the 'last successful' flag when rendering + * the tile. */ private getNextEventInfo( - arr: MatrixEvent[], - shouldShow: boolean[], + events: EventAndShouldShow[], i: number, - ): { nextEvent: MatrixEvent; nextTile: MatrixEvent } { + ): { nextEvent: MatrixEvent | null; nextTile: MatrixEvent | null } { // WARNING: this method is on a hot path. - const nextEvent = i < arr.length - 1 ? arr[i + 1] : null; + const nextEvent = i < events.length - 1 ? events[i + 1].event : null; - let nextTile = null; - for (let n = i + 1; n < arr.length; n++) { - if (shouldShow[n]) { - nextTile = arr[n]; - break; - } - } + const nextTile = findFirstShownAfter(i, events); return { nextEvent, nextTile }; } @@ -618,14 +609,16 @@ export default class MessagePanel extends React.Component { // we also need to figure out which is the last event we show which isn't // a local echo, to manage the read-marker. let lastShownEvent: MatrixEvent | undefined; - const shouldShow = this.props.events.map((e) => this.shouldShowEvent(e)); + const events: EventAndShouldShow[] = this.props.events.map((event) => { + return { event, shouldShow: this.shouldShowEvent(event) }; + }); let lastShownNonLocalEchoIndex = -1; - for (let i = this.props.events.length - 1; i >= 0; i--) { - if (!shouldShow[i]) { + for (let i = events.length - 1; i >= 0; i--) { + const { event: mxEv, shouldShow } = events[i]; + if (!shouldShow) { continue; } - const mxEv = this.props.events[i]; if (lastShownEvent === undefined) { lastShownEvent = mxEv; @@ -654,12 +647,11 @@ export default class MessagePanel extends React.Component { let grouper: BaseGrouper = null; - for (let i = 0; i < this.props.events.length; i++) { - const mxEv = this.props.events[i]; - const shouldShowEv = shouldShow[i]; + for (let i = 0; i < events.length; i++) { + const { event: mxEv, shouldShow } = events[i]; const eventId = mxEv.getId(); const last = mxEv === lastShownEvent; - const { nextEvent, nextTile } = this.getNextEventInfo(this.props.events, shouldShow, i); + const { nextEvent, nextTile } = this.getNextEventInfo(events, i); if (grouper) { if (grouper.shouldGroup(mxEv)) { @@ -682,7 +674,7 @@ export default class MessagePanel extends React.Component { } if (!grouper) { - if (shouldShowEv) { + if (shouldShow) { // make sure we unpack the array returned by getTilesForEvent, // otherwise React will auto-generate keys, and we will end up // replacing all the DOM elements every time we paginate. @@ -1064,6 +1056,15 @@ export default class MessagePanel extends React.Component { } } +/** + * Holds on to an event, caching the information about whether it should be + * shown. Avoids calling shouldShowEvent more times than we need to. + */ +interface EventAndShouldShow { + event: MatrixEvent; + shouldShow: boolean; +} + abstract class BaseGrouper { public static canStartGroup = (panel: MessagePanel, ev: MatrixEvent): boolean => true; @@ -1393,3 +1394,17 @@ class MainGrouper extends BaseGrouper { // all the grouper classes that we use, ordered by priority const groupers = [CreationGrouper, MainGrouper]; + +/** + * Look through the supplied list of EventAndShouldShow, and return the first + * event that is >start items through the list, and is shown. + */ +function findFirstShownAfter(start: number, events: EventAndShouldShow[]): MatrixEvent | null { + for (let n = start + 1; n < events.length; n++) { + const { event, shouldShow } = events[n]; + if (shouldShow) { + return event; + } + } + return null; +}