11import { useScripts } from "@/src/features/scripts/hooks/useScripts" ;
2- import { useCallback , useRef } from "react" ;
2+ import { forwardRef , useCallback , useRef } from "react" ;
33import { StyleSheet , View , ViewProps } from "react-native" ;
44import {
55 WebView ,
@@ -24,95 +24,120 @@ function getInjectionScripts(
2424type WebViewContainerProps = {
2525 bottomPadding ?: number ;
2626 onScrollDirectionChange ?: ( direction : "up" | "down" ) => void ;
27+ goBack : ( ) => void ;
28+ goForward : ( ) => void ;
29+ reload : ( ) => void ;
30+ stopLoading : ( ) => void ;
2731} & ViewProps ;
2832
29- export default function WebViewContainer ( {
30- bottomPadding = 0 ,
31- style,
32- onScrollDirectionChange,
33- ...props
34- } : WebViewContainerProps ) {
35- const { activeTab, updateTabById } = useBrowser ( ) ;
36- const { getScriptsByRunAt, logExecution } = useScripts ( ) ;
37- const webviewRef = useRef < WebView > ( null ) ;
33+ const WebViewContainer = forwardRef < WebView , WebViewContainerProps > (
34+ function WebViewContainer (
35+ {
36+ bottomPadding = 0 ,
37+ style,
38+ onScrollDirectionChange,
39+ goBack,
40+ goForward,
41+ reload,
42+ stopLoading,
43+ ...props
44+ } ,
45+ ref
46+ ) {
47+ const { activeTab, updateTabById } = useBrowser ( ) ;
48+ const { getScriptsByRunAt, logExecution } = useScripts ( ) ;
3849
39- // Prepare URL
40- const url = activeTab ?. url ? normalizeUrl ( activeTab . url ) : "about:blank" ;
50+ // Prepare URL
51+ const url = activeTab ?. url ? normalizeUrl ( activeTab . url ) : "about:blank" ;
4152
42- // Inject scripts at different lifecycle points
43- const injectedJavaScriptBeforeContentLoaded = activeTab ?. url
44- ? getInjectionScripts ( url , "document-start" , getScriptsByRunAt )
45- : "" ;
46- const injectedJavaScript = activeTab ?. url
47- ? getInjectionScripts ( url , "document-ready" , getScriptsByRunAt )
48- : "" ;
53+ // Inject scripts at different lifecycle points
54+ const injectedJavaScriptBeforeContentLoaded = activeTab ?. url
55+ ? getInjectionScripts ( url , "document-start" , getScriptsByRunAt )
56+ : "" ;
57+ const injectedJavaScript = activeTab ?. url
58+ ? getInjectionScripts ( url , "document-ready" , getScriptsByRunAt )
59+ : "" ;
4960
50- // Handle navigation events
51- const onNavigationStateChange = useCallback (
52- ( navState : WebViewNavigation ) => {
53- if ( ! activeTab ) return ;
54- updateTabById ( activeTab . id , {
55- url : navState . url ,
56- title : navState . title ?? "" ,
57- canGoBack : navState . canGoBack ,
58- canGoForward : navState . canGoForward ,
59- isLoading : navState . loading ,
60- } ) ;
61- } ,
62- [ activeTab , updateTabById ]
63- ) ;
61+ // document-end: injected after page load
62+ const documentEndScripts = activeTab ?. url
63+ ? getInjectionScripts ( url , "document-end" , getScriptsByRunAt )
64+ : "" ;
65+
66+ // Handle navigation events
67+ const onNavigationStateChange = useCallback (
68+ ( navState : WebViewNavigation ) => {
69+ if ( ! activeTab ) return ;
70+ updateTabById ( activeTab . id , {
71+ url : navState . url ,
72+ title : navState . title ?? "" ,
73+ canGoBack : navState . canGoBack ,
74+ canGoForward : navState . canGoForward ,
75+ isLoading : navState . loading ,
76+ } ) ;
77+ // Inject document-end scripts when loading finishes
78+ if ( ! navState . loading && documentEndScripts ) {
79+ ( ref as React . RefObject < WebView > ) ?. current ?. injectJavaScript (
80+ documentEndScripts
81+ ) ;
82+ }
83+ } ,
84+ [ activeTab , updateTabById , documentEndScripts ]
85+ ) ;
6486
65- // Handle JS execution results
66- const onMessage = useCallback ( ( event : WebViewMessageEvent ) => {
67- // TODO: Log execution results
68- } , [ ] ) ;
87+ // Handle JS execution results
88+ const onMessage = useCallback ( ( event : WebViewMessageEvent ) => {
89+ // TODO: Log execution results
90+ } , [ ] ) ;
6991
70- // Inject CSS to add bottom padding to the body
71- const injectedCSS = `
92+ // Inject CSS to add bottom padding to the body
93+ const injectedCSS = `
7294 const style = document.createElement('style');
7395 style.innerHTML = 'body { padding-bottom: ${ bottomPadding } px !important; box-sizing: border-box; }';
7496 document.head.appendChild(style);
7597 ` ;
7698
77- const lastScrollY = useRef ( 0 ) ;
78- const lastDirection = useRef < "up" | "down" | null > ( null ) ; // pixels
79- const handleScroll = ( event : any ) => {
80- const currentY = event . nativeEvent . contentOffset ?. y ?? 0 ;
81- const diff = currentY - lastScrollY . current ;
99+ const lastScrollY = useRef ( 0 ) ;
100+ const lastDirection = useRef < "up" | "down" | null > ( null ) ; // pixels
101+ const handleScroll = ( event : any ) => {
102+ const currentY = event . nativeEvent . contentOffset ?. y ?? 0 ;
103+ const diff = currentY - lastScrollY . current ;
82104
83- if ( Math . abs ( diff ) > SCROLL_DIRECTION_THRESHOLD ) {
84- const newDirection = diff > 0 ? "down" : "up" ;
85- if ( newDirection !== lastDirection . current ) {
86- onScrollDirectionChange ?.( newDirection ) ;
87- lastDirection . current = newDirection ;
105+ if ( Math . abs ( diff ) > SCROLL_DIRECTION_THRESHOLD ) {
106+ const newDirection = diff > 0 ? "down" : "up" ;
107+ if ( newDirection !== lastDirection . current ) {
108+ onScrollDirectionChange ?.( newDirection ) ;
109+ lastDirection . current = newDirection ;
110+ }
88111 }
89- }
90- lastScrollY . current = currentY ;
91- } ;
112+ lastScrollY . current = currentY ;
113+ } ;
92114
93- return (
94- < View style = { [ styles . container , style ] } { ...props } >
95- < WebView
96- ref = { webviewRef }
97- source = { { uri : url } }
98- injectedJavaScriptBeforeContentLoaded = {
99- injectedJavaScriptBeforeContentLoaded
100- }
101- injectedJavaScript = { injectedJavaScript }
102- onNavigationStateChange = { onNavigationStateChange }
103- onMessage = { onMessage }
104- startInLoadingState = { true }
105- allowsInlineMediaPlayback
106- javaScriptEnabled
107- domStorageEnabled
108- setSupportMultipleWindows = { false }
109- allowsBackForwardNavigationGestures
110- originWhitelist = { [ "*" ] }
111- onScroll = { handleScroll }
112- />
113- </ View >
114- ) ;
115- }
115+ return (
116+ < View style = { [ styles . container , style ] } { ...props } >
117+ < WebView
118+ ref = { ref }
119+ source = { { uri : url } }
120+ injectedJavaScriptBeforeContentLoaded = {
121+ injectedJavaScriptBeforeContentLoaded
122+ }
123+ injectedJavaScript = { injectedJavaScript }
124+ onNavigationStateChange = { onNavigationStateChange }
125+ onMessage = { onMessage }
126+ startInLoadingState = { true }
127+ allowsInlineMediaPlayback
128+ javaScriptEnabled
129+ domStorageEnabled
130+ setSupportMultipleWindows = { false }
131+ allowsBackForwardNavigationGestures
132+ originWhitelist = { [ "*" ] }
133+ onScroll = { handleScroll }
134+ />
135+ </ View >
136+ ) ;
137+ }
138+ ) ;
139+
140+ export default WebViewContainer ;
116141
117142const styles = StyleSheet . create ( {
118143 container : {
0 commit comments