From 7c6330ffd2426c161f54048c8f4b71938edb49f8 Mon Sep 17 00:00:00 2001 From: shivam <91240327+shivamhwp@users.noreply.github.com> Date: Tue, 3 Nov 2026 23:09:33 +0000 Subject: [PATCH 1/2] fix(mobile): custom question answers autocorrect on iOS The custom-answer field never set autoCorrect or spellCheck, so Fabric left the UITextView at UITextAutocorrectionTypeDefault. On device, typos stayed uncorrected while the main composer, which sets both to true, corrected the same taps. Set both explicitly on that field, the same way the composer does. Co-Authored-By: Claude Opus 5.5 --- apps/mobile/src/features/threads/QuestionAttachments.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/mobile/src/features/threads/QuestionAttachments.tsx b/apps/mobile/src/features/threads/QuestionAttachments.tsx index 724a607f57d52..a92067ab66538 100644 --- a/apps/mobile/src/features/threads/QuestionAttachments.tsx +++ b/apps/mobile/src/features/threads/QuestionAttachments.tsx @@ -199,6 +199,8 @@ export function QuestionAttachments(props: { multiline={props.question.initialAnswer !== undefined} submitBehavior={props.question.initialAnswer !== undefined ? "newline" : undefined} editable={!props.disabled} + autoCorrect + spellCheck onChangeText={props.onChangeText} onFocus={() => { focusedRef.current = true; From 62834cd8b2989499ac2d576fdadf7b02f92b401e Mon Sep 17 00:00:00 2001 From: shivam <91240327+shivamhwp@users.noreply.github.com> Date: Wed, 4 Nov 2026 00:48:45 +0000 Subject: [PATCH 2/2] fix(mobile): keep iOS autocorrect in custom question answers The custom-answer field was a controlled TextInput, so every keystroke changed its text prop and Fabric rebuilt the native attributed string. That reassignment drops the pending autocorrection, so typos stayed as typed while the native composer, which syncs by revision, corrected them. Setting autoCorrect and spellCheck did not help and is removed. The input is now uncontrolled: it starts from the stored draft and reports changes to it as before. The only outside write while it is mounted, an option selection clearing the draft, clears the native input through its ref. Co-Authored-By: Claude Opus 5.5 --- .../features/threads/QuestionAttachments.tsx | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/apps/mobile/src/features/threads/QuestionAttachments.tsx b/apps/mobile/src/features/threads/QuestionAttachments.tsx index a92067ab66538..d98488c755dda 100644 --- a/apps/mobile/src/features/threads/QuestionAttachments.tsx +++ b/apps/mobile/src/features/threads/QuestionAttachments.tsx @@ -7,7 +7,7 @@ import { useNativePaste } from "../../lib/useNativePaste"; import { convertPastedImagesToAttachments } from "../../lib/composerImages"; import { PROVIDER_SEND_TURN_MAX_ATTACHMENTS, type UserInputQuestion } from "@t3tools/contracts"; import { useAtomValue } from "@effect/atom-react"; -import { View } from "react-native"; +import { View, type TextInputInstance } from "react-native"; import { useEffect, useEffectEvent, useRef, useState } from "react"; import { ComposerAttachmentButton } from "./ComposerAttachmentButton"; import { ComposerAttachmentStrip } from "../../components/ComposerAttachmentStrip"; @@ -64,6 +64,18 @@ export function QuestionAttachments(props: { }); // oxlint-disable-next-line t3code/no-unreasoned-state-sync -- releases keyboard ownership on unmount, since an unmounted native input never emits blur useEffect(() => () => releaseFocus(), []); + // A controlled value makes Fabric rebuild the native text on every keystroke, which discards + // iOS's pending autocorrection. The input owns its text; the only outside write is an option + // selection clearing the draft. + const inputRef = useRef(null); + const [initialText] = useState(props.value); + const nativeText = useRef(props.value); + // oxlint-disable-next-line t3code/no-unreasoned-state-sync -- pushes an option selection's cleared draft into the uncontrolled native input + useEffect(() => { + if (props.value !== "" || nativeText.current === "") return; + nativeText.current = ""; + inputRef.current?.clear(); + }, [props.value]); const append = ( key: string, attachments: Parameters[1], @@ -195,13 +207,15 @@ export function QuestionAttachments(props: { setPreviewVideo(null)} /> { + nativeText.current = text; + props.onChangeText(text); + }} onFocus={() => { focusedRef.current = true; onInputFocusChange?.(true);