diff --git a/src/features/browser/screens/BrowserScreen/WebViewComponent.tsx b/src/features/browser/screens/BrowserScreen/WebViewComponent.tsx
deleted file mode 100644
index 406eeb3..0000000
--- a/src/features/browser/screens/BrowserScreen/WebViewComponent.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import { useRef } from "react";
-import { StyleSheet, View, ViewProps } from "react-native";
-import { WebView } from "react-native-webview";
-import { useBrowser } from "../../hooks/useBrowser";
-
-type WebViewComponentProps = {
- bottomPadding?: number;
- onScrollDirectionChange?: (direction: "up" | "down") => void;
-} & ViewProps;
-
-const SCROLL_DIRECTION_THRESHOLD = 8;
-
-export default function WebViewComponent({
- bottomPadding = 0,
- style,
- onScrollDirectionChange,
- ...props
-}: WebViewComponentProps) {
- const { activeTab, updateTabById } = useBrowser();
- const { url } = activeTab || {};
- // 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 (
-
- {
- if (activeTab?.id) {
- updateTabById(activeTab!.id, { url: navState.url });
- }
- }}
- onScroll={handleScroll}
- />
-
- );
-}
-
-const styles = StyleSheet.create({
- container: {
- flex: 1,
- backgroundColor: "#ecf0f1",
- },
-});
diff --git a/src/features/browser/screens/BrowserScreen/WebViewContainer.tsx b/src/features/browser/screens/BrowserScreen/WebViewContainer.tsx
new file mode 100644
index 0000000..307447d
--- /dev/null
+++ b/src/features/browser/screens/BrowserScreen/WebViewContainer.tsx
@@ -0,0 +1,122 @@
+import { useScripts } from "@/src/features/scripts/hooks/useScripts";
+import { 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;
+} & ViewProps;
+
+export default function WebViewContainer({
+ bottomPadding = 0,
+ style,
+ onScrollDirectionChange,
+ ...props
+}: WebViewContainerProps) {
+ const { activeTab, updateTabById } = useBrowser();
+ const { getScriptsByRunAt, logExecution } = useScripts();
+ const webviewRef = useRef(null);
+
+ // 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)
+ : "";
+
+ // 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,
+ });
+ },
+ [activeTab, updateTabById]
+ );
+
+ // 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 (
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: "#ecf0f1",
+ },
+});
diff --git a/src/features/browser/screens/BrowserScreen/index.tsx b/src/features/browser/screens/BrowserScreen/index.tsx
index d3fc774..db74dd5 100644
--- a/src/features/browser/screens/BrowserScreen/index.tsx
+++ b/src/features/browser/screens/BrowserScreen/index.tsx
@@ -10,7 +10,7 @@ import { useBrowser } from "../../hooks/useBrowser";
import AddressBar from "./AddressBar";
import AddressBarDisplay from "./AddressBarDisplay";
import Toolbar from "./Toolbar";
-import WebViewComponent from "./WebViewComponent";
+import WebViewContainer from "./WebViewContainer";
const KEYBOARD_OPENED_HEIGHT = 102;
const KEYBOARD_CLOSED_HEIGHT = 0;
@@ -55,30 +55,33 @@ export default function BrowserScreen() {
outputRange: [KEYBOARD_CLOSED_HEIGHT, KEYBOARD_OPENED_HEIGHT],
});
- const addressBarStyle = useMemo(
+ const bottomContainerY = useMemo(() => new Animated.Value(0), []);
+
+ useEffect(() => {
+ Animated.timing(bottomContainerY, {
+ toValue: showBottomContainer ? 0 : 200,
+ duration: 400,
+ useNativeDriver: true,
+ }).start();
+ }, [showBottomContainer, bottomContainerY]);
+
+ const bottomContainerStyle = useMemo(
() => [
{
transform: [
{
translateY: Animated.add(height, offset),
},
+ {
+ translateY: bottomContainerY,
+ },
],
},
{ backgroundColor: "white" },
],
- [height, offset]
+ [height, offset, bottomContainerY]
);
- const bottomContainerY = useMemo(() => new Animated.Value(0), []);
-
- useEffect(() => {
- Animated.timing(bottomContainerY, {
- toValue: showBottomContainer ? 0 : 200,
- duration: 400,
- useNativeDriver: true,
- }).start();
- }, [showBottomContainer, bottomContainerY]);
-
return (
-
-
-
- {showAddressBar && (
-
- )}
- {!showAddressBar && }
-
+
+ {showAddressBar && (
+
+ )}
+ {!showAddressBar && }
{/* Toolbar contains the SafeAreaView */}
@@ -132,5 +121,10 @@ const styles = StyleSheet.create({
},
bottomContainer: {
width: "100%",
+ backgroundColor: "white",
+ position: "absolute",
+ left: 0,
+ right: 0,
+ bottom: 0,
},
});