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 (