1- import type { Ref } from 'vue'
1+ import type { Reactive , Ref } from 'vue'
22import { reactive , watch } from 'vue'
33
44import type { ChangeHeightData } from '/@/components/Main/MainView/MessageElement/composables/useElementRenderObserver'
55import type { LoadingDirection } from '/@/components/Main/MainView/MessagesScroller/composables/useMessagesFetcher'
6+ import { defer } from '/@/lib/basic/timer'
67import type { MessageId } from '/@/types/entity-ids'
78
89const useMessageScroller = (
910 rootRef : Ref < HTMLElement | null > ,
10- scrollerProps : {
11+ scrollerProps : Reactive < {
1112 messageIds : MessageId [ ]
1213 lastLoadingDirection : LoadingDirection
1314 entryMessageId ?: MessageId
14- }
15+ } >
1516) => {
1617 const state = reactive ( {
1718 height : 0 ,
1819 scrollTop : 0
1920 } )
2021
21- const onChangeHeight = ( payload : ChangeHeightData ) => {
22- if ( ! rootRef . value ) {
23- return
24- }
22+ const onChangeHeight = async ( payload : ChangeHeightData ) => {
23+ if ( ! rootRef . value ) return
2524
2625 if (
2726 scrollerProps . lastLoadingDirection === 'around' &&
@@ -49,20 +48,34 @@ const useMessageScroller = (
4948 scrollerProps . lastLoadingDirection === 'former'
5049 ) {
5150 const scrollerTop = rootRef . value . getBoundingClientRect ( ) . top
51+
5252 // 視界より上にある要素の高さが変わった場合のみ補正する
53- if ( payload . top < scrollerTop ) {
54- rootRef . value . scrollTo ( {
55- top : rootRef . value . scrollTop + payload . heightDiff
56- } )
53+ if (
54+ ( payload . heightDiff > 0 && payload . bottom < scrollerTop ) ||
55+ ( payload . heightDiff < 0 && payload . top < scrollerTop )
56+ ) {
57+ rootRef . value . scrollBy ( { top : payload . heightDiff } )
5758 }
59+
60+ const scrollBottom = rootRef . value . scrollTop + rootRef . value . clientHeight
61+
62+ // 末尾付近を閲覧しているときは,末尾を保つ
63+ if ( state . height - scrollBottom <= 50 ) {
64+ if ( ! rootRef . value ) return
65+
66+ // 複数の onChangeHeight が同フレーム内に複数回呼ばれる場合があるので,
67+ // すべての更新を待ってからスクロールする
68+ await defer ( )
69+ rootRef . value . scrollTo ( { top : rootRef . value . scrollHeight } )
70+ }
71+
5872 state . height = rootRef . value . scrollHeight
5973 }
6074 }
6175
6276 const onEntryMessageLoaded = ( relativePos : number ) => {
63- if ( ! rootRef . value ) {
64- return
65- }
77+ if ( ! rootRef . value ) return
78+
6679 const rootHeight = rootRef . value . getBoundingClientRect ( ) . height
6780 rootRef . value . scrollTo ( { top : relativePos - rootHeight / 3 } )
6881 }
@@ -94,6 +107,7 @@ const useMessageScroller = (
94107 rootRef . value . scrollTo ( { top : newHeight } )
95108 }
96109 state . height = newHeight
110+
97111 return
98112 }
99113 //上に追加された時はスクロール位置を変更する。
@@ -109,6 +123,7 @@ const useMessageScroller = (
109123 rootRef . value . scrollTo ( {
110124 top : newHeight
111125 } )
126+
112127 state . height = newHeight
113128 }
114129 } else state . height = newHeight
0 commit comments