Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,7 @@ class ParametrizedStyles(
}

val spanEnd = start + text.length
val span = EnrichedInputLinkSpan(url, view.htmlStyle, true)
val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(start, spanEnd)
spannable.setSpan(span, safeStart, safeEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)

applyManualLinkSpan(spannable, start, spanEnd, url)
view.selection?.validateStyles()
isSettingLinkSpan = false
}
Expand All @@ -90,6 +87,7 @@ class ParametrizedStyles(
startCursorPosition: Int,
endCursorPosition: Int,
) {
afterTextChangedManualLinks(startCursorPosition, endCursorPosition)
afterTextChangedLinks(startCursorPosition, endCursorPosition)
afterTextChangedMentions(s, startCursorPosition)
}
Expand Down Expand Up @@ -227,6 +225,53 @@ class ParametrizedStyles(
return true
}

private fun afterTextChangedManualLinks(
editStart: Int,
editEnd: Int,
) {
if (isSettingLinkSpan) return
val spannable = view.text as? Spannable ?: return
if (spannable.subSequence(editStart, editEnd).none { it == '\n' || it == '\r' }) return

spannable
.getSpans(editStart, editEnd, EnrichedInputLinkSpan::class.java)
.filter { it.getIsManual() }
.forEach { splitManualLinkOnNewlines(spannable, it) }
}

private fun splitManualLinkOnNewlines(
spannable: Spannable,
span: EnrichedInputLinkSpan,
) {
val start = spannable.getSpanStart(span)
val end = spannable.getSpanEnd(span)
val url = span.getUrl()
spannable.removeSpan(span)

var runStart = start
for (i in start..end) {
if (i == end || spannable[i] == '\n' || spannable[i] == '\r') {
if (runStart < i) applyManualLinkSpan(spannable, runStart, i, url)
runStart = i + 1
}
}
}

private fun applyManualLinkSpan(
spannable: Spannable,
start: Int,
end: Int,
url: String,
) {
val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(start, end)
spannable.setSpan(
EnrichedInputLinkSpan(url, view.htmlStyle, true),
safeStart,
safeEnd,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE,
)
}

private fun afterTextChangedLinks(
editStart: Int,
editEnd: Int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ class EnrichedSelection(
var start: Int = 0
var end: Int = 0

private var previousLinkDetectedEvent: MutableMap<String, String> = mutableMapOf("text" to "", "url" to "")
private var previousLinkDetectedEvent: MutableMap<String, String> =
mutableMapOf("text" to "", "url" to "", "start" to "", "end" to "")
private var previousMentionDetectedEvent: MutableMap<String, String> = mutableMapOf("text" to "", "payload" to "")

fun onSelection(
Expand Down Expand Up @@ -273,14 +274,24 @@ class EnrichedSelection(
val text = spannable.substring(start, end).replace(EnrichedConstants.ZWS_STRING, "")
val url = span?.getUrl() ?: ""

// Prevents emitting unnecessary events
if (text == previousLinkDetectedEvent["text"] && url == previousLinkDetectedEvent["url"]) return

previousLinkDetectedEvent.put("text", text)
previousLinkDetectedEvent.put("url", url)

val visibleStart = start - spannable.zwsCountBefore(start)
val visibleEnd = end - spannable.zwsCountBefore(end)
val visibleStartString = visibleStart.toString()
val visibleEndString = visibleEnd.toString()

// Prevents emitting unnecessary events
if (text == previousLinkDetectedEvent["text"] &&
url == previousLinkDetectedEvent["url"] &&
visibleStartString == previousLinkDetectedEvent["start"] &&
visibleEndString == previousLinkDetectedEvent["end"]
) {
return
}

previousLinkDetectedEvent["text"] = text
previousLinkDetectedEvent["url"] = url
previousLinkDetectedEvent["start"] = visibleStartString
previousLinkDetectedEvent["end"] = visibleEndString

val context = view.context as ReactContext
val surfaceId = UIManagerHelper.getSurfaceId(context)
Expand Down
6 changes: 5 additions & 1 deletion apps/example/src/hooks/useEditorState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,11 @@ export function useEditorState() {
const [isImageModalOpen, setIsImageModalOpen] = useState(false);
const [isValueModalOpen, setIsValueModalOpen] = useState(false);
const [currentHtml, setCurrentHtml] = useState('');
const [selection, setSelection] = useState<Selection>();
const [selection, setSelection] = useState<Selection>({
start: 0,
end: 0,
text: '',
});
const [stylesState, setStylesState] = useState<StylesState>(DEFAULT_STYLES);
const [currentLink, setCurrentLink] =
useState<CurrentLinkState>(DEFAULT_LINK_STATE);
Expand Down
12 changes: 7 additions & 5 deletions ios/EnrichedTextInputView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1611,8 +1611,11 @@ - (void)manageSelectionBasedChanges {
[attributesManager
manageTypingAttributesWithOnlySelection:onlySelectionChanged];

// always update active styles
[self tryUpdatingActiveStyles];
// When text changed, anyTextMayHaveBeenModified runs tryUpdatingActiveStyles
if ([_recentInputString isEqualToString:currentString]) {
// update active styles
[self tryUpdatingActiveStyles];
}
Comment thread
kacperzolkiewski marked this conversation as resolved.
}

- (void)handleWordModificationBasedChanges:(NSString *)word
Expand Down Expand Up @@ -1689,12 +1692,11 @@ - (void)anyTextMayHaveBeenModified {
}

if (![textView.textStorage.string isEqualToString:_recentInputString]) {
_recentInputString = [textView.textStorage.string copy];

// emit onChangeText event
auto emitter = [self getEventEmitter];
if (emitter != nullptr && _emitTextChange) {
// set the recent input string only if the emitter is defined
_recentInputString = [textView.textStorage.string copy];

// emit string without zero width spaces
NSString *stringToBeEmitted = [[textView.textStorage.string
stringByReplacingOccurrencesOfString:@"\u200B"
Expand Down
5 changes: 4 additions & 1 deletion ios/styles/LinkStyle.mm
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,10 @@ - (void)handleManualLinks:(NSString *)word inRange:(NSRange)wordRange {
if ([manualLinkMinValue isEqualToLinkData:manualLinkMaxValue]) {
NSRange newRange =
NSMakeRange(manualLinkMinIdx, manualLinkMaxIdx - manualLinkMinIdx + 1);
[self applyLinkMetaWithData:manualLinkMinValue range:newRange];
LinkData *updatedData = [manualLinkMinValue copy];
updatedData.text =
[self.host.textView.textStorage.string substringWithRange:newRange];
[self applyLinkMetaWithData:updatedData range:newRange];
[self.host.attributesManager addDirtyRange:newRange];
}
}
Expand Down
Loading