From d00dd3673e04a6e87c6aa1dbe35928b2c6e6692b Mon Sep 17 00:00:00 2001 From: Michael Geers Date: Tue, 18 Aug 2026 11:19:36 +0200 Subject: [PATCH 1/2] Android: back button navigates webview history --- screens/MainScreen.tsx | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/screens/MainScreen.tsx b/screens/MainScreen.tsx index 7ca0b2f..d749fa3 100644 --- a/screens/MainScreen.tsx +++ b/screens/MainScreen.tsx @@ -1,6 +1,13 @@ import { useState, useEffect, useRef, useCallback, useMemo } from "react"; import { WebView, WebViewMessageEvent } from "react-native-webview"; -import { StyleSheet, Animated, View, Text } from "react-native"; +import { + StyleSheet, + Animated, + View, + Text, + Platform, + BackHandler, +} from "react-native"; import * as Linking from "expo-linking"; import * as Haptics from "expo-haptics"; import AppText from "components/AppText"; @@ -14,6 +21,7 @@ import { WebViewErrorEvent, WebViewHttpErrorEvent, WebViewTerminatedEvent, + WebViewNavigation, } from "react-native-webview/lib/WebViewTypes"; import { NativeStackScreenProps } from "@react-navigation/native-stack"; import { RootStackParamList } from "types"; @@ -32,6 +40,7 @@ export default function MainScreen({ const insets = useSafeAreaInsets(); const { activeServer, targetPath, clearTargetPath } = useAppContext(); const webViewRef = useRef(null); + const canGoBackRef = useRef(false); const [isConnected, setIsConnected] = useState(false); const [webViewKey, setWebViewKey] = useState(0); const [downloadedFile, setDownloadedFile] = useState(null); @@ -62,6 +71,22 @@ export default function MainScreen({ } }, [targetPath, isConnected, clearTargetPath]); + // Android back button navigates the web UI's history instead of leaving the app + useEffect(() => { + if (Platform.OS !== "android") return; + const subscription = BackHandler.addEventListener( + "hardwareBackPress", + () => { + if (navigation.isFocused() && canGoBackRef.current) { + webViewRef.current?.goBack(); + return true; + } + return false; + }, + ); + return () => subscription.remove(); + }, [navigation]); + // Reconnect if connection is lost useEffect(() => { let intervalId: NodeJS.Timeout | undefined; @@ -202,6 +227,13 @@ export default function MainScreen({ setIsConnected(false); }, []); + const onNavigationStateChange = useCallback( + (navState: WebViewNavigation) => { + canGoBackRef.current = navState.canGoBack; + }, + [], + ); + const LayoutMemoized = useMemo( () => ( @@ -239,6 +271,7 @@ export default function MainScreen({ onContentProcessDidTerminate={onTerminate} onMessage={handleMessage} onShouldStartLoadWithRequest={onShouldStartLoadWithRequest} + onNavigationStateChange={onNavigationStateChange} /> Date: Tue, 18 Aug 2026 11:19:36 +0200 Subject: [PATCH 2/2] docs: commit and PR prefix conventions --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 2910b18..aa5e1db 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -128,4 +128,4 @@ Avoid file paths, line numbers, or code listings reproduced from the diff. Inclu ## Git Workflow - Main branch: `main` -- Conventional-ish prefixes are common in history: `feat:`, `fix:`, `chore:`, `test(e2e):`, `chore(deps):`. Keep messages short. +- Commit and PR subjects: `prefix: short description`, no trailing period. Platform-specific changes use `Android: …` / `iOS: …`; everything else is prefixed with the affected part of the app (`onboarding: …`, `widget: …`) or `chore:` / `fix:` / `docs:` for non-feature changes. Same pattern as the main evcc repo. Keep messages short.