Conversation
Android's ReactEditText silently drops JS text commands whose event count is behind the native counter, and JS only learns about native counter bumps asynchronously via onChange. A programmatic replace that races an IME transition (e.g. confirming dictation) can carry a stale count and be dropped, leaving the native composer text stale while the draft state stays correct — the dictated transcript was sendable but invisible until the composer remounted. The RN 0.81 Kotlin handler only compares the event count and never adopts the passed value, so replaceText now sends the maximum count on Android to make the write unconditional.
|
Closing for now: this needs a failing-first regression for the Android IME/event-count race and a device recording or event trace showing dictation → Edit and subsequent typing work. Please reopen with that evidence. |
|
Please disregard the earlier invitation to reopen with evidence. This PR remains closed unless I explicitly follow up. You don't need to resubmit this PR or provide further QA evidence. Please read the updated PR policy. For now, I'm automatically closing feature PRs so I can focus more time on bug fixes and core improvements. Focused bug-fix PRs with a clear reproduction and QA are still welcome. The previous guide already asked contributors to submit only if they were comfortable with closure, and explained that unsolicited PRs could be closed without a detailed review. For feature ideas, please start in GitHub Discussions and share your workflow: what you're trying to do, how you do it today, and where Paseo gets in the way. If there's already a discussion about it, join in and share your use case. I'll periodically review discussions for highly requested workflows and use that feedback to shape the roadmap. I'll periodically look through closed PRs and choose which contributions to take forward, whether solicited or unsolicited. I may reopen a PR, including one closed by mistake, or use it as a reference for my own implementation, with attribution either way. There are hundreds of PRs alongside ongoing development. I can't commit to reviewing every submission, providing individual feedback or giving timelines. |
Linked issue
Closes #3904
Type of change
Reasoning
On Android, the composer's native text updates go through RN's
setTextAndSelectioncommand, andReactEditTextsilently drops a JS text command whose event count is behind the native counter (canUpdateWithEventCount, ReactEditText.kt). JS only learns about native counter bumps asynchronously viaonChange, so a programmatic replace that races an IME transition — exactly what happens when a dictation transcript is confirmed while the IME commits its composing region — carries a stale count and is dropped without any error.This explains the symptoms in #3904 precisely:
defaultValuefrom the draft store).The fix is in the vendored
@mattermost/react-native-paste-inputpatch that this repo already owns:replaceTextnow sends the maximum event count on Android. RN 0.81's Kotlin handler (maybeSetText/maybeSetSelection) only compares the count and never adopts the passed value (the native counter is only advanced byincrementAndGetEventCounteron native text changes), so this makes the write unconditional and cannot desync future updates. iOS keeps using the real count.Goals
Non-goals
clear()keeps its current behavior.QA
replaceText, and that normal typing, send, queue, and clear-after-send still behave.npm run typecheck(0 errors),npm run lint(0 warnings),npm run format:checkclean;packages/app/src/components/ui/text-input/text-input.native.test.tsx(5 tests) andpackages/app/src/composer/input/state.test.ts(18 tests) pass.patch-packageand verified it applies cleanly to a pristine@mattermost/react-native-paste-input@2.0.1install.Checklist
npm run typecheckpassesnpm run lintpassesnpm run formatpasses