fix(keyboard): use transform instead of padding for keyboard animation

This commit is contained in:
Mohamed Boudra
2025-10-26 16:19:30 +01:00
parent 985e9db6db
commit cf7e14e36b
3 changed files with 40 additions and 45 deletions

View File

@@ -3,7 +3,7 @@ import { View, Text, ActivityIndicator } from "react-native";
import { useLocalSearchParams } from "expo-router";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { useReanimatedKeyboardAnimation } from "react-native-keyboard-controller";
import ReanimatedAnimated, { useAnimatedStyle } from "react-native-reanimated";
import ReanimatedAnimated, { useAnimatedStyle, useSharedValue } from "react-native-reanimated";
import { StyleSheet, useUnistyles } from "react-native-unistyles";
import { BackHeader } from "@/components/headers/back-header";
import { AgentStreamView } from "@/components/agent-stream-view";
@@ -20,13 +20,18 @@ export default function AgentScreen() {
// Keyboard animation
const { height: keyboardHeight } = useReanimatedKeyboardAnimation();
const bottomInset = insets.bottom;
const bottomInset = useSharedValue(insets.bottom);
useEffect(() => {
bottomInset.value = insets.bottom;
}, [insets.bottom, bottomInset]);
const animatedKeyboardStyle = useAnimatedStyle(() => {
"worklet";
const absoluteHeight = Math.abs(keyboardHeight.value);
const padding = Math.max(0, absoluteHeight - bottomInset);
const shift = Math.max(0, absoluteHeight - bottomInset.value);
return {
paddingBottom: padding,
transform: [{ translateY: -shift }],
};
});
@@ -81,25 +86,26 @@ export default function AgentScreen() {
<BackHeader title={agent.title || "Agent"} />
{/* Content Area with Keyboard Animation */}
<ReanimatedAnimated.View style={[styles.content, animatedKeyboardStyle]}>
{isInitializing ? (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={theme.colors.primary} />
<Text style={styles.loadingText}>Loading agent...</Text>
</View>
) : (
<AgentStreamView
agentId={id!}
agent={agent}
streamItems={streamItems}
pendingPermissions={agentPermissions}
onPermissionResponse={(requestId, optionId) =>
respondToPermission(requestId, id!, agent.sessionId || "", [optionId])
}
keyboardHeight={keyboardHeight}
/>
)}
</ReanimatedAnimated.View>
<View style={styles.contentContainer}>
<ReanimatedAnimated.View style={[styles.content, animatedKeyboardStyle]}>
{isInitializing ? (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={theme.colors.primary} />
<Text style={styles.loadingText}>Loading agent...</Text>
</View>
) : (
<AgentStreamView
agentId={id!}
agent={agent}
streamItems={streamItems}
pendingPermissions={agentPermissions}
onPermissionResponse={(requestId, optionId) =>
respondToPermission(requestId, id!, agent.sessionId || "", [optionId])
}
/>
)}
</ReanimatedAnimated.View>
</View>
</View>
);
}
@@ -109,6 +115,10 @@ const styles = StyleSheet.create((theme) => ({
flex: 1,
backgroundColor: theme.colors.background,
},
contentContainer: {
flex: 1,
overflow: "hidden",
},
content: {
flex: 1,
},

View File

@@ -3,7 +3,7 @@ import { View, Text, ScrollView, Pressable } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { StyleSheet, useUnistyles } from "react-native-unistyles";
import { BottomSheetModal } from "@gorhom/bottom-sheet";
import Animated, { FadeIn, FadeOut, useAnimatedStyle, SharedValue } from "react-native-reanimated";
import Animated, { FadeIn, FadeOut } from "react-native-reanimated";
import { ChevronDown } from "lucide-react-native";
import {
AssistantMessage,
@@ -25,7 +25,6 @@ export interface AgentStreamViewProps {
streamItems: StreamItem[];
pendingPermissions: Map<string, PendingPermission>;
onPermissionResponse: (requestId: string, optionId: string) => void;
keyboardHeight: SharedValue<number>;
}
export function AgentStreamView({
@@ -34,7 +33,6 @@ export function AgentStreamView({
streamItems,
pendingPermissions,
onPermissionResponse,
keyboardHeight,
}: AgentStreamViewProps) {
const scrollViewRef = useRef<ScrollView>(null);
const bottomSheetRef = useRef<BottomSheetModal | null>(null);
@@ -44,17 +42,6 @@ export function AgentStreamView({
const [isNearBottom, setIsNearBottom] = useState(true);
const hasScrolledInitially = useRef(false);
// Animated content padding that responds to keyboard
const contentPaddingStyle = useAnimatedStyle(() => {
"worklet";
const absoluteHeight = Math.abs(keyboardHeight.value);
const keyboardPadding = Math.max(0, absoluteHeight - insets.bottom);
const basePadding = Math.max(insets.bottom, 32);
return {
paddingBottom: basePadding + keyboardPadding,
};
}, [insets.bottom]);
// Scroll to bottom immediately on initial load, then animate for new messages
useEffect(() => {
if (isNearBottom) {
@@ -95,12 +82,10 @@ export function AgentStreamView({
<ScrollView
ref={scrollViewRef}
style={stylesheet.scrollView}
contentContainerStyle={[
{
paddingTop: 24,
},
contentPaddingStyle,
]}
contentContainerStyle={{
paddingTop: 24,
paddingBottom: Math.max(insets.bottom, 32),
}}
onScroll={handleScroll}
scrollEventThrottle={16}
>

View File

@@ -374,13 +374,13 @@
{
"id": "97423777-33d3-414a-ae79-ea303230010a",
"title": "Commit Local Changes",
"sessionId": "019a20d4-5fd4-76db-8021-697b8204597e",
"sessionId": "addbd42f-dbb1-4536-bc2b-e70a64587de6",
"options": {
"type": "claude",
"sessionId": "addbd42f-dbb1-4536-bc2b-e70a64587de6"
},
"createdAt": "2025-10-26T14:02:59.844Z",
"lastActivityAt": "2025-10-26T14:03:22.696Z",
"lastActivityAt": "2025-10-26T14:08:58.462Z",
"cwd": "/Users/moboudra/dev/voice-dev"
}
]