From ba2053dd4c1eec8623d53a21cf79f7e9c86ec975 Mon Sep 17 00:00:00 2001 From: suraj-markup Date: Tue, 3 Mar 2026 03:48:44 +0530 Subject: [PATCH] fix(mobile): replace KeyboardAvoidingView with Keyboard API listener KeyboardAvoidingView was causing two issues: scrolling the page to the top on focus, and not restoring the layout when dismissing the keyboard. New approach uses Keyboard.addListener to track keyboard height and applies it as paddingBottom on the message bar directly. This works reliably on both iOS and Android regardless of softInputMode. - Removed softwareKeyboardLayoutMode: "pan" from app.json - Replaced KAV with plain View + dynamic keyboard padding - Reverted SpawnSession/Settings screens to simple iOS-only KAV Co-Authored-By: Claude Opus 4.6 --- packages/mobile/app.json | 3 +- .../src/screens/SessionDetailScreen.tsx | 35 ++++++++++++------- .../mobile/src/screens/SettingsScreen.tsx | 3 +- .../mobile/src/screens/SpawnSessionScreen.tsx | 3 +- 4 files changed, 26 insertions(+), 18 deletions(-) diff --git a/packages/mobile/app.json b/packages/mobile/app.json index 7e66845b9..04e78a318 100644 --- a/packages/mobile/app.json +++ b/packages/mobile/app.json @@ -30,8 +30,7 @@ "foregroundImage": "./assets/adaptive-icon.png", "backgroundColor": "#0d1117" }, - "package": "com.composio.aomobile", - "softwareKeyboardLayoutMode": "pan" + "package": "com.composio.aomobile" }, "web": { "favicon": "./assets/favicon.png" diff --git a/packages/mobile/src/screens/SessionDetailScreen.tsx b/packages/mobile/src/screens/SessionDetailScreen.tsx index c60e1b10a..1be162ce6 100644 --- a/packages/mobile/src/screens/SessionDetailScreen.tsx +++ b/packages/mobile/src/screens/SessionDetailScreen.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback, useRef } from "react"; +import React, { useState, useCallback, useRef, useEffect } from "react"; import { View, Text, @@ -8,7 +8,7 @@ import { TouchableOpacity, Alert, ActivityIndicator, - KeyboardAvoidingView, + Keyboard, Platform, Linking, } from "react-native"; @@ -43,6 +43,24 @@ export default function SessionDetailScreen({ route, navigation }: Props) { const [ciFixState, setCiFixState] = useState("idle"); const [commentFixStates, setCommentFixStates] = useState>({}); const scrollRef = useRef(null); + const [keyboardHeight, setKeyboardHeight] = useState(0); + + useEffect(() => { + const showEvent = Platform.OS === "ios" ? "keyboardWillShow" : "keyboardDidShow"; + const hideEvent = Platform.OS === "ios" ? "keyboardWillHide" : "keyboardDidHide"; + + const showSub = Keyboard.addListener(showEvent, (e) => { + setKeyboardHeight(e.endCoordinates.height); + }); + const hideSub = Keyboard.addListener(hideEvent, () => { + setKeyboardHeight(0); + }); + + return () => { + showSub.remove(); + hideSub.remove(); + }; + }, []); const handleSend = useCallback(async () => { if (!message.trim()) return; @@ -162,11 +180,7 @@ export default function SessionDetailScreen({ route, navigation }: Props) { const unresolvedComments = pr?.unresolvedComments ?? []; return ( - + {/* Header row */} @@ -336,7 +350,7 @@ export default function SessionDetailScreen({ route, navigation }: Props) { {/* Message input — only show for active sessions */} {!isDone && ( - + 0 ? keyboardHeight - (Platform.OS === "ios" ? 34 : 0) : 10 }]}> { - setTimeout(() => scrollRef.current?.scrollToEnd({ animated: true }), 300); - }} /> )} - + ); } diff --git a/packages/mobile/src/screens/SettingsScreen.tsx b/packages/mobile/src/screens/SettingsScreen.tsx index e28e248bc..0b9ac101f 100644 --- a/packages/mobile/src/screens/SettingsScreen.tsx +++ b/packages/mobile/src/screens/SettingsScreen.tsx @@ -132,8 +132,7 @@ export default function SettingsScreen({ navigation }: Props) { return ( diff --git a/packages/mobile/src/screens/SpawnSessionScreen.tsx b/packages/mobile/src/screens/SpawnSessionScreen.tsx index 34163e3f5..0d52a696c 100644 --- a/packages/mobile/src/screens/SpawnSessionScreen.tsx +++ b/packages/mobile/src/screens/SpawnSessionScreen.tsx @@ -52,8 +52,7 @@ export default function SpawnSessionScreen({ navigation }: Props) { return (