@@ -254,6 +254,9 @@ export default class MessagePanel extends React.Component<IProps, IState> {
254
254
private readonly showTypingNotificationsWatcherRef : string ;
255
255
private eventTiles : Record < string , EventTile > = { } ;
256
256
257
+ // A map to allow groupers to maintain consistent keys even if their first event is uprooted due to back-pagination.
258
+ public grouperKeyMap = new WeakMap < MatrixEvent , string > ( ) ;
259
+
257
260
constructor ( props , context ) {
258
261
super ( props , context ) ;
259
262
@@ -684,14 +687,13 @@ export default class MessagePanel extends React.Component<IProps, IState> {
684
687
break ; // break on first grouper
685
688
}
686
689
}
690
+
687
691
if ( ! grouper ) {
688
- const wantTile = this . shouldShowEvent ( mxEv ) ;
689
- const isGrouped = false ;
690
- if ( wantTile ) {
692
+ if ( this . shouldShowEvent ( mxEv ) ) {
691
693
// make sure we unpack the array returned by getTilesForEvent,
692
- // otherwise react will auto-generate keys and we will end up
693
- // replacing all of the DOM elements every time we paginate.
694
- ret . push ( ...this . getTilesForEvent ( prevEvent , mxEv , last , isGrouped , nextEvent , nextTile ) ) ;
694
+ // otherwise React will auto-generate keys, and we will end up
695
+ // replacing all the DOM elements every time we paginate.
696
+ ret . push ( ...this . getTilesForEvent ( prevEvent , mxEv , last , false , nextEvent , nextTile ) ) ;
695
697
prevEvent = mxEv ;
696
698
}
697
699
@@ -1127,15 +1129,15 @@ class CreationGrouper extends BaseGrouper {
1127
1129
if ( ! this . events || ! this . events . length ) return [ ] ;
1128
1130
1129
1131
const panel = this . panel ;
1130
- const ret = [ ] ;
1132
+ const ret : ReactNode [ ] = [ ] ;
1131
1133
const isGrouped = true ;
1132
1134
const createEvent = this . event ;
1133
1135
const lastShownEvent = this . lastShownEvent ;
1134
1136
1135
1137
if ( panel . wantsDateSeparator ( this . prevEvent , createEvent . getDate ( ) ) ) {
1136
1138
const ts = createEvent . getTs ( ) ;
1137
1139
ret . push (
1138
- < li key = { ts + '~' } > < DateSeparator key = { ts + '~' } roomId = { createEvent . getRoomId ( ) } ts = { ts } /> </ li > ,
1140
+ < li key = { ts + '~' } > < DateSeparator roomId = { createEvent . getRoomId ( ) } ts = { ts } /> </ li > ,
1139
1141
) ;
1140
1142
}
1141
1143
@@ -1262,6 +1264,10 @@ class MainGrouper extends BaseGrouper {
1262
1264
this . events . push ( ev ) ;
1263
1265
}
1264
1266
1267
+ private generateKey ( ) : string {
1268
+ return "eventlistsummary-" + this . events [ 0 ] . getId ( ) ;
1269
+ }
1270
+
1265
1271
public getTiles ( ) : ReactNode [ ] {
1266
1272
// If we don't have any events to group, don't even try to group them. The logic
1267
1273
// below assumes that we have a group of events to deal with, but we might not if
@@ -1271,24 +1277,28 @@ class MainGrouper extends BaseGrouper {
1271
1277
const isGrouped = true ;
1272
1278
const panel = this . panel ;
1273
1279
const lastShownEvent = this . lastShownEvent ;
1274
- const ret = [ ] ;
1280
+ const ret : ReactNode [ ] = [ ] ;
1275
1281
1276
1282
if ( panel . wantsDateSeparator ( this . prevEvent , this . events [ 0 ] . getDate ( ) ) ) {
1277
1283
const ts = this . events [ 0 ] . getTs ( ) ;
1278
1284
ret . push (
1279
- < li key = { ts + '~' } > < DateSeparator key = { ts + '~' } roomId = { this . events [ 0 ] . getRoomId ( ) } ts = { ts } /> </ li > ,
1285
+ < li key = { ts + '~' } > < DateSeparator roomId = { this . events [ 0 ] . getRoomId ( ) } ts = { ts } /> </ li > ,
1280
1286
) ;
1281
1287
}
1282
1288
1283
- // Ensure that the key of the EventListSummary does not change with new events.
1284
- // This will prevent it from being re-created unnecessarily, and
1285
- // instead will allow new props to be provided. In turn, the shouldComponentUpdate
1286
- // method on ELS can be used to prevent unnecessary renderings.
1287
- //
1288
- // Whilst back-paginating with an ELS at the top of the panel, prevEvent will be null,
1289
- // so use the key "eventlistsummary-initial". Otherwise, use the ID of the first
1290
- // membership event, which will not change during forward pagination.
1291
- const key = "eventlistsummary-" + ( this . prevEvent ? this . events [ 0 ] . getId ( ) : "initial" ) ;
1289
+ // Ensure that the key of the EventListSummary does not change with new events in either direction.
1290
+ // This will prevent it from being re-created unnecessarily, and instead will allow new props to be provided.
1291
+ // In turn, the shouldComponentUpdate method on ELS can be used to prevent unnecessary renderings.
1292
+ const keyEvent = this . events . find ( e => this . panel . grouperKeyMap . get ( e ) ) ;
1293
+ const key = keyEvent ? this . panel . grouperKeyMap . get ( keyEvent ) : this . generateKey ( ) ;
1294
+ if ( ! keyEvent ) {
1295
+ // Populate the weak map with the key that we are using for all related events.
1296
+ this . events . forEach ( e => {
1297
+ if ( ! this . panel . grouperKeyMap . has ( e ) ) {
1298
+ this . panel . grouperKeyMap . set ( e , key ) ;
1299
+ }
1300
+ } ) ;
1301
+ }
1292
1302
1293
1303
let highlightInSummary = false ;
1294
1304
let eventTiles = this . events . map ( ( e , i ) => {
0 commit comments