Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions src/features/browser/screens/BrowserScreen/AddressBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@ import { formatUrlForDisplay } from "../../utils/urlUtils";

interface AddressBarProps {
onFocusChange?: (isFocused: boolean) => void;
onReload: () => void;
onStop: () => void;
}

export default function AddressBar({ onFocusChange }: AddressBarProps) {
export default function AddressBar({
onFocusChange,
onReload,
onStop,
}: AddressBarProps) {
const theme = useTheme();
const { activeTab, navigateTab, activeTabId } = useBrowser();

Expand Down Expand Up @@ -74,13 +80,21 @@ export default function AddressBar({ onFocusChange }: AddressBarProps) {
/>
<View style={[styles.iconRight]}>
<IconButton
icon={activeTab?.url && !isFocused ? Icons.refresh : Icons.microphone}
icon={
activeTab?.isLoading
? Icons.stop
: activeTab?.url && !isFocused
? Icons.refresh
: Icons.microphone
}
color={theme.colors.textSecondary}
size={26}
accessibilityLabel={activeTab?.url ? "Reload" : "Start voice search"}
onPress={() => {
if (activeTabId && activeTab?.url) {
navigateTab(activeTabId, activeTab.url);
if (activeTab?.isLoading) {
onStop();
} else if (activeTabId && activeTab?.url) {
onReload();
}
}}
/>
Expand Down
20 changes: 10 additions & 10 deletions src/features/browser/screens/BrowserScreen/Toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@
import { SafeAreaView } from "react-native-safe-area-context";
import { useBrowser } from "../../hooks/useBrowser";

export default function Toolbar() {
type ToolbarProps = {
onBack: () => void;
onForward: () => void;
onShare?: () => void;
};

export default function Toolbar({ onBack, onForward, onShare }: ToolbarProps) {
const theme = useTheme();
const {
activeTab,
createNewTab,

Check warning on line 19 in src/features/browser/screens/BrowserScreen/Toolbar.tsx

View workflow job for this annotation

GitHub Actions / test

'createNewTab' is assigned a value but never used
closeTabById,

Check warning on line 20 in src/features/browser/screens/BrowserScreen/Toolbar.tsx

View workflow job for this annotation

GitHub Actions / test

'closeTabById' is assigned a value but never used
toggleTabs,
toggleBookmark,
isBookmarked,
Expand All @@ -25,28 +31,22 @@
size={26}
disabled={!activeTab?.canGoBack}
accessibilityLabel="Back"
onPress={() => {
// WebView navigation logic will go here
}}
onPress={onBack}
/>
<IconButton
icon={Icons.forward}
color={theme.colors.textPrimary}
size={26}
disabled={!activeTab?.canGoForward}
accessibilityLabel="Forward"
onPress={() => {
// WebView navigation logic will go here
}}
onPress={onForward}
/>
<IconButton
icon={Icons.share}
color={theme.colors.textPrimary}
size={26}
accessibilityLabel="Share"
onPress={() => {
// More actions (settings, share, etc.)
}}
onPress={onShare}
/>
<IconButton
icon={
Expand Down
175 changes: 100 additions & 75 deletions src/features/browser/screens/BrowserScreen/WebViewContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useScripts } from "@/src/features/scripts/hooks/useScripts";
import { useCallback, useRef } from "react";
import { forwardRef, useCallback, useRef } from "react";
import { StyleSheet, View, ViewProps } from "react-native";
import {
WebView,
Expand All @@ -24,95 +24,120 @@
type WebViewContainerProps = {
bottomPadding?: number;
onScrollDirectionChange?: (direction: "up" | "down") => void;
goBack: () => void;
goForward: () => void;
reload: () => void;
stopLoading: () => void;
} & ViewProps;

export default function WebViewContainer({
bottomPadding = 0,
style,
onScrollDirectionChange,
...props
}: WebViewContainerProps) {
const { activeTab, updateTabById } = useBrowser();
const { getScriptsByRunAt, logExecution } = useScripts();
const webviewRef = useRef<WebView>(null);
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();

Check warning on line 48 in src/features/browser/screens/BrowserScreen/WebViewContainer.tsx

View workflow job for this annotation

GitHub Actions / test

'logExecution' is assigned a value but never used

// Prepare URL
const url = activeTab?.url ? normalizeUrl(activeTab.url) : "about:blank";
// 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)
: "";
// 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]
);
// 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]

Check warning on line 84 in src/features/browser/screens/BrowserScreen/WebViewContainer.tsx

View workflow job for this annotation

GitHub Actions / test

React Hook useCallback has a missing dependency: 'ref'. Either include it or remove the dependency array
);

// Handle JS execution results
const onMessage = useCallback((event: WebViewMessageEvent) => {
// TODO: Log execution results
}, []);
// Handle JS execution results
const onMessage = useCallback((event: WebViewMessageEvent) => {
// TODO: Log execution results
}, []);

// Inject CSS to add bottom padding to the body
const injectedCSS = `
// Inject CSS to add bottom padding to the body
const injectedCSS = `

Check warning on line 93 in src/features/browser/screens/BrowserScreen/WebViewContainer.tsx

View workflow job for this annotation

GitHub Actions / test

'injectedCSS' is assigned a value but never used
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;
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;
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;
};
lastScrollY.current = currentY;
};

return (
<View style={[styles.container, style]} {...props}>
<WebView
ref={webviewRef}
source={{ uri: url }}
injectedJavaScriptBeforeContentLoaded={
injectedJavaScriptBeforeContentLoaded
}
injectedJavaScript={injectedJavaScript}
onNavigationStateChange={onNavigationStateChange}
onMessage={onMessage}
startInLoadingState={true}
allowsInlineMediaPlayback
javaScriptEnabled
domStorageEnabled
setSupportMultipleWindows={false}
allowsBackForwardNavigationGestures
originWhitelist={["*"]}
onScroll={handleScroll}
/>
</View>
);
}
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: {
Expand Down
23 changes: 20 additions & 3 deletions src/features/browser/screens/BrowserScreen/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useTheme } from "@/src/theme/useTheme";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Animated, StyleSheet, View } from "react-native";
import {
KeyboardEvents,
Expand All @@ -17,7 +17,7 @@

export default function BrowserScreen() {
const theme = useTheme();
const { showTabs } = useBrowser();

Check warning on line 20 in src/features/browser/screens/BrowserScreen/index.tsx

View workflow job for this annotation

GitHub Actions / test

'showTabs' is assigned a value but never used
const { height, progress } = useKeyboardAnimation();

const [isAddressBarFocused, setAddressBarFocused] = useState(false);
Expand Down Expand Up @@ -82,6 +82,14 @@
[height, offset, bottomContainerY]
);

// Navigation handlers for WebView
const webviewRef = useRef<any>(null);

const goBack = () => webviewRef.current?.goBack?.();
const goForward = () => webviewRef.current?.goForward?.();
const reload = () => webviewRef.current?.reload?.();
const stopLoading = () => webviewRef.current?.stopLoading?.();

return (
<View style={{ flex: 1 }}>
<SafeAreaView
Expand All @@ -93,6 +101,11 @@
>
<View style={styles.webViewContainer}>
<WebViewContainer
ref={webviewRef}
goBack={goBack}
goForward={goForward}
reload={reload}
stopLoading={stopLoading}
bottomPadding={isKeyboardVisible ? 16 : 0}
onScrollDirectionChange={setScrollDirection}
/>
Expand All @@ -101,11 +114,15 @@

<Animated.View style={[styles.bottomContainer, bottomContainerStyle]}>
{showAddressBar && (
<AddressBar onFocusChange={onAddressBarFocusChange} />
<AddressBar
onReload={reload}
onStop={stopLoading}
onFocusChange={onAddressBarFocusChange}
/>
)}
{!showAddressBar && <AddressBarDisplay />}
{/* Toolbar contains the SafeAreaView */}
<Toolbar />
<Toolbar onBack={goBack} onForward={goForward} />
</Animated.View>
</View>
);
Expand Down