fix(app): restore expansion of long native user messages (#8089)

Native hidden overflow capped the measured content at the preview height, so long messages never offered Show more. Use the native scroll layout mode on the existing View to measure full content while preserving clipping, and store only whether it overflows.
This commit is contained in:
Mohamed Boudra
2026-10-28 13:46:43 +07:00
committed by GitHub
parent 53bbd7522d
commit befc3d5663
4 changed files with 53 additions and 6 deletions
@@ -0,0 +1,14 @@
# Native user message preview
Open a chat with a user message containing 16 newline-separated lines of
`Preview line N`, with N from 1 to 16. Scroll the message into view on a
phone-sized Android emulator or iOS simulator. Its text must exceed the 240-point preview cap,
while its expanded bubble and toggle fit in the chat viewport. Close any other
expanded messages so the toggle labels identify this message.
Replay `native-user-message-preview.android.ad` or
`native-user-message-preview.ios.ad` through the existing mobile runner or
agent-device. It checks that a clipped message offers Show more and
that two expand/collapse round trips keep both controls reachable. Before the
fix, the first Show more assertion fails because native layout reports the
capped height as the full content height.
@@ -0,0 +1,14 @@
context platform=android timeout=60000 retries=0
env APP_ID=sh.paseo.debug
open "${APP_ID}"
wait "role=\"button\" label=\"Show more\"" 10000
press "role=\"button\" label=\"Show more\""
wait "role=\"button\" label=\"Show less\"" 10000
press "role=\"button\" label=\"Show less\""
wait "role=\"button\" label=\"Show more\"" 10000
press "role=\"button\" label=\"Show more\""
wait "role=\"button\" label=\"Show less\"" 10000
press "role=\"button\" label=\"Show less\""
wait "role=\"button\" label=\"Show more\"" 10000
close
@@ -0,0 +1,14 @@
context platform=ios timeout=60000 retries=0
env APP_ID=sh.paseo.debug
open "${APP_ID}"
wait "role=\"button\" label=\"Show more\"" 10000
press "role=\"button\" label=\"Show more\""
wait "role=\"button\" label=\"Show less\"" 10000
press "role=\"button\" label=\"Show less\""
wait "role=\"button\" label=\"Show more\"" 10000
press "role=\"button\" label=\"Show more\""
wait "role=\"button\" label=\"Show less\"" 10000
press "role=\"button\" label=\"Show less\""
wait "role=\"button\" label=\"Show more\"" 10000
close
@@ -1,5 +1,5 @@
import { useState, useCallback, useMemo, type ReactNode } from "react";
import { View, type LayoutChangeEvent } from "react-native";
import { View, type LayoutChangeEvent, type ViewStyle } from "react-native";
import { StyleSheet } from "react-native-unistyles";
import { useTranslation } from "react-i18next";
import type { AgentMessage } from "@getpaseo/protocol/agent-message";
@@ -8,23 +8,29 @@ import { EdgeFade } from "@/components/ui/edge-fade";
import { useProviderIcon } from "@/components/provider-icons";
import { useSessionStore } from "@/stores/session-store";
import { navigateToAgent } from "@/utils/navigate-to-agent";
import { isNative } from "@/constants/platform";
const PREVIEW_HEIGHT = 240;
// Native View still clips with "scroll", but Yoga measures its children without
// the height cap. "hidden" caps the measured body too, hiding the expand control.
const previewStyle: ViewStyle = {
maxHeight: PREVIEW_HEIGHT,
overflow: isNative ? "scroll" : "hidden",
};
export function MessagePreview({ children }: { children: ReactNode }) {
const { t } = useTranslation();
const [expanded, setExpanded] = useState(false);
const [height, setHeight] = useState(0);
const overflowing = height > PREVIEW_HEIGHT;
const [overflowing, setOverflowing] = useState(false);
const measure = useCallback(
(event: LayoutChangeEvent) => setHeight(event.nativeEvent.layout.height),
(event: LayoutChangeEvent) => setOverflowing(event.nativeEvent.layout.height > PREVIEW_HEIGHT),
[],
);
const toggle = useCallback(() => setExpanded((value) => !value), []);
const accessibilityState = useMemo(() => ({ expanded }), [expanded]);
return (
<>
<View style={!expanded && styles.preview}>
<View style={!expanded && previewStyle}>
<View style={styles.body} onLayout={measure}>
{children}
</View>
@@ -83,7 +89,6 @@ export function MessageSender({
}
const styles = StyleSheet.create((theme) => ({
preview: { maxHeight: PREVIEW_HEIGHT, overflow: "hidden" },
body: { flexShrink: 0 },
sender: { paddingRight: 0, borderRightWidth: 0 },
toggle: { alignSelf: "flex-start", paddingHorizontal: 0, minHeight: 0, borderWidth: 0 },