From a1c6e8dfafee91ac250af09553f7975a38fb3c25 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Sat, 17 Oct 2026 00:38:15 +0700 Subject: [PATCH] fix(app): keep iOS fullscreen composer above the keyboard (#7170) Keep keyboard translation attached to the native animation graph when fullscreen resizes the dock. Apply the enabled mode on the native animation frame so Fabric does not restore the previous transform during layout. --- .../native-composer-fullscreen.ios.ad | 18 +++++++++++++++ .../keyboard/shift/internal/translate.ios.tsx | 22 +++++++++++++++---- 2 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 packages/app/e2e/mobile/agent-device/native-composer-fullscreen.ios.ad diff --git a/packages/app/e2e/mobile/agent-device/native-composer-fullscreen.ios.ad b/packages/app/e2e/mobile/agent-device/native-composer-fullscreen.ios.ad new file mode 100644 index 0000000000..d991b227b7 --- /dev/null +++ b/packages/app/e2e/mobile/agent-device/native-composer-fullscreen.ios.ad @@ -0,0 +1,18 @@ +context platform=ios timeout=60000 retries=0 +# Start with an empty composer in an idle chat or new-agent draft on an iPhone with the software keyboard enabled. +# Expand a long draft with the keyboard open; Exit fullscreen must remain reachable. +open sh.paseo.debug +wait "editable=true" 60000 +fill "editable=true" "iOS fullscreen composer smoke.\n\nLine 1: Check that the composer keeps this draft when expanded and collapsed.\nLine 2: Check that the composer keeps this draft when expanded and collapsed.\nLine 3: Check that the composer keeps this draft when expanded and collapsed.\nLine 4: Check that the composer keeps this draft when expanded and collapsed.\nLine 5: Check that the composer keeps this draft when expanded and collapsed.\nLine 6: Check that the composer keeps this draft when expanded and collapsed.\nLine 7: Check that the composer keeps this draft when expanded and collapsed.\nLine 8: Check that the composer keeps this draft when expanded and collapsed.\nLine 9: Check that the composer keeps this draft when expanded and collapsed.\nLine 10: Check that the composer keeps this draft when expanded and collapsed.\nLine 11: Check that the composer keeps this draft when expanded and collapsed.\nLine 12: Check that the composer keeps this draft when expanded and collapsed.\nLine 13: Check that the composer keeps this draft when expanded and collapsed.\nLine 14: Check that the composer keeps this draft when expanded and collapsed." +press "label=\"Fullscreen\"" +wait "label=\"Exit fullscreen\" role=button" 5000 +is visible "label=\"Exit fullscreen\" role=button" +press "label=\"Exit fullscreen\" role=button" +wait "label=\"Fullscreen\" role=button" 5000 +is visible "label=\"Fullscreen\" role=button" +press "label=\"Fullscreen\"" +wait "label=\"Exit fullscreen\" role=button" 5000 +is visible "label=\"Exit fullscreen\" role=button" +press "label=\"Exit fullscreen\" role=button" +wait "label=\"Fullscreen\" role=button" 5000 +is visible "label=\"Fullscreen\" role=button" diff --git a/packages/app/src/keyboard/shift/internal/translate.ios.tsx b/packages/app/src/keyboard/shift/internal/translate.ios.tsx index 348fe828d0..1fea4fab0c 100644 --- a/packages/app/src/keyboard/shift/internal/translate.ios.tsx +++ b/packages/app/src/keyboard/shift/internal/translate.ios.tsx @@ -1,5 +1,5 @@ -import { useMemo, type ReactNode } from "react"; -import { Animated, type ViewProps } from "react-native"; +import { useMemo, useEffect, type ReactNode } from "react"; +import { Animated, useAnimatedValue, type ViewProps } from "react-native"; import { useKeyboardAnimation } from "react-native-keyboard-controller"; import { useSafeAreaInsets } from "react-native-safe-area-context"; @@ -32,9 +32,23 @@ export function KeyboardTranslateView({ }), [bottomClearance, height, insets.bottom, progress], ); + // Fullscreen resizes this view while disabling translation. iOS Fabric can + // reapply its previous transform during that layout commit. Keep the graph + // attached and apply the mode on the native animation frame; a static zero or + // setValue during the React commit gets overwritten by the resize. + const enabledValue = useAnimatedValue(enabled ? 1 : 0); + useEffect(() => { + const animation = Animated.timing(enabledValue, { + toValue: enabled ? 1 : 0, + duration: 0, + useNativeDriver: true, + }); + animation.start(); + return () => animation.stop(); + }, [enabled, enabledValue]); const keyboardStyle = useMemo( - () => ({ transform: [{ translateY: enabled ? translateY : 0 }] }), - [enabled, translateY], + () => ({ transform: [{ translateY: Animated.multiply(translateY, enabledValue) }] }), + [enabledValue, translateY], ); return (