Skip to content

Commit 078f223

Browse files
author
Jianlong-Nie
authored
fix(android): block mention events when cursor is inside a finalized mention span (#489)
## Problem Fixes: #490 On Android, after a mention is finalized (user selects from the suggestion list), typing new characters after the mention continues to trigger the mention suggestion list. **Root cause:** `afterTextChangedMentions` uses a `previousWord` fallback — when the cursor moves to a new word after the mention, it looks back and finds `@username` as the previous word, which matches `mentionRegex`, causing `onMention` to fire again even though the mention span is already resolved. ## Fix After determining the candidate range `[finalStart, finalEnd]`, scan for `EnrichedInputMentionSpan` across the full range: - **Span text matches buffer** → mention is intact, call `endMention()` and return. This blocks the spurious event when the cursor is in a word adjacent to a finalized mention span (mirrors iOS `conflictingStyles` behaviour). - **Span text has diverged** (user edited inside it) → remove the stale span and record `mentionStart = spanStart` so `setMentionSpan` can replace the correct range when the user picks a new mention. ## Changes - `android/…/styles/ParametrizedStyles.kt` — reworked `afterTextChangedMentions` span-check logic. before: https://github.com/user-attachments/assets/00cd2d7a-6fb4-498d-bc41-5db36385af26 after: https://github.com/user-attachments/assets/14cffa5c-5797-4530-a08b-70f83b84773c
1 parent 2475695 commit 078f223

1 file changed

Lines changed: 19 additions & 5 deletions

File tree

android/src/main/java/com/swmansion/enriched/textinput/styles/ParametrizedStyles.kt

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -230,11 +230,6 @@ class ParametrizedStyles(
230230
val mentionIndicatorRegex = Regex("^($indicatorsPattern)")
231231
val mentionRegex = Regex("^($indicatorsPattern)\\w*")
232232

233-
val spans = spannable.getSpans(currentWord.start, currentWord.end, EnrichedInputMentionSpan::class.java)
234-
for (span in spans) {
235-
spannable.removeSpan(span)
236-
}
237-
238233
var indicator: String
239234
var finalStart: Int
240235
val finalEnd = currentWord.end
@@ -264,6 +259,25 @@ class ParametrizedStyles(
264259
indicator = mentionIndicatorRegex.find(currentWord.text)?.value ?: ""
265260
}
266261

262+
// Mirror iOS conflicting-styles behaviour: check the full candidate range for
263+
// a finalized mention span. If the span's stored text still matches what is in
264+
// the buffer the mention is intact — block the event (covers HTML-loaded
265+
// mentions and typing adjacent to a freshly-selected mention).
266+
// If the span is stale (user edited inside it), remove it and record mentionStart
267+
// so setMentionSpan can replace text correctly when the user picks a new mention.
268+
val rangeSpans = spannable.getSpans(finalStart, finalEnd, EnrichedInputMentionSpan::class.java)
269+
for (span in rangeSpans) {
270+
val spanStart = spannable.getSpanStart(span)
271+
val spanEnd = spannable.getSpanEnd(span)
272+
val currentSpanText = spannable.subSequence(spanStart, spanEnd).toString()
273+
if (currentSpanText == span.getText()) {
274+
mentionHandler.endMention()
275+
return
276+
}
277+
spannable.removeSpan(span)
278+
mentionStart = spanStart
279+
}
280+
267281
// Extract text without indicator
268282
val text = spannable.subSequence(finalStart, finalEnd).toString().replaceFirst(indicator, "")
269283

0 commit comments

Comments
 (0)