From 3cf531ac06e1ebf80bc7b3e4680016ac442fb452 Mon Sep 17 00:00:00 2001 From: Gibran Chevalley Date: Thu, 11 Jul 2024 10:44:49 +0200 Subject: [PATCH] Refactor isEditorEmpty to start emitting a value as soon a possible and have a default value of null --- .../src/main/assets/attach_listeners.js | 5 +++-- .../src/main/assets/define_listeners.js | 21 +++++++++---------- .../infomaniak/lib/richhtmleditor/JsBridge.kt | 4 ++-- .../richhtmleditor/RichHtmlEditorWebView.kt | 8 ++++++- .../sample/EditorSampleFragment.kt | 2 ++ .../res/layout/fragment_editor_sample.xml | 4 +++- 6 files changed, 27 insertions(+), 17 deletions(-) diff --git a/rich-html-editor/src/main/assets/attach_listeners.js b/rich-html-editor/src/main/assets/attach_listeners.js index b5ff520..97441a1 100644 --- a/rich-html-editor/src/main/assets/attach_listeners.js +++ b/rich-html-editor/src/main/assets/attach_listeners.js @@ -8,6 +8,7 @@ document.addEventListener("selectionchange", () => { focusCursorOnScreen() }) -onEmptyBodyChanges(isEditorEmpty => { - window.editor.onEmptyBodyChanges(isEditorEmpty) +reportEmptyBodyStatus() +onEditorChildListChange(() => { + reportEmptyBodyStatus() }) diff --git a/rich-html-editor/src/main/assets/define_listeners.js b/rich-html-editor/src/main/assets/define_listeners.js index 5c98ac2..8afbca8 100644 --- a/rich-html-editor/src/main/assets/define_listeners.js +++ b/rich-html-editor/src/main/assets/define_listeners.js @@ -22,18 +22,10 @@ function getSelectionRangeOrNull() { return (selection.rangeCount > 0) ? selection.getRangeAt(0) : null } -function onEmptyBodyChanges(callback) { +function onEditorChildListChange(callback) { let config = { childList: true } - let editor = getEditor() - - var observer = new MutationObserver(_ => { - var isEditorEmpty = editor.childNodes.length === 0 - if (previousEmptyStat === isEditorEmpty) return - previousEmptyStat = isEditorEmpty - callback(isEditorEmpty) - }) - - observer.observe(editor, config) + var observer = new MutationObserver(callback) + observer.observe(getEditor(), config) } // Core logic @@ -144,3 +136,10 @@ function updateWebViewHeightWithBodyHeight() { window.editor.reportNewDocumentHeight((documentElement.offsetHeight + paddingTop + paddingBottom) * window.devicePixelRatio) } + +function reportEmptyBodyStatus() { + var isEditorEmpty = getEditor().childNodes.length === 0 + if (previousEmptyStat === isEditorEmpty) return + previousEmptyStat = isEditorEmpty + window.editor.onEmptyBodyChanges(isEditorEmpty) +} diff --git a/rich-html-editor/src/main/java/com/infomaniak/lib/richhtmleditor/JsBridge.kt b/rich-html-editor/src/main/java/com/infomaniak/lib/richhtmleditor/JsBridge.kt index 0a1a22f..3c6ada4 100644 --- a/rich-html-editor/src/main/java/com/infomaniak/lib/richhtmleditor/JsBridge.kt +++ b/rich-html-editor/src/main/java/com/infomaniak/lib/richhtmleditor/JsBridge.kt @@ -53,8 +53,8 @@ internal class JsBridge( ) val editorStatusesFlow: SharedFlow = _editorStatusesFlow.asSharedFlow() - private var _isEmptyFlow: MutableStateFlow = MutableStateFlow(true) - var isEmptyFlow: StateFlow = _isEmptyFlow.asStateFlow() + private var _isEmptyFlow: MutableStateFlow = MutableStateFlow(null) + var isEmptyFlow: StateFlow = _isEmptyFlow.asStateFlow() fun toggleBold() = execCommand(StatusCommand.BOLD) diff --git a/rich-html-editor/src/main/java/com/infomaniak/lib/richhtmleditor/RichHtmlEditorWebView.kt b/rich-html-editor/src/main/java/com/infomaniak/lib/richhtmleditor/RichHtmlEditorWebView.kt index fecc82f..4c439dd 100644 --- a/rich-html-editor/src/main/java/com/infomaniak/lib/richhtmleditor/RichHtmlEditorWebView.kt +++ b/rich-html-editor/src/main/java/com/infomaniak/lib/richhtmleditor/RichHtmlEditorWebView.kt @@ -114,8 +114,14 @@ class RichHtmlEditorWebView @JvmOverloads constructor( * * With some user inputs, the editor could visually look empty but still have a
or other tags inside it. In this case * [isEmptyFlow] will still return false, so this value might not be suited for all usages. + * + * The flow is initialized to the value `null` until it receives its very first value from the javascript observer and won't + * ever become `null` again. + * + * If the flow emits a new value it will always be different than the previous value, i.e. it's already distinctUntilChanged + * by nature. */ - val isEmptyFlow: StateFlow by jsBridge::isEmptyFlow + val isEmptyFlow: StateFlow by jsBridge::isEmptyFlow private var htmlExportCallback: MutableList<((html: String) -> Unit)> = mutableListOf() diff --git a/sample/src/main/java/com/infomaniak/lib/richhtmleditor/sample/EditorSampleFragment.kt b/sample/src/main/java/com/infomaniak/lib/richhtmleditor/sample/EditorSampleFragment.kt index 324ec06..a7da345 100644 --- a/sample/src/main/java/com/infomaniak/lib/richhtmleditor/sample/EditorSampleFragment.kt +++ b/sample/src/main/java/com/infomaniak/lib/richhtmleditor/sample/EditorSampleFragment.kt @@ -34,6 +34,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.infomaniak.lib.richhtmleditor.Justification import com.infomaniak.lib.richhtmleditor.sample.databinding.CreateLinkTextInputBinding import com.infomaniak.lib.richhtmleditor.sample.databinding.FragmentEditorSampleBinding +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @@ -68,6 +69,7 @@ class EditorSampleFragment : Fragment() { setOnFocusChangeListener { _, hasFocus -> setToolbarEnabledStatus(hasFocus) } isEmptyFlow + .filterNotNull() .onEach { isEditorEmpty -> placeholder.isVisible = isEditorEmpty } .launchIn(lifecycleScope) } diff --git a/sample/src/main/res/layout/fragment_editor_sample.xml b/sample/src/main/res/layout/fragment_editor_sample.xml index 6668150..c54e139 100644 --- a/sample/src/main/res/layout/fragment_editor_sample.xml +++ b/sample/src/main/res/layout/fragment_editor_sample.xml @@ -362,11 +362,13 @@ android:text="Write here" android:textColor="@color/placeholderColor" android:textSize="16sp" + android:visibility="gone" app:layout_constraintBottom_toBottomOf="@id/webViewLayout" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="@id/webViewLayout" /> + app:layout_constraintTop_toTopOf="@id/webViewLayout" + tools:visibility="visible" />