1- import React , { PropsWithChildren , ReactNode , useCallback , useMemo , useRef , useState } from 'react' ;
1+ import React , { PropsWithChildren , ReactNode , useCallback , useMemo } from 'react' ;
22import {
33 GestureResponderEvent ,
44 Linking ,
55 Platform ,
6- ScrollView ,
76 Text ,
87 TextProps ,
98 View ,
@@ -13,7 +12,7 @@ import {
1312import { Gesture , GestureDetector } from 'react-native-gesture-handler' ;
1413// @ts -expect-error
1514import Markdown from 'react-native-markdown-package' ;
16- import { runOnJS } from 'react-native-reanimated' ;
15+ import Animated , { clamp , scrollTo , useAnimatedRef , useSharedValue } from 'react-native-reanimated' ;
1716
1817import {
1918 DefaultRules ,
@@ -38,42 +37,51 @@ import { escapeRegExp } from '../../../../utils/utils';
3837import type { MessageType } from '../../../MessageList/hooks/useMessageList' ;
3938
4039const ReactiveScrollView = ( { children } : { children : ReactNode } ) => {
41- const [ scrollViewXOffset , setScrollViewXOffset ] = useState ( 0 ) ;
42- const scrollViewRef = useRef < ScrollView > ( null ) ;
43-
44- const scrollTo = useCallback ( ( translation : number ) => {
45- if ( scrollViewRef . current ) {
46- scrollViewRef . current . scrollTo ( {
47- animated : false ,
48- x : translation ,
49- } ) ;
50- }
51- } , [ ] ) ;
40+ const scrollViewRef = useAnimatedRef < Animated . ScrollView > ( ) ;
41+ const contentWidth = useSharedValue ( 0 ) ;
42+ const visibleContentWidth = useSharedValue ( 0 ) ;
43+ const offsetBeforeScroll = useSharedValue ( 0 ) ;
5244
5345 const panGesture = Gesture . Pan ( )
54- . activeOffsetX ( [ - 10 , 10 ] )
46+ . activeOffsetX ( [ - 5 , 5 ] )
5547 . onUpdate ( ( event ) => {
5648 const { translationX } = event ;
5749
58- runOnJS ( scrollTo ) ( scrollViewXOffset - translationX ) ;
50+ scrollTo ( scrollViewRef , offsetBeforeScroll . value - translationX , 0 , false ) ;
5951 } )
6052 . onEnd ( ( event ) => {
6153 const { translationX } = event ;
6254
63- runOnJS ( setScrollViewXOffset ) ( scrollViewXOffset - translationX ) ;
55+ const velocityEffect = event . velocityX * 0.3 ;
56+
57+ const finalPosition = clamp (
58+ offsetBeforeScroll . value - translationX - velocityEffect ,
59+ 0 ,
60+ contentWidth . value - visibleContentWidth . value ,
61+ ) ;
62+
63+ offsetBeforeScroll . value = finalPosition ;
64+
65+ scrollTo ( scrollViewRef , finalPosition , 0 , true ) ;
6466 } ) ;
6567
6668 return (
6769 < GestureDetector gesture = { panGesture } >
68- < ScrollView
70+ < Animated . ScrollView
6971 contentContainerStyle = { { flexGrow : 1 } }
7072 horizontal
7173 nestedScrollEnabled = { true }
74+ onContentSizeChange = { ( width ) => {
75+ contentWidth . value = width ;
76+ } }
77+ onLayout = { ( e ) => {
78+ visibleContentWidth . value = e . nativeEvent . layout . width ;
79+ } }
7280 ref = { scrollViewRef }
7381 scrollEnabled = { false }
7482 >
7583 { children }
76- </ ScrollView >
84+ </ Animated . ScrollView >
7785 </ GestureDetector >
7886 ) ;
7987} ;
@@ -560,7 +568,7 @@ const MarkdownTableColumn = ({ items, output, state, styles }: MarkdownTableRowP
560568 ) ;
561569
562570 return (
563- < View style = { { flexDirection : 'column' , flex : 1 } } >
571+ < View style = { { flex : 1 , flexDirection : 'column' } } >
564572 { headerCellContent ? (
565573 < View key = { - 1 } style = { styles . tableHeader } >
566574 < Text style = { styles . tableHeaderCell } > { output ( headerCellContent , state ) } </ Text >
0 commit comments