Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ class EnrichedTextInputView : AppCompatEditText {
var spanWatcher: EnrichedSpanWatcher? = null
var layoutManager: EnrichedTextInputViewLayoutManager = EnrichedTextInputViewLayoutManager(this)

var experimentalSynchronousEvents: Boolean = false

var fontSize: Float? = null
private var autoFocus = false
private var typefaceDirty = false
Expand Down Expand Up @@ -147,9 +149,9 @@ class EnrichedTextInputView : AppCompatEditText {
val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(context, id)

if (focused) {
dispatcher?.dispatchEvent(OnInputFocusEvent(surfaceId, id))
dispatcher?.dispatchEvent(OnInputFocusEvent(surfaceId, id, experimentalSynchronousEvents))
} else {
dispatcher?.dispatchEvent(OnInputBlurEvent(surfaceId, id))
dispatcher?.dispatchEvent(OnInputBlurEvent(surfaceId, id, experimentalSynchronousEvents))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,13 @@ class EnrichedTextInputViewManager : SimpleViewManager<EnrichedTextInputView>(),
view?.setAutoCapitalize(flag)
}

override fun setAndroidExperimentalSynchronousEvents(
view: EnrichedTextInputView?,
value: Boolean
) {
view?.experimentalSynchronousEvents = value
}

override fun focus(view: EnrichedTextInputView?) {
view?.requestFocusProgrammatically()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ class MentionHandler(private val view: EnrichedTextInputView) {
val context = view.context as ReactContext
val surfaceId = UIManagerHelper.getSurfaceId(context)
val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(context, view.id)
dispatcher?.dispatchEvent(OnMentionEvent(surfaceId, view.id, indicator, text))
dispatcher?.dispatchEvent(OnMentionEvent(
surfaceId,
view.id,
indicator,
text,
view.experimentalSynchronousEvents,
))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap
import com.facebook.react.uimanager.events.Event

class OnChangeHtmlEvent(surfaceId: Int, viewId: Int, private val html: String) :
class OnChangeHtmlEvent(surfaceId: Int, viewId: Int, private val html: String, private val experimentalSynchronousEvents: Boolean) :
Event<OnChangeHtmlEvent>(surfaceId, viewId) {

override fun getEventName(): String {
Expand All @@ -18,6 +18,10 @@ class OnChangeHtmlEvent(surfaceId: Int, viewId: Int, private val html: String) :
return eventData
}

override fun experimental_isSynchronous(): Boolean {
return experimentalSynchronousEvents
}

companion object {
const val EVENT_NAME: String = "onChangeHtml"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap
import com.facebook.react.uimanager.events.Event

class OnChangeSelectionEvent(surfaceId: Int, viewId: Int, private val text: String, private val start: Int, private val end: Int) :
class OnChangeSelectionEvent(surfaceId: Int, viewId: Int, private val text: String, private val start: Int, private val end: Int, private val experimentalSynchronousEvents: Boolean) :
Event<OnChangeSelectionEvent>(surfaceId, viewId) {

override fun getEventName(): String {
Expand All @@ -19,6 +19,10 @@ class OnChangeSelectionEvent(surfaceId: Int, viewId: Int, private val text: Stri
return eventData
}

override fun experimental_isSynchronous(): Boolean {
return experimentalSynchronousEvents
}

companion object {
const val EVENT_NAME: String = "onChangeSelection"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.swmansion.enriched.events
import com.facebook.react.bridge.WritableMap
import com.facebook.react.uimanager.events.Event

class OnChangeStateEvent(surfaceId: Int, viewId: Int, private val state: WritableMap) :
class OnChangeStateEvent(surfaceId: Int, viewId: Int, private val state: WritableMap, private val experimentalSynchronousEvents: Boolean) :
Event<OnChangeStateEvent>(surfaceId, viewId) {

override fun getEventName(): String {
Expand All @@ -14,6 +14,10 @@ class OnChangeStateEvent(surfaceId: Int, viewId: Int, private val state: Writabl
return state
}

override fun experimental_isSynchronous(): Boolean {
return experimentalSynchronousEvents
}

companion object {
const val EVENT_NAME: String = "onChangeState"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap
import com.facebook.react.uimanager.events.Event

class OnChangeTextEvent(surfaceId: Int, viewId: Int, private val editable: Editable) :
class OnChangeTextEvent(surfaceId: Int, viewId: Int, private val editable: Editable, private val experimentalSynchronousEvents: Boolean) :
Event<OnChangeTextEvent>(surfaceId, viewId) {

override fun getEventName(): String {
Expand All @@ -20,6 +20,10 @@ class OnChangeTextEvent(surfaceId: Int, viewId: Int, private val editable: Edita
return eventData
}

override fun experimental_isSynchronous(): Boolean {
return experimentalSynchronousEvents
}

companion object {
const val EVENT_NAME: String = "onChangeText"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap
import com.facebook.react.uimanager.events.Event

class OnInputBlurEvent(surfaceId: Int, viewId: Int) :
class OnInputBlurEvent(surfaceId: Int, viewId: Int, private val experimentalSynchronousEvents: Boolean) :
Event<OnInputBlurEvent>(surfaceId, viewId) {

override fun getEventName(): String {
Expand All @@ -17,6 +17,10 @@ class OnInputBlurEvent(surfaceId: Int, viewId: Int) :
return eventData
}

override fun experimental_isSynchronous(): Boolean {
return experimentalSynchronousEvents
}

companion object {
const val EVENT_NAME: String = "onInputBlur"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap
import com.facebook.react.uimanager.events.Event

class OnInputFocusEvent(surfaceId: Int, viewId: Int) :
class OnInputFocusEvent(surfaceId: Int, viewId: Int, private val experimentalSynchronousEvents: Boolean) :
Event<OnInputFocusEvent>(surfaceId, viewId) {

override fun getEventName(): String {
Expand All @@ -17,6 +17,10 @@ class OnInputFocusEvent(surfaceId: Int, viewId: Int) :
return eventData
}

override fun experimental_isSynchronous(): Boolean {
return experimentalSynchronousEvents
}

companion object {
const val EVENT_NAME: String = "onInputFocus"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap
import com.facebook.react.uimanager.events.Event

class OnLinkDetectedEvent(surfaceId: Int, viewId: Int, private val text: String, private val url: String, private val start: Int, private val end: Int) :
class OnLinkDetectedEvent(surfaceId: Int, viewId: Int, private val text: String, private val url: String, private val start: Int, private val end: Int, private val experimentalSynchronousEvents: Boolean) :
Event<OnLinkDetectedEvent>(surfaceId, viewId) {

override fun getEventName(): String {
Expand All @@ -20,6 +20,10 @@ class OnLinkDetectedEvent(surfaceId: Int, viewId: Int, private val text: String,
return eventData
}

override fun experimental_isSynchronous(): Boolean {
return experimentalSynchronousEvents
}

companion object {
const val EVENT_NAME: String = "onLinkDetected"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap
import com.facebook.react.uimanager.events.Event

class OnMentionDetectedEvent(surfaceId: Int, viewId: Int, private val text: String, private val indicator: String, private val payload: String) :
class OnMentionDetectedEvent(surfaceId: Int, viewId: Int, private val text: String, private val indicator: String, private val payload: String, private val experimentalSynchronousEvents: Boolean) :
Event<OnMentionDetectedEvent>(surfaceId, viewId) {

override fun getEventName(): String {
Expand All @@ -19,6 +19,10 @@ class OnMentionDetectedEvent(surfaceId: Int, viewId: Int, private val text: Stri
return eventData
}

override fun experimental_isSynchronous(): Boolean {
return experimentalSynchronousEvents
}

companion object {
const val EVENT_NAME: String = "onMentionDetected"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap
import com.facebook.react.uimanager.events.Event

class OnMentionEvent(surfaceId: Int, viewId: Int, private val indicator: String, private val text: String?) : Event<OnMentionEvent>(surfaceId, viewId) {
class OnMentionEvent(surfaceId: Int, viewId: Int, private val indicator: String, private val text: String?, private val experimentalSynchronousEvents: Boolean) : Event<OnMentionEvent>(surfaceId, viewId) {

override fun getEventName(): String {
return EVENT_NAME
Expand All @@ -23,6 +23,10 @@ class OnMentionEvent(surfaceId: Int, viewId: Int, private val indicator: String,
return eventData
}

override fun experimental_isSynchronous(): Boolean {
return experimentalSynchronousEvents
}

companion object {
const val EVENT_NAME: String = "onMention"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,14 @@ class EnrichedSelection(private val view: EnrichedTextInputView) {
val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(context, view.id)

val text = editable.substring(start, end)
dispatcher?.dispatchEvent(OnChangeSelectionEvent(surfaceId, view.id, text, start ,end))
dispatcher?.dispatchEvent(OnChangeSelectionEvent(
surfaceId,
view.id,
text,
start ,
end,
view.experimentalSynchronousEvents,
))
}

private fun emitLinkDetectedEvent(spannable: Spannable, span: EnrichedLinkSpan?, start: Int, end: Int) {
Expand All @@ -236,7 +243,15 @@ class EnrichedSelection(private val view: EnrichedTextInputView) {
val context = view.context as ReactContext
val surfaceId = UIManagerHelper.getSurfaceId(context)
val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(context, view.id)
dispatcher?.dispatchEvent(OnLinkDetectedEvent(surfaceId, view.id, text, url, start, end))
dispatcher?.dispatchEvent(OnLinkDetectedEvent(
surfaceId,
view.id,
text,
url,
start,
end,
view.experimentalSynchronousEvents,
))
}

private fun emitMentionDetectedEvent(spannable: Spannable, span: EnrichedMentionSpan?, start: Int, end: Int) {
Expand All @@ -258,6 +273,13 @@ class EnrichedSelection(private val view: EnrichedTextInputView) {
val context = view.context as ReactContext
val surfaceId = UIManagerHelper.getSurfaceId(context)
val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(context, view.id)
dispatcher?.dispatchEvent(OnMentionDetectedEvent(surfaceId, view.id, text, indicator, payload))
dispatcher?.dispatchEvent(OnMentionDetectedEvent(
surfaceId,
view.id,
text,
indicator,
payload,
view.experimentalSynchronousEvents,
))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,12 @@ class EnrichedSpanState(private val view: EnrichedTextInputView) {
val context = view.context as ReactContext
val surfaceId = UIManagerHelper.getSurfaceId(context)
val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(context, view.id)
dispatcher?.dispatchEvent(OnChangeStateEvent(surfaceId, view.id, payload))
dispatcher?.dispatchEvent(OnChangeStateEvent(
surfaceId,
view.id,
payload,
view.experimentalSynchronousEvents,
))
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ class EnrichedSpanWatcher(private val view: EnrichedTextInputView) : SpanWatcher
val context = view.context as ReactContext
val surfaceId = UIManagerHelper.getSurfaceId(context)
val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(context, view.id)
dispatcher?.dispatchEvent(OnChangeHtmlEvent(surfaceId, view.id, html))
dispatcher?.dispatchEvent(OnChangeHtmlEvent(
surfaceId,
view.id,
html,
view.experimentalSynchronousEvents,
))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ class EnrichedTextWatcher(private val view: EnrichedTextInputView) : TextWatcher
val context = view.context as ReactContext
val surfaceId = UIManagerHelper.getSurfaceId(context)
val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(context, view.id)
dispatcher?.dispatchEvent(OnChangeTextEvent(surfaceId, view.id, s))
dispatcher?.dispatchEvent(OnChangeTextEvent(
surfaceId,
view.id,
s,
view.experimentalSynchronousEvents,
))
view.spanWatcher?.emitEvent(s, null)
}
}
7 changes: 7 additions & 0 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ const DEFAULT_LINK_STATE = {

const DEBUG_SCROLLABLE = false;

// Enabling this prop fixes input flickering while auto growing.
// However, it's still experimental and not tested well.
const ANDROID_EXPERIMENTAL_SYNCHRONOUS_EVENTS = true;

export default function App() {
const [isChannelPopupOpen, setIsChannelPopupOpen] = useState(false);
const [isUserPopupOpen, setIsUserPopupOpen] = useState(false);
Expand Down Expand Up @@ -240,6 +244,9 @@ export default function App() {
onFocus={handleFocusEvent}
onBlur={handleBlurEvent}
onChangeSelection={handleSelectionChangeEvent}
androidExperimentalSynchronousEvents={
ANDROID_EXPERIMENTAL_SYNCHRONOUS_EVENTS
}
/>
<Toolbar
stylesState={stylesState}
Expand Down
12 changes: 12 additions & 0 deletions src/EnrichedTextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,14 @@ export interface EnrichedTextInputProps extends Omit<ViewProps, 'children'> {
onChangeMention?: (e: OnChangeMentionEvent) => void;
onEndMention?: (indicator: string) => void;
onChangeSelection?: (e: NativeSyntheticEvent<OnChangeSelectionEvent>) => void;
/**
* If true, Android will use experimental synchronous events.
* This will prevent from input flickering when updating component size.
* However, this is an experimental feature, which has not been thoroughly tested.
* We may decide to enable it by default in a future release.
* Disabled by default.
*/
androidExperimentalSynchronousEvents?: boolean;
}

const nullthrows = <T,>(value: T | null | undefined): T => {
Expand Down Expand Up @@ -183,6 +191,7 @@ export const EnrichedTextInput = ({
onChangeMention,
onEndMention,
onChangeSelection,
androidExperimentalSynchronousEvents = false,
...rest
}: EnrichedTextInputProps) => {
const nativeRef = useRef<ComponentType | null>(null);
Expand Down Expand Up @@ -340,6 +349,9 @@ export const EnrichedTextInput = ({
onMentionDetected={handleMentionDetected}
onMention={handleMentionEvent}
onChangeSelection={onChangeSelection}
androidExperimentalSynchronousEvents={
androidExperimentalSynchronousEvents
}
{...rest}
/>
);
Expand Down
3 changes: 3 additions & 0 deletions src/EnrichedTextInputNativeComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ export interface NativeProps extends ViewProps {

// Used for onChangeHtml event performance optimization
isOnChangeHtmlSet: boolean;

// Experimental
androidExperimentalSynchronousEvents: boolean;
}

type ComponentType = HostComponent<NativeProps>;
Expand Down
Loading