From 78f9b0d06855ba7c34da98cfe83096cb4d62399c Mon Sep 17 00:00:00 2001 From: JamesOBrien2 Date: Sat, 24 Oct 2026 17:05:05 +0100 Subject: [PATCH 1/4] fix(mobile): dismiss keyboard interactively while dragging chat Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- apps/mobile/src/features/threads/ThreadFeed.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/mobile/src/features/threads/ThreadFeed.tsx b/apps/mobile/src/features/threads/ThreadFeed.tsx index 48b1346646acf..1eeaba2c85a04 100644 --- a/apps/mobile/src/features/threads/ThreadFeed.tsx +++ b/apps/mobile/src/features/threads/ThreadFeed.tsx @@ -3135,7 +3135,7 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) { onItemSizeChanged={handleItemSizeChanged} drawDistance={500} keyboardShouldPersistTaps="always" - keyboardDismissMode="none" + keyboardDismissMode="interactive" keyboardLiftBehavior="whenAtEnd" // Seed the list's scroll math with the real viewport before its own // onLayout: the empty→filled remount can then tell at mount that From 79ed6cfae46b90d673f201a5dd839306f67cfad8 Mon Sep 17 00:00:00 2001 From: JamesOBrien2 Date: Sat, 24 Oct 2026 17:15:14 +0100 Subject: [PATCH 2/4] fix(mobile): use native keyboard dismissal per platform Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- apps/mobile/src/features/threads/ThreadFeed.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/mobile/src/features/threads/ThreadFeed.tsx b/apps/mobile/src/features/threads/ThreadFeed.tsx index 1eeaba2c85a04..59b7089a13448 100644 --- a/apps/mobile/src/features/threads/ThreadFeed.tsx +++ b/apps/mobile/src/features/threads/ThreadFeed.tsx @@ -3135,7 +3135,7 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) { onItemSizeChanged={handleItemSizeChanged} drawDistance={500} keyboardShouldPersistTaps="always" - keyboardDismissMode="interactive" + keyboardDismissMode={Platform.OS === "ios" ? "interactive" : "on-drag"} keyboardLiftBehavior="whenAtEnd" // Seed the list's scroll math with the real viewport before its own // onLayout: the empty→filled remount can then tell at mount that From def32c889e98be4d61080494850a2358e86a6451 Mon Sep 17 00:00:00 2001 From: maria-rcks Date: Wed, 28 Oct 2026 03:57:28 +0000 Subject: [PATCH 3/4] fix(mobile): dismiss native android composer on chat drag --- apps/mobile/src/features/threads/ThreadFeed.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/mobile/src/features/threads/ThreadFeed.tsx b/apps/mobile/src/features/threads/ThreadFeed.tsx index 948e251f50a7a..19f0b89ec28cb 100644 --- a/apps/mobile/src/features/threads/ThreadFeed.tsx +++ b/apps/mobile/src/features/threads/ThreadFeed.tsx @@ -123,6 +123,7 @@ import { useWindowDimensions, View, } from "react-native"; +import { KeyboardController } from "react-native-keyboard-controller"; import { Image as ExpoImage } from "expo-image"; import { useResolveClassNames } from "uniwind"; import { useFilePreviewGallery, FilePreviewGallery } from "../../components/FilePreviewGallery"; @@ -2676,6 +2677,9 @@ export const ThreadFeed = memo(function ThreadFeed({ } }; const handleScrollBeginDrag = () => { + if (Platform.OS === "android") { + void KeyboardController.dismiss(); + } clearUserScrollSettle(); userScrollSessionRef.current = true; cancelReflow(); From ed1742f49605706f1d06ce42aa40f8e2b491253d Mon Sep 17 00:00:00 2001 From: maria-rcks Date: Fri, 30 Oct 2026 08:03:17 +0000 Subject: [PATCH 4/4] test(mobile): mock keyboard controller in file previews --- apps/mobile/src/lib/filePreview.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/mobile/src/lib/filePreview.test.ts b/apps/mobile/src/lib/filePreview.test.ts index 97a5051577026..e7bb9baad2a37 100644 --- a/apps/mobile/src/lib/filePreview.test.ts +++ b/apps/mobile/src/lib/filePreview.test.ts @@ -99,6 +99,9 @@ vi.mock("react-native", () => ({ props.children, ), })); +vi.mock("react-native-keyboard-controller", () => ({ + KeyboardController: { dismiss: vi.fn(async () => {}) }, +})); vi.mock("../components/PierreEntryIcon", () => ({ PierreEntryIcon: () => null })); vi.mock("../components/ui/AppText", () => ({ AppText: ({ children }: { children?: ReactNode }) => createElement("span", null, children),