@@ -561,14 +561,29 @@ export default class MessagePanel extends React.Component<IProps, IState> {
561
561
} ) ;
562
562
} ;
563
563
564
- private getNextEventInfo ( arr : MatrixEvent [ ] , i : number ) : { nextEvent : MatrixEvent ; nextTile : MatrixEvent } {
565
- const nextEvent = i < arr . length - 1 ? arr [ i + 1 ] : null ;
564
+ /**
565
+ * Find the next event in the list, and the next visible event in the list.
566
+ *
567
+ * @param event - the list of events to look in and whether they are shown
568
+ * @param i - where in the list we are now
569
+ *
570
+ * @returns { nextEvent, nextTile }
571
+ *
572
+ * nextEvent is the event after i in the supplied array.
573
+ *
574
+ * nextTile is the first event in the array after i that we will show a tile
575
+ * for. It is used to to determine the 'last successful' flag when rendering
576
+ * the tile.
577
+ */
578
+ private getNextEventInfo (
579
+ events : EventAndShouldShow [ ] ,
580
+ i : number ,
581
+ ) : { nextEvent : MatrixEvent | null ; nextTile : MatrixEvent | null } {
582
+ // WARNING: this method is on a hot path.
583
+
584
+ const nextEvent = i < events . length - 1 ? events [ i + 1 ] . event : null ;
566
585
567
- // The next event with tile is used to to determine the 'last successful' flag
568
- // when rendering the tile. The shouldShowEvent function is pretty quick at what
569
- // it does, so this should have no significant cost even when a room is used for
570
- // not-chat purposes.
571
- const nextTile = arr . slice ( i + 1 ) . find ( ( e ) => this . shouldShowEvent ( e ) ) ;
586
+ const nextTile = findFirstShownAfter ( i , events ) ;
572
587
573
588
return { nextEvent, nextTile } ;
574
589
}
@@ -587,20 +602,21 @@ export default class MessagePanel extends React.Component<IProps, IState> {
587
602
}
588
603
589
604
private getEventTiles ( ) : ReactNode [ ] {
590
- let i ;
591
-
592
605
// first figure out which is the last event in the list which we're
593
606
// actually going to show; this allows us to behave slightly
594
607
// differently for the last event in the list. (eg show timestamp)
595
608
//
596
609
// we also need to figure out which is the last event we show which isn't
597
610
// a local echo, to manage the read-marker.
598
- let lastShownEvent ;
611
+ let lastShownEvent : MatrixEvent | undefined ;
612
+ const events : EventAndShouldShow [ ] = this . props . events . map ( ( event ) => {
613
+ return { event, shouldShow : this . shouldShowEvent ( event ) } ;
614
+ } ) ;
599
615
600
616
let lastShownNonLocalEchoIndex = - 1 ;
601
- for ( i = this . props . events . length - 1 ; i >= 0 ; i -- ) {
602
- const mxEv = this . props . events [ i ] ;
603
- if ( ! this . shouldShowEvent ( mxEv ) ) {
617
+ for ( let i = events . length - 1 ; i >= 0 ; i -- ) {
618
+ const { event : mxEv , shouldShow } = events [ i ] ;
619
+ if ( ! shouldShow ) {
604
620
continue ;
605
621
}
606
622
@@ -631,11 +647,11 @@ export default class MessagePanel extends React.Component<IProps, IState> {
631
647
632
648
let grouper : BaseGrouper = null ;
633
649
634
- for ( i = 0 ; i < this . props . events . length ; i ++ ) {
635
- const mxEv = this . props . events [ i ] ;
650
+ for ( let i = 0 ; i < events . length ; i ++ ) {
651
+ const { event : mxEv , shouldShow } = events [ i ] ;
636
652
const eventId = mxEv . getId ( ) ;
637
653
const last = mxEv === lastShownEvent ;
638
- const { nextEvent, nextTile } = this . getNextEventInfo ( this . props . events , i ) ;
654
+ const { nextEvent, nextTile } = this . getNextEventInfo ( events , i ) ;
639
655
640
656
if ( grouper ) {
641
657
if ( grouper . shouldGroup ( mxEv ) ) {
@@ -658,7 +674,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
658
674
}
659
675
660
676
if ( ! grouper ) {
661
- if ( this . shouldShowEvent ( mxEv ) ) {
677
+ if ( shouldShow ) {
662
678
// make sure we unpack the array returned by getTilesForEvent,
663
679
// otherwise React will auto-generate keys, and we will end up
664
680
// replacing all the DOM elements every time we paginate.
@@ -1040,6 +1056,15 @@ export default class MessagePanel extends React.Component<IProps, IState> {
1040
1056
}
1041
1057
}
1042
1058
1059
+ /**
1060
+ * Holds on to an event, caching the information about whether it should be
1061
+ * shown. Avoids calling shouldShowEvent more times than we need to.
1062
+ */
1063
+ interface EventAndShouldShow {
1064
+ event : MatrixEvent ;
1065
+ shouldShow : boolean ;
1066
+ }
1067
+
1043
1068
abstract class BaseGrouper {
1044
1069
public static canStartGroup = ( panel : MessagePanel , ev : MatrixEvent ) : boolean => true ;
1045
1070
@@ -1369,3 +1394,17 @@ class MainGrouper extends BaseGrouper {
1369
1394
1370
1395
// all the grouper classes that we use, ordered by priority
1371
1396
const groupers = [ CreationGrouper , MainGrouper ] ;
1397
+
1398
+ /**
1399
+ * Look through the supplied list of EventAndShouldShow, and return the first
1400
+ * event that is >start items through the list, and is shown.
1401
+ */
1402
+ function findFirstShownAfter ( start : number , events : EventAndShouldShow [ ] ) : MatrixEvent | null {
1403
+ for ( let n = start + 1 ; n < events . length ; n ++ ) {
1404
+ const { event, shouldShow } = events [ n ] ;
1405
+ if ( shouldShow ) {
1406
+ return event ;
1407
+ }
1408
+ }
1409
+ return null ;
1410
+ }
0 commit comments