diff --git a/apps/mobile/src/features/threads/thread-list-v2-items.tsx b/apps/mobile/src/features/threads/thread-list-v2-items.tsx index 3951ffef8cab2..74704ef116354 100644 --- a/apps/mobile/src/features/threads/thread-list-v2-items.tsx +++ b/apps/mobile/src/features/threads/thread-list-v2-items.tsx @@ -31,7 +31,7 @@ import { canSnooze } from "@t3tools/client-runtime/state/thread-settled"; import { threadChipCopyTarget } from "@t3tools/client-runtime/thread-reference"; import type { MenuAction } from "@react-native-menu/menu"; import { memo, useCallback, useMemo, useState, type ComponentProps } from "react"; -import { Pressable, useWindowDimensions, View } from "react-native"; +import { Platform, Pressable, useWindowDimensions, View } from "react-native"; import type { SwipeableMethods } from "react-native-gesture-handler/ReanimatedSwipeable"; import type { ThreadListProvider } from "../../state/thread-list-environments"; @@ -637,7 +637,12 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { const metadataTypography = useScaledTextRole("label"); const projectIconSize = titleTypography.fontSize * 1.25 * fontScale; const machineIconSize = metadataTypography.fontSize * fontScale; - const titleLineHeight = titleTypography.lineHeight * fontScale; + // Android's nonlinear system text scaling can differ from the fontScale estimate. + const [androidTitleLineHeight, setTitleLineHeight] = useState( + titleTypography.lineHeight * fontScale, + ); + const titleLineHeight = + Platform.OS === "android" ? androidTitleLineHeight : titleTypography.lineHeight * fontScale; const { thread, variant, @@ -1079,6 +1084,16 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { isRegeneratingTitle && "opacity-55", )} numberOfLines={2} + onTextLayout={ + Platform.OS === "android" + ? ({ nativeEvent }) => { + const firstLine = nativeEvent.lines[0]; + if (firstLine && Math.abs(firstLine.height - titleLineHeight) > 0.5) { + setTitleLineHeight(firstLine.height); + } + } + : undefined + } > {thread.title}