From aaed8d1efe6e51baae3dbda366e9903e017a3c2a Mon Sep 17 00:00:00 2001 From: Igor Furgala Date: Thu, 13 Nov 2025 09:56:13 +0100 Subject: [PATCH 1/3] fix: setting up default values on Android --- .../enriched/EnrichedTextInputView.kt | 23 ++++++++++++++++++- .../enriched/EnrichedTextInputViewManager.kt | 4 ++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt b/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt index bae67e50b..4508f37c2 100644 --- a/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt +++ b/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt @@ -70,6 +70,8 @@ class EnrichedTextInputView : AppCompatEditText { private var fontFamily: String? = null private var fontStyle: Int = ReactConstants.UNSET private var fontWeight: Int = ReactConstants.UNSET + private var defaultValue: CharSequence? = null + private var defaultValueDirty: Boolean = false private var inputMethodManager: InputMethodManager? = null @@ -360,7 +362,26 @@ class EnrichedTextInputView : AppCompatEditText { return false } - fun updateTypeface() { + fun afterUpdateTransaction() { + updateTypeface() + updateDefaultValue() + } + + fun setDefaultValue(value: CharSequence?) { + defaultValue = value + defaultValueDirty = true + } + + private fun updateDefaultValue() { + if (!defaultValueDirty) return + + defaultValueDirty = false + setValue(defaultValue ?: "") + } + + private fun updateTypeface() { + setValue(defaultValue) + if (!typefaceDirty) return typefaceDirty = false diff --git a/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt b/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt index 418c3fb2a..8676ef14e 100644 --- a/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt +++ b/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt @@ -78,7 +78,7 @@ class EnrichedTextInputViewManager : SimpleViewManager(), @ReactProp(name = "defaultValue") override fun setDefaultValue(view: EnrichedTextInputView?, value: String?) { - view?.setValue(value) + view?.setDefaultValue(value) } @ReactProp(name = "placeholder") @@ -157,7 +157,7 @@ class EnrichedTextInputViewManager : SimpleViewManager(), override fun onAfterUpdateTransaction(view: EnrichedTextInputView) { super.onAfterUpdateTransaction(view) - view.updateTypeface() + view.afterUpdateTransaction() } override fun setPadding( From 653ad2c57f54b9c879cc73df05680a6bd193e640 Mon Sep 17 00:00:00 2001 From: Igor Furgala Date: Thu, 13 Nov 2025 09:58:21 +0100 Subject: [PATCH 2/3] chore: remove leftover --- .../main/java/com/swmansion/enriched/EnrichedTextInputView.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt b/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt index 4508f37c2..62cb17bce 100644 --- a/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt +++ b/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt @@ -380,8 +380,6 @@ class EnrichedTextInputView : AppCompatEditText { } private fun updateTypeface() { - setValue(defaultValue) - if (!typefaceDirty) return typefaceDirty = false From a662c3587871eb2be13ab5526b973428670032dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Szyd=C5=82owski?= <9szydlowski9@gmail.com> Date: Mon, 17 Nov 2025 15:51:20 +0100 Subject: [PATCH 3/3] fix: state change emitting with default values improvement (#251) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://github.com/software-mansion/react-native-enriched/issues/237 Turns out `eventEmitter` isn't available from the very beginning, but the iOS code was remembering recently active styles even if no state change event was emitted. This way, when the event emitter was finally available, no state change event was emitted because recently active styles were exactly same (even though no event went out). Except that, I added all lifecycle callbacks (`anyTextMayHaveBeenModified` that e.g. runs the state change event) to the `didMoveToWindow` because it seems like the event emitter is defined then. The issue was;`defaultValue` was set but its state changes emitted only when some change was done to an input (and not on focus or when eventEmitter is available). The PR fixes both scenarios. --------- Co-authored-by: Igor FurgaƂa <74370735+exploIF@users.noreply.github.com> --- ios/EnrichedTextInputView.mm | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/ios/EnrichedTextInputView.mm b/ios/EnrichedTextInputView.mm index a2d4cd326..df6783fed 100644 --- a/ios/EnrichedTextInputView.mm +++ b/ios/EnrichedTextInputView.mm @@ -601,6 +601,9 @@ - (void)tryUpdatingActiveStyles { // style updates are emitted only if something differs from the previously active styles BOOL updateNeeded = NO; + // active styles are kept in a separate set until we're sure they can be emitted + NSMutableSet *newActiveStyles = [_activeStyles mutableCopy]; + // data for onLinkDetected event LinkData *detectedLinkData; NSRange detectedLinkRange = NSMakeRange(0, 0); @@ -611,14 +614,14 @@ - (void)tryUpdatingActiveStyles { for (NSNumber* type in stylesDict) { id style = stylesDict[type]; - BOOL wasActive = [_activeStyles containsObject: type]; + BOOL wasActive = [newActiveStyles containsObject: type]; BOOL isActive = [style detectStyle:textView.selectedRange]; if(wasActive != isActive) { updateNeeded = YES; if(isActive) { - [_activeStyles addObject:type]; + [newActiveStyles addObject:type]; } else { - [_activeStyles removeObject:type]; + [newActiveStyles removeObject:type]; } } @@ -678,6 +681,9 @@ - (void)tryUpdatingActiveStyles { if(updateNeeded) { auto emitter = [self getEventEmitter]; if(emitter != nullptr) { + // update activeStyles only if emitter is available + _activeStyles = newActiveStyles; + emitter->onChangeState({ .isBold = [_activeStyles containsObject: @([BoldStyle getStyleType])], .isItalic = [_activeStyles containsObject: @([ItalicStyle getStyleType])], @@ -990,6 +996,9 @@ - (void)manageSelectionBasedChanges { textView.typingAttributes = defaultTypingAttributes; } } + + // update active styles as well + [self tryUpdatingActiveStyles]; } - (void)handleWordModificationBasedChanges:(NSString*)word inRange:(NSRange)range { @@ -1105,17 +1114,18 @@ - (void)_performRelayout if (!textView) { return; } dispatch_async(dispatch_get_main_queue(), ^{ - NSRange wholeRange = NSMakeRange(0, textView.textStorage.string.length); + NSRange wholeRange = NSMakeRange(0, self->textView.textStorage.string.length); NSRange actualRange = NSMakeRange(0, 0); - [textView.layoutManager invalidateLayoutForCharacterRange:wholeRange actualCharacterRange:&actualRange]; - [textView.layoutManager ensureLayoutForCharacterRange:actualRange]; - [textView.layoutManager invalidateDisplayForCharacterRange:wholeRange]; + [self->textView.layoutManager invalidateLayoutForCharacterRange:wholeRange actualCharacterRange:&actualRange]; + [self->textView.layoutManager ensureLayoutForCharacterRange:actualRange]; + [self->textView.layoutManager invalidateDisplayForCharacterRange:wholeRange]; }); } - (void)didMoveToWindow { [super didMoveToWindow]; - [self scheduleRelayoutIfNeeded]; + // used to run all lifecycle callbacks + [self anyTextMayHaveBeenModified]; } // MARK: - UITextView delegate methods @@ -1200,9 +1210,6 @@ - (void)textViewDidChangeSelection:(UITextView *)textView { // manage selection changes [self manageSelectionBasedChanges]; - - // update active styles - [self tryUpdatingActiveStyles]; } // this function isn't called always when some text changes (for example setting link or starting mention with indicator doesn't fire it)