11import type { FC } from "react" ;
2- import React , { useCallback , useEffect , useState } from "react" ;
2+ import React , { forwardRef , useCallback , useEffect , useImperativeHandle , useState } from "react" ;
3+ import type { NativeScrollEvent , NativeSyntheticEvent } from "react-native" ;
34import { StyleSheet , useWindowDimensions } from "react-native" ;
45import DeviceInfo from "react-native-device-info" ;
5- import Animated , { useSharedValue } from "react-native-reanimated" ;
6+ import Animated , { runOnUI , useAnimatedStyle , useSharedValue } from "react-native-reanimated" ;
67import { useSafeAreaInsets } from "react-native-safe-area-context" ;
78
9+ import { Image } from "expo-image" ;
810import { Stack } from "tamagui" ;
911
1012import { ImageGallery } from "@/components/ImageGallery" ;
@@ -18,18 +20,23 @@ import { javaScriptBeforeContentLoaded } from "./javascript-before-content";
1820import { javaScriptContentLoaded } from "./javascript-content" ;
1921import { Skeleton } from "./Skeleton" ;
2022
21- export const WebViewRenderer : FC < {
23+ interface Props {
2224 headerContainerHeight : number
2325 postUri ?: string
2426 bottomBarHeight : number
25- } > = ( { headerContainerHeight, postUri, bottomBarHeight } ) => {
26- const { top } = useSafeAreaInsets ( ) ;
27+ }
28+
29+ export interface WebViewRendererInstance {
30+ handleScroll : ( event : NativeSyntheticEvent < NativeScrollEvent > ) => void
31+ }
32+
33+ export const WebViewRenderer = forwardRef < WebViewRendererInstance , Props > ( ( { headerContainerHeight, postUri, bottomBarHeight } , ref ) => {
34+ const { top, bottom } = useSafeAreaInsets ( ) ;
2735 const { width, height } = useWindowDimensions ( ) ;
2836 const { isDarkMode, mode } = useThemeStore ( ) ;
2937 const navigation = useRootNavigation ( ) ;
3038 const headerHeight = top + headerContainerHeight ;
3139 const contentLoaderDimensions = { width, height : height - headerHeight } ;
32- const [ webviewHeight , setWebviewHeight ] = useState ( contentLoaderDimensions . height ) ;
3340 const [ userAgent , setUserAgent ] = React . useState < string > ( null ) ;
3441 const [ displayImageUris , setDisplayImageUris ] = React . useState < string [ ] > ( [ ] ) ;
3542 const webviewLoadingAnimValue = useSharedValue < number > ( 0 ) ;
@@ -70,7 +77,7 @@ export const WebViewRenderer: FC<{
7077 }
7178
7279 if ( height ) {
73- setWebviewHeight ( Math . max ( height , contentLoaderDimensions . height ) ) ;
80+ setMaxContentHeight ( Math . max ( height , contentLoaderDimensions . height ) ) ;
7481 }
7582
7683 if ( imageUrlArray ) {
@@ -88,14 +95,31 @@ export const WebViewRenderer: FC<{
8895 } ) ;
8996 } , [ ] ) ;
9097
98+ const { height : screenHeight } = useWindowDimensions ( ) ;
99+ const [ maxContentHeight , setMaxContentHeight ] = useState < number > ( screenHeight ) ;
100+ const animHeight = useSharedValue < number > ( screenHeight ) ;
101+ const animatedStyle = useAnimatedStyle ( ( ) => ( { height : animHeight . value } ) , [ ] ) ;
102+
103+ const updateHeight = React . useCallback ( ( offsetY ) => {
104+ "worklet" ;
105+ const tolerance = 100 ;
106+ const isReachBottom = ( offsetY + bottomBarHeight + headerContainerHeight ) > ( animHeight . value / 2 ) ;
107+ if ( isReachBottom && animHeight . value < maxContentHeight ) {
108+ animHeight . value += tolerance ;
109+ }
110+ } , [ maxContentHeight , bottomBarHeight , headerContainerHeight ] ) ;
111+
112+ useImperativeHandle ( ref , ( ) => ( {
113+ handleScroll : ( e ) => {
114+ runOnUI ( updateHeight ) ( e . nativeEvent . contentOffset . y ) ;
115+ } ,
116+ } ) , [ updateHeight ] ) ;
117+
91118 const closeModal = React . useCallback ( ( ) => setDisplayImageUris ( [ ] ) , [ ] ) ;
92119
93120 return (
94121 < >
95- < Animated . ScrollView
96- contentContainerStyle = { { height : webviewHeight } }
97- scrollEventThrottle = { 16 }
98- >
122+ < Animated . View style = { animatedStyle } >
99123 { postUri && userAgent && (
100124 < WebView
101125 progressBarShown = { false }
@@ -118,7 +142,7 @@ export const WebViewRenderer: FC<{
118142 injectedJavaScriptBeforeContentLoaded = { javaScriptBeforeContentLoaded ( mode ) }
119143 />
120144 ) }
121- </ Animated . ScrollView >
145+ </ Animated . View >
122146 {
123147 displayImageUris . length > 0 && (
124148 < ImageGallery
@@ -130,7 +154,7 @@ export const WebViewRenderer: FC<{
130154 }
131155 </ >
132156 ) ;
133- } ;
157+ } ) ;
134158
135159const styles = StyleSheet . create ( {
136160 container : {
0 commit comments