Skip to content

Commit a7f185e

Browse files
authored
Merge pull request #9 from afnx/feature/run-scripts
Feature/run scripts
2 parents 1346a3c + 7efdc80 commit a7f185e

3 files changed

Lines changed: 149 additions & 98 deletions

File tree

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

Lines changed: 0 additions & 65 deletions
This file was deleted.
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { useScripts } from "@/src/features/scripts/hooks/useScripts";
2+
import { useCallback, useRef } from "react";
3+
import { StyleSheet, View, ViewProps } from "react-native";
4+
import {
5+
WebView,
6+
WebViewMessageEvent,
7+
WebViewNavigation,
8+
} from "react-native-webview";
9+
import { useBrowser } from "../../hooks/useBrowser";
10+
import { normalizeUrl } from "../../utils/urlUtils";
11+
12+
const SCROLL_DIRECTION_THRESHOLD = 8;
13+
14+
function getInjectionScripts(
15+
url: string,
16+
runAt: "document-start" | "document-ready" | "document-end",
17+
getScriptsByRunAt: any
18+
) {
19+
return getScriptsByRunAt(url, runAt)
20+
.map((script: any) => script.code)
21+
.join("\n");
22+
}
23+
24+
type WebViewContainerProps = {
25+
bottomPadding?: number;
26+
onScrollDirectionChange?: (direction: "up" | "down") => void;
27+
} & ViewProps;
28+
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);
38+
39+
// Prepare URL
40+
const url = activeTab?.url ? normalizeUrl(activeTab.url) : "about:blank";
41+
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+
: "";
49+
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+
);
64+
65+
// Handle JS execution results
66+
const onMessage = useCallback((event: WebViewMessageEvent) => {
67+
// TODO: Log execution results
68+
}, []);
69+
70+
// Inject CSS to add bottom padding to the body
71+
const injectedCSS = `
72+
const style = document.createElement('style');
73+
style.innerHTML = 'body { padding-bottom: ${bottomPadding}px !important; box-sizing: border-box; }';
74+
document.head.appendChild(style);
75+
`;
76+
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;
82+
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;
88+
}
89+
}
90+
lastScrollY.current = currentY;
91+
};
92+
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+
}
116+
117+
const styles = StyleSheet.create({
118+
container: {
119+
flex: 1,
120+
backgroundColor: "#ecf0f1",
121+
},
122+
});

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

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { useBrowser } from "../../hooks/useBrowser";
1010
import AddressBar from "./AddressBar";
1111
import AddressBarDisplay from "./AddressBarDisplay";
1212
import Toolbar from "./Toolbar";
13-
import WebViewComponent from "./WebViewComponent";
13+
import WebViewContainer from "./WebViewContainer";
1414

1515
const KEYBOARD_OPENED_HEIGHT = 102;
1616
const KEYBOARD_CLOSED_HEIGHT = 0;
@@ -55,30 +55,33 @@ export default function BrowserScreen() {
5555
outputRange: [KEYBOARD_CLOSED_HEIGHT, KEYBOARD_OPENED_HEIGHT],
5656
});
5757

58-
const addressBarStyle = useMemo(
58+
const bottomContainerY = useMemo(() => new Animated.Value(0), []);
59+
60+
useEffect(() => {
61+
Animated.timing(bottomContainerY, {
62+
toValue: showBottomContainer ? 0 : 200,
63+
duration: 400,
64+
useNativeDriver: true,
65+
}).start();
66+
}, [showBottomContainer, bottomContainerY]);
67+
68+
const bottomContainerStyle = useMemo(
5969
() => [
6070
{
6171
transform: [
6272
{
6373
translateY: Animated.add(height, offset),
6474
},
75+
{
76+
translateY: bottomContainerY,
77+
},
6578
],
6679
},
6780
{ backgroundColor: "white" },
6881
],
69-
[height, offset]
82+
[height, offset, bottomContainerY]
7083
);
7184

72-
const bottomContainerY = useMemo(() => new Animated.Value(0), []);
73-
74-
useEffect(() => {
75-
Animated.timing(bottomContainerY, {
76-
toValue: showBottomContainer ? 0 : 200,
77-
duration: 400,
78-
useNativeDriver: true,
79-
}).start();
80-
}, [showBottomContainer, bottomContainerY]);
81-
8285
return (
8386
<View style={{ flex: 1 }}>
8487
<SafeAreaView
@@ -89,32 +92,18 @@ export default function BrowserScreen() {
8992
edges={["top"]}
9093
>
9194
<View style={styles.webViewContainer}>
92-
<WebViewComponent
95+
<WebViewContainer
9396
bottomPadding={isKeyboardVisible ? 16 : 0}
9497
onScrollDirectionChange={setScrollDirection}
9598
/>
9699
</View>
97100
</SafeAreaView>
98101

99-
<Animated.View
100-
style={[
101-
styles.bottomContainer,
102-
{
103-
backgroundColor: "white",
104-
transform: [{ translateY: bottomContainerY }],
105-
position: "absolute",
106-
left: 0,
107-
right: 0,
108-
bottom: 0,
109-
},
110-
]}
111-
>
112-
<Animated.View style={addressBarStyle}>
113-
{showAddressBar && (
114-
<AddressBar onFocusChange={onAddressBarFocusChange} />
115-
)}
116-
{!showAddressBar && <AddressBarDisplay />}
117-
</Animated.View>
102+
<Animated.View style={[styles.bottomContainer, bottomContainerStyle]}>
103+
{showAddressBar && (
104+
<AddressBar onFocusChange={onAddressBarFocusChange} />
105+
)}
106+
{!showAddressBar && <AddressBarDisplay />}
118107
{/* Toolbar contains the SafeAreaView */}
119108
<Toolbar />
120109
</Animated.View>
@@ -132,5 +121,10 @@ const styles = StyleSheet.create({
132121
},
133122
bottomContainer: {
134123
width: "100%",
124+
backgroundColor: "white",
125+
position: "absolute",
126+
left: 0,
127+
right: 0,
128+
bottom: 0,
135129
},
136130
});

0 commit comments

Comments
 (0)