-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathWebViewContainer.tsx
More file actions
147 lines (132 loc) · 4.38 KB
/
Copy pathWebViewContainer.tsx
File metadata and controls
147 lines (132 loc) · 4.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import { useScripts } from "@/src/features/scripts/hooks/useScripts";
import { forwardRef, useCallback, useRef } from "react";
import { StyleSheet, View, ViewProps } from "react-native";
import {
WebView,
WebViewMessageEvent,
WebViewNavigation,
} from "react-native-webview";
import { useBrowser } from "../../hooks/useBrowser";
import { normalizeUrl } from "../../utils/urlUtils";
const SCROLL_DIRECTION_THRESHOLD = 8;
function getInjectionScripts(
url: string,
runAt: "document-start" | "document-ready" | "document-end",
getScriptsByRunAt: any
) {
return getScriptsByRunAt(url, runAt)
.map((script: any) => script.code)
.join("\n");
}
type WebViewContainerProps = {
bottomPadding?: number;
onScrollDirectionChange?: (direction: "up" | "down") => void;
goBack: () => void;
goForward: () => void;
reload: () => void;
stopLoading: () => void;
} & ViewProps;
const WebViewContainer = forwardRef<WebView, WebViewContainerProps>(
function WebViewContainer(
{
bottomPadding = 0,
style,
onScrollDirectionChange,
goBack,
goForward,
reload,
stopLoading,
...props
},
ref
) {
const { activeTab, updateTabById } = useBrowser();
const { getScriptsByRunAt, logExecution } = useScripts();
// Prepare URL
const url = activeTab?.url ? normalizeUrl(activeTab.url) : "about:blank";
// Inject scripts at different lifecycle points
const injectedJavaScriptBeforeContentLoaded = activeTab?.url
? getInjectionScripts(url, "document-start", getScriptsByRunAt)
: "";
const injectedJavaScript = activeTab?.url
? getInjectionScripts(url, "document-ready", getScriptsByRunAt)
: "";
// document-end: injected after page load
const documentEndScripts = activeTab?.url
? getInjectionScripts(url, "document-end", getScriptsByRunAt)
: "";
// Handle navigation events
const onNavigationStateChange = useCallback(
(navState: WebViewNavigation) => {
if (!activeTab) return;
updateTabById(activeTab.id, {
url: navState.url,
title: navState.title ?? "",
canGoBack: navState.canGoBack,
canGoForward: navState.canGoForward,
isLoading: navState.loading,
});
// Inject document-end scripts when loading finishes
if (!navState.loading && documentEndScripts) {
(ref as React.RefObject<WebView>)?.current?.injectJavaScript(
documentEndScripts
);
}
},
[activeTab, updateTabById, documentEndScripts]
);
// Handle JS execution results
const onMessage = useCallback((event: WebViewMessageEvent) => {
// TODO: Log execution results
}, []);
// Inject CSS to add bottom padding to the body
const injectedCSS = `
const style = document.createElement('style');
style.innerHTML = 'body { padding-bottom: ${bottomPadding}px !important; box-sizing: border-box; }';
document.head.appendChild(style);
`;
const lastScrollY = useRef(0);
const lastDirection = useRef<"up" | "down" | null>(null); // pixels
const handleScroll = (event: any) => {
const currentY = event.nativeEvent.contentOffset?.y ?? 0;
const diff = currentY - lastScrollY.current;
if (Math.abs(diff) > SCROLL_DIRECTION_THRESHOLD) {
const newDirection = diff > 0 ? "down" : "up";
if (newDirection !== lastDirection.current) {
onScrollDirectionChange?.(newDirection);
lastDirection.current = newDirection;
}
}
lastScrollY.current = currentY;
};
return (
<View style={[styles.container, style]} {...props}>
<WebView
ref={ref}
source={{ uri: url }}
injectedJavaScriptBeforeContentLoaded={
injectedJavaScriptBeforeContentLoaded
}
injectedJavaScript={injectedJavaScript}
onNavigationStateChange={onNavigationStateChange}
onMessage={onMessage}
startInLoadingState={true}
allowsInlineMediaPlayback
javaScriptEnabled
domStorageEnabled
setSupportMultipleWindows={false}
allowsBackForwardNavigationGestures
originWhitelist={["*"]}
onScroll={handleScroll}
/>
</View>
);
}
);
export default WebViewContainer;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#ecf0f1",
},
});