@@ -148,6 +148,7 @@ export class MessageListComponent
148148 private checkIfUnreadNotificationIsVisibleTimeout ?: ReturnType <
149149 typeof setTimeout
150150 > ;
151+ private jumpToMessageTimeouts : ReturnType < typeof setTimeout > [ ] = [ ] ;
151152 private jumpToLatestButtonVisibilityTimeout ?: ReturnType < typeof setTimeout > ;
152153 private forceRepaintSubject = new Subject < void > ( ) ;
153154 private messageIdToAnchorTo ?: string ;
@@ -202,6 +203,11 @@ export class MessageListComponent
202203 if ( this . checkIfUnreadNotificationIsVisibleTimeout ) {
203204 clearTimeout ( this . checkIfUnreadNotificationIsVisibleTimeout ) ;
204205 }
206+ this . jumpToMessageTimeouts . forEach ( ( timeout ) =>
207+ clearTimeout ( timeout )
208+ ) ;
209+ this . jumpToMessageTimeouts = [ ] ;
210+ this . highlightedMessageId = undefined ;
205211 this . isUnreadNotificationVisible = false ;
206212 this . parsedDates = new Map ( ) ;
207213 this . resetScrollState ( ) ;
@@ -445,6 +451,7 @@ export class MessageListComponent
445451 if ( this . jumpToLatestButtonVisibilityTimeout ) {
446452 clearTimeout ( this . jumpToLatestButtonVisibilityTimeout ) ;
447453 }
454+ this . jumpToMessageTimeouts . forEach ( ( timeout ) => clearTimeout ( timeout ) ) ;
448455 this . disposeVirtualizedList ( ) ;
449456 }
450457
@@ -501,22 +508,10 @@ export class MessageListComponent
501508 this . isJumpToLatestButtonVisible = true ;
502509 }
503510 }
511+
504512 this . scroll$ . next ( ) ;
505- let scrollPosition = this . getScrollPosition ( ) ;
506513
507- const isUserScrolled =
508- ( this . direction === 'bottom-to-top'
509- ? scrollPosition !== 'bottom'
510- : scrollPosition !== 'top' ) || ! this . isLatestMessageInList ;
511- if ( this . isUserScrolled !== isUserScrolled ) {
512- this . ngZone . run ( ( ) => {
513- this . isUserScrolled = isUserScrolled ;
514- if ( ! this . isUserScrolled ) {
515- this . newMessageCountWhileBeingScrolled = 0 ;
516- }
517- this . cdRef . detectChanges ( ) ;
518- } ) ;
519- }
514+ this . checkIfUserScrolled ( ) ;
520515
521516 if ( this . hideJumpToLatestButtonDuringScroll ) {
522517 if ( this . isJumpToLatestButtonVisible ) {
@@ -534,35 +529,6 @@ export class MessageListComponent
534529 }
535530 } , 100 ) ;
536531 }
537-
538- const prevScrollPosition = this . scrollPosition$ . getValue ( ) ;
539-
540- if ( this . direction === 'top-to-bottom' ) {
541- if ( scrollPosition === 'top' ) {
542- scrollPosition = 'bottom' ;
543- } else if ( scrollPosition === 'bottom' ) {
544- scrollPosition = 'top' ;
545- }
546- }
547-
548- if ( prevScrollPosition !== scrollPosition && ! this . isJumpingToMessage ) {
549- if ( scrollPosition === 'top' || scrollPosition === 'bottom' ) {
550- this . virtualizedList ?. virtualizedItems$
551- . pipe ( take ( 1 ) )
552- . subscribe ( ( items ) => {
553- this . messageIdToAnchorTo =
554- scrollPosition === 'top'
555- ? items [ 0 ] ?. id
556- : items [ items . length - 1 ] ?. id ;
557- this . anchorMessageTopOffset = document
558- . getElementById ( this . messageIdToAnchorTo )
559- ?. getBoundingClientRect ( ) ?. top ;
560- } ) ;
561- }
562- this . ngZone . run ( ( ) => {
563- this . scrollPosition$ . next ( scrollPosition ) ;
564- } ) ;
565- }
566532 }
567533
568534 jumpToFirstUnreadMessage ( ) {
@@ -611,6 +577,53 @@ export class MessageListComponent
611577 : this . emptyThreadMessageListTemplate ;
612578 }
613579
580+ private checkIfUserScrolled ( ) {
581+ let scrollPosition = this . getScrollPosition ( ) ;
582+
583+ const isUserScrolled =
584+ ( this . direction === 'bottom-to-top'
585+ ? scrollPosition !== 'bottom'
586+ : scrollPosition !== 'top' ) || ! this . isLatestMessageInList ;
587+ if ( this . isUserScrolled !== isUserScrolled ) {
588+ this . ngZone . run ( ( ) => {
589+ this . isUserScrolled = isUserScrolled ;
590+ if ( ! this . isUserScrolled ) {
591+ this . newMessageCountWhileBeingScrolled = 0 ;
592+ }
593+ this . cdRef . detectChanges ( ) ;
594+ } ) ;
595+ }
596+
597+ const prevScrollPosition = this . scrollPosition$ . getValue ( ) ;
598+
599+ if ( this . direction === 'top-to-bottom' ) {
600+ if ( scrollPosition === 'top' ) {
601+ scrollPosition = 'bottom' ;
602+ } else if ( scrollPosition === 'bottom' ) {
603+ scrollPosition = 'top' ;
604+ }
605+ }
606+
607+ if ( prevScrollPosition !== scrollPosition && ! this . isJumpingToMessage ) {
608+ if ( scrollPosition === 'top' || scrollPosition === 'bottom' ) {
609+ this . virtualizedList ?. virtualizedItems$
610+ . pipe ( take ( 1 ) )
611+ . subscribe ( ( items ) => {
612+ this . messageIdToAnchorTo =
613+ scrollPosition === 'top'
614+ ? items [ 0 ] ?. id
615+ : items [ items . length - 1 ] ?. id ;
616+ this . anchorMessageTopOffset = document
617+ . getElementById ( this . messageIdToAnchorTo )
618+ ?. getBoundingClientRect ( ) ?. top ;
619+ } ) ;
620+ }
621+ this . ngZone . run ( ( ) => {
622+ this . scrollPosition$ . next ( scrollPosition ) ;
623+ } ) ;
624+ }
625+ }
626+
614627 private preserveScrollbarPosition ( ) {
615628 if ( ! this . messageIdToAnchorTo ) {
616629 return ;
@@ -792,32 +805,46 @@ export class MessageListComponent
792805 withRetry : boolean = true
793806 ) {
794807 const element = document . getElementById ( options . messageId ) ;
808+ this . jumpToMessageTimeouts . forEach ( ( t ) => clearTimeout ( t ) ) ;
809+ this . jumpToMessageTimeouts = [ ] ;
795810 if ( ! element && withRetry ) {
796811 // If the message was newly inserted into activeChannelMessages$, the message will be rendered after the current change detection cycle -> wait for this cycle to complete
797- setTimeout ( ( ) => this . scrollMessageIntoView ( options , false ) ) ;
812+ this . jumpToMessageTimeouts . push (
813+ setTimeout ( ( ) => this . scrollMessageIntoView ( options , false ) )
814+ ) ;
798815 } else if ( element ) {
799816 const blockMapping : { [ key : string ] : ScrollLogicalPosition } = {
800817 top : 'start' ,
801818 bottom : 'end' ,
802819 middle : 'center' ,
803820 } ;
821+ // We can't know when smooth scrolling ends, so we set the behavior to instant https://github.com/w3c/csswg-drafts/issues/3744
804822 element . scrollIntoView ( {
823+ behavior : 'instant' as ScrollBehavior ,
805824 block : blockMapping [ options . position ] ,
806825 } ) ;
807826 if ( options . position !== 'middle' ) {
808827 options . position === 'bottom'
809828 ? this . scrollToBottom ( )
810829 : this . scrollToTop ( ) ;
811830 }
812- setTimeout ( ( ) => {
813- this . isJumpingToMessage = false ;
814- } , 0 ) ;
815- setTimeout ( ( ) => {
816- this . highlightedMessageId = undefined ;
817- this . firstUnreadMessageId = undefined ;
818- this . isJumpingToLatestUnreadMessage = false ;
819- this . cdRef . detectChanges ( ) ;
820- } , 1000 ) ;
831+ this . jumpToMessageTimeouts . push (
832+ setTimeout ( ( ) => {
833+ this . isJumpingToMessage = false ;
834+ if ( ! this . isUserScrolled ) {
835+ this . checkIfUserScrolled ( ) ;
836+ }
837+ } , 200 )
838+ ) ;
839+ this . jumpToMessageTimeouts . push (
840+ setTimeout ( ( ) => {
841+ this . highlightedMessageId = undefined ;
842+ this . firstUnreadMessageId = undefined ;
843+ this . isJumpingToLatestUnreadMessage = false ;
844+ this . jumpToMessageTimeouts = [ ] ;
845+ this . cdRef . detectChanges ( ) ;
846+ } , 1000 )
847+ ) ;
821848 }
822849 }
823850
0 commit comments