Skip to content

Commit ac86e7a

Browse files
committed
feat: refactor WebViewContainer to use forwardRef and enhance navigation handling
1 parent a7f185e commit ac86e7a

1 file changed

Lines changed: 100 additions & 75 deletions

File tree

src/features/browser/screens/BrowserScreen/WebViewContainer.tsx

Lines changed: 100 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useScripts } from "@/src/features/scripts/hooks/useScripts";
2-
import { useCallback, useRef } from "react";
2+
import { forwardRef, useCallback, useRef } from "react";
33
import { StyleSheet, View, ViewProps } from "react-native";
44
import {
55
WebView,
@@ -24,95 +24,120 @@ function getInjectionScripts(
2424
type 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

117142
const styles = StyleSheet.create({
118143
container: {

0 commit comments

Comments
 (0)