@@ -371,6 +371,69 @@ class TimelinePanel extends React.Component<IProps, IState> {
371
371
}
372
372
}
373
373
374
+ /**
375
+ * Logs out debug info to describe the state of the TimelinePanel and the
376
+ * events in the room according to the matrix-js-sdk. This is useful when
377
+ * debugging problems like messages out of order, or messages that should
378
+ * not be showing up in a thread, etc.
379
+ *
380
+ * It's too expensive and cumbersome to do all of these calculations for
381
+ * every message change so instead we only log it out when asked.
382
+ */
383
+ private onDumpDebugLogs = ( ) : void => {
384
+ const roomId = this . props . timelineSet . room ?. roomId ;
385
+ // Get a list of the event IDs used in this TimelinePanel.
386
+ // This includes state and hidden events which we don't render
387
+ const eventIdList = this . state . events . map ( ( ev ) => ev . getId ( ) ) ;
388
+
389
+ // Get the list of actually rendered events seen in the DOM.
390
+ // This is useful to know for sure what's being shown on screen.
391
+ // And we can suss out any corrupted React `key` problems.
392
+ let renderedEventIds : string [ ] ;
393
+ const messagePanel = this . messagePanel . current ;
394
+ if ( messagePanel ) {
395
+ const messagePanelNode = ReactDOM . findDOMNode ( messagePanel ) as Element ;
396
+ if ( messagePanelNode ) {
397
+ const actuallyRenderedEvents = messagePanelNode . querySelectorAll ( '[data-event-id]' ) ;
398
+ renderedEventIds = [ ...actuallyRenderedEvents ] . map ( ( renderedEvent ) => {
399
+ return renderedEvent . getAttribute ( 'data-event-id' ) ;
400
+ } ) ;
401
+ }
402
+ }
403
+
404
+ // Get the list of events and threads for the room as seen by the
405
+ // matrix-js-sdk.
406
+ let serializedEventIdsFromTimelineSets : { [ key : string ] : string [ ] } [ ] ;
407
+ let serializedEventIdsFromThreadsTimelineSets : { [ key : string ] : string [ ] } [ ] ;
408
+ const serializedThreadsMap : { [ key : string ] : string [ ] } = { } ;
409
+ const client = MatrixClientPeg . get ( ) ;
410
+ const room = client ?. getRoom ( roomId ) ;
411
+ if ( room ) {
412
+ const timelineSets = room . getTimelineSets ( ) ;
413
+ const threadsTimelineSets = room . threadsTimelineSets ;
414
+
415
+ // Serialize all of the timelineSets and timelines in each set to their event IDs
416
+ serializedEventIdsFromTimelineSets = serializeEventIdsFromTimelineSets ( timelineSets ) ;
417
+ serializedEventIdsFromThreadsTimelineSets = serializeEventIdsFromTimelineSets ( threadsTimelineSets ) ;
418
+
419
+ // Serialize all threads in the room from theadId -> event IDs in the thread
420
+ room . getThreads ( ) . forEach ( ( thread ) => {
421
+ serializedThreadsMap [ thread . id ] = thread . events . map ( ev => ev . getId ( ) ) ;
422
+ } ) ;
423
+ }
424
+
425
+ logger . debug (
426
+ `TimelinePanel(${ this . context . timelineRenderingType } ): Debugging info for ${ roomId } \n` +
427
+ `\tevents(${ eventIdList . length } )=${ JSON . stringify ( eventIdList ) } \n` +
428
+ `\trenderedEventIds(${ renderedEventIds ? renderedEventIds . length : 0 } )=` +
429
+ `${ JSON . stringify ( renderedEventIds ) } \n` +
430
+ `\tserializedEventIdsFromTimelineSets=${ JSON . stringify ( serializedEventIdsFromTimelineSets ) } \n` +
431
+ `\tserializedEventIdsFromThreadsTimelineSets=` +
432
+ `${ JSON . stringify ( serializedEventIdsFromThreadsTimelineSets ) } \n` +
433
+ `\tserializedThreadsMap=${ JSON . stringify ( serializedThreadsMap ) } ` ,
434
+ ) ;
435
+ } ;
436
+
374
437
private onMessageListUnfillRequest = ( backwards : boolean , scrollToken : string ) : void => {
375
438
// If backwards, unpaginate from the back (i.e. the start of the timeline)
376
439
const dir = backwards ? EventTimeline . BACKWARDS : EventTimeline . FORWARDS ;
@@ -528,6 +591,9 @@ class TimelinePanel extends React.Component<IProps, IState> {
528
591
case "ignore_state_changed" :
529
592
this . forceUpdate ( ) ;
530
593
break ;
594
+ case Action . DumpDebugLogs :
595
+ this . onDumpDebugLogs ( ) ;
596
+ break ;
531
597
}
532
598
} ;
533
599
@@ -1464,7 +1530,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
1464
1530
const messagePanel = this . messagePanel . current ;
1465
1531
if ( ! messagePanel ) return null ;
1466
1532
1467
- const messagePanelNode = ReactDOM . findDOMNode ( messagePanel ) as HTMLElement ;
1533
+ const messagePanelNode = ReactDOM . findDOMNode ( messagePanel ) as Element ;
1468
1534
if ( ! messagePanelNode ) return null ; // sometimes this happens for fresh rooms/post-sync
1469
1535
const wrapperRect = messagePanelNode . getBoundingClientRect ( ) ;
1470
1536
const myUserId = MatrixClientPeg . get ( ) . credentials . userId ;
@@ -1686,4 +1752,30 @@ class TimelinePanel extends React.Component<IProps, IState> {
1686
1752
}
1687
1753
}
1688
1754
1755
+ /**
1756
+ * Iterate across all of the timelineSets and timelines inside to expose all of
1757
+ * the event IDs contained inside.
1758
+ *
1759
+ * @return An event ID list for every timeline in every timelineSet
1760
+ */
1761
+ function serializeEventIdsFromTimelineSets ( timelineSets ) : { [ key : string ] : string [ ] } [ ] {
1762
+ const serializedEventIdsInTimelineSet = timelineSets . map ( ( timelineSet ) => {
1763
+ const timelineMap = { } ;
1764
+
1765
+ const timelines = timelineSet . getTimelines ( ) ;
1766
+ const liveTimeline = timelineSet . getLiveTimeline ( ) ;
1767
+
1768
+ timelines . forEach ( ( timeline , index ) => {
1769
+ // Add a special label when it is the live timeline so we can tell
1770
+ // it apart from the others
1771
+ const isLiveTimeline = timeline === liveTimeline ;
1772
+ timelineMap [ isLiveTimeline ? 'liveTimeline' : `${ index } ` ] = timeline . getEvents ( ) . map ( ev => ev . getId ( ) ) ;
1773
+ } ) ;
1774
+
1775
+ return timelineMap ;
1776
+ } ) ;
1777
+
1778
+ return serializedEventIdsInTimelineSet ;
1779
+ }
1780
+
1689
1781
export default TimelinePanel ;
0 commit comments