From 9c80bd303a9ad0d3cb77a2c06960127c4b15f6b7 Mon Sep 17 00:00:00 2001 From: Pixel Perfect Date: Wed, 28 Oct 2026 14:19:35 -0700 Subject: [PATCH 1/2] fix(mobile): keep Android thread title spacing consistent --- apps/mobile/src/features/threads/thread-list-v2-items.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 b519cd7227612..e454b28e61b36 100644 --- a/apps/mobile/src/features/threads/thread-list-v2-items.tsx +++ b/apps/mobile/src/features/threads/thread-list-v2-items.tsx @@ -635,7 +635,8 @@ 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 [titleLineHeight, setTitleLineHeight] = useState(titleTypography.lineHeight * fontScale); const { thread, variant, @@ -1062,6 +1063,10 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { : rowAppearance.foregroundClassName, )} numberOfLines={2} + onTextLayout={({ nativeEvent }) => { + const firstLine = nativeEvent.lines[0]; + if (firstLine) setTitleLineHeight(firstLine.height); + }} > {thread.title} From 3b1bc2d1906ba4c7653a79d4f64ca63d2dcc08f9 Mon Sep 17 00:00:00 2001 From: maria-rcks Date: Sat, 31 Oct 2026 07:30:53 +0000 Subject: [PATCH 2/2] fix(mobile): limit title measurements to android changes --- .../features/threads/thread-list-v2-items.tsx | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) 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 12c3a09ba26e0..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"; @@ -638,7 +638,11 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { const projectIconSize = titleTypography.fontSize * 1.25 * fontScale; const machineIconSize = metadataTypography.fontSize * fontScale; // Android's nonlinear system text scaling can differ from the fontScale estimate. - const [titleLineHeight, setTitleLineHeight] = useState(titleTypography.lineHeight * fontScale); + const [androidTitleLineHeight, setTitleLineHeight] = useState( + titleTypography.lineHeight * fontScale, + ); + const titleLineHeight = + Platform.OS === "android" ? androidTitleLineHeight : titleTypography.lineHeight * fontScale; const { thread, variant, @@ -1080,10 +1084,16 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { isRegeneratingTitle && "opacity-55", )} numberOfLines={2} - onTextLayout={({ nativeEvent }) => { - const firstLine = nativeEvent.lines[0]; - if (firstLine) setTitleLineHeight(firstLine.height); - }} + 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}