From b32cfa5174c5b90c15218c44a0e76fbbc6b27790 Mon Sep 17 00:00:00 2001 From: Dima Date: Thu, 1 Aug 2024 20:12:11 +0900 Subject: [PATCH] Fix for "This item has been changed remotely. It will now reload." dialog appearing on ItemDetailsScreen not only when item was changed remotely, but also every time user changed any field on the device. Adding 500 ms debounce to Abstract text field, to reduce number of DB Request when user is inputting data. Upping versionCode to 86 --- .../itemdetails/ItemDetailsEditScreen.kt | 4 +- .../itemdetails/ItemDetailsViewModel.kt | 54 ++++++++++++++++--- .../itemdetails/ItemDetailsViewScreen.kt | 2 +- buildSrc/src/main/kotlin/BuildConfig.kt | 2 +- 4 files changed, 51 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/zotero/android/screens/itemdetails/ItemDetailsEditScreen.kt b/app/src/main/java/org/zotero/android/screens/itemdetails/ItemDetailsEditScreen.kt index 1620e266..ee2285fd 100644 --- a/app/src/main/java/org/zotero/android/screens/itemdetails/ItemDetailsEditScreen.kt +++ b/app/src/main/java/org/zotero/android/screens/itemdetails/ItemDetailsEditScreen.kt @@ -104,8 +104,8 @@ internal fun ItemDetailsEditScreen( ) if (!viewState.data.isAttachment) { EditAbstractRow( - detailValue = viewState.data.abstract ?: "", - onValueChange = viewModel::onAbstractEdit + detailValue = viewState.abstractText, + onValueChange = viewModel::onAbstractTextChange ) } } diff --git a/app/src/main/java/org/zotero/android/screens/itemdetails/ItemDetailsViewModel.kt b/app/src/main/java/org/zotero/android/screens/itemdetails/ItemDetailsViewModel.kt index 3cada7a6..55259435 100644 --- a/app/src/main/java/org/zotero/android/screens/itemdetails/ItemDetailsViewModel.kt +++ b/app/src/main/java/org/zotero/android/screens/itemdetails/ItemDetailsViewModel.kt @@ -39,7 +39,6 @@ import org.zotero.android.database.objects.Attachment import org.zotero.android.database.objects.FieldKeys import org.zotero.android.database.objects.ItemTypes import org.zotero.android.database.objects.RItem -import org.zotero.android.database.objects.UpdatableChangeType import org.zotero.android.database.requests.CancelParentCreationDbRequest import org.zotero.android.database.requests.CreateAttachmentsDbRequest import org.zotero.android.database.requests.CreateItemFromDetailDbRequest @@ -208,6 +207,7 @@ class ItemDetailsViewModel @Inject constructor( EventBus.getDefault().register(this) setupFileObservers() setupOnFieldValueTextChangeFlow() + setupOnAbstractTextChangeFlow() val args = ScreenArguments.itemDetailsArgs @@ -292,7 +292,8 @@ class ItemDetailsViewModel @Inject constructor( type = type, userId = userId, library = library, - preScrolledChildKey = preScrolledChildKey + preScrolledChildKey = preScrolledChildKey, + abstractText = viewState.data.abstract ?: "" ) } conflictResolutionUseCase.currentlyDisplayedItemLibraryIdentifier = viewState.library?.identifier @@ -316,6 +317,7 @@ class ItemDetailsViewModel @Inject constructor( snapshot = updatedStateData, data = updatedData, isEditing = true, + abstractText = updatedData.abstract ?: "" ) } } @@ -471,7 +473,10 @@ class ItemDetailsViewModel @Inject constructor( isEditing: Boolean, ) { updateState { - copy(data = data) + copy( + data = data, + abstractText = data.abstract ?: "" + ) } if (viewState.snapshot != null || isEditing) { val updatedSnapshot = data.deepCopy( @@ -508,10 +513,17 @@ class ItemDetailsViewModel @Inject constructor( } } + private var ignoreScreenRefreshOnNextDbUpdate: Boolean = false + private fun shouldReloadData(item: RItem, changes: Array): Boolean { if (changes.contains("version")) { - //Use old value? - if (changes.contains("changeType") && item.changeType != UpdatableChangeType.user.name) { + if (changes.contains("changeType")) { + //Unfortunately there is no way on Android's RealmDB to get the previous value of RealmObject's 'changeType' field in it's ChangeListener. + // That's why we have to adjust shouldReloadData logic to ignore user's input during DB Refresh with this flag. + if (ignoreScreenRefreshOnNextDbUpdate) { + ignoreScreenRefreshOnNextDbUpdate = false + return false + } return true } return false @@ -622,6 +634,29 @@ class ItemDetailsViewModel @Inject constructor( } } + private var onAbstractTextChangeFlow = MutableStateFlow(null) + + private fun setupOnAbstractTextChangeFlow() { + onAbstractTextChangeFlow + .debounce(500) + .map { data -> + if (data != null) { + onAbstractEdit(data) + } + } + .launchIn(viewModelScope) + } + + fun onAbstractTextChange(newAbstract: String) { + updateState { + copy( + abstractText = newAbstract + ) + } + onAbstractTextChangeFlow.tryEmit(newAbstract) + } + + private var onFieldValueTextChangeFlow = MutableStateFlow?>(null) private fun setupOnFieldValueTextChangeFlow() { @@ -661,6 +696,7 @@ class ItemDetailsViewModel @Inject constructor( if (field == null) { return } + ignoreScreenRefreshOnNextDbUpdate = true field.value = value field.isTappable = ItemDetailDataCreator.isTappable( @@ -740,7 +776,8 @@ class ItemDetailsViewModel @Inject constructor( } } - fun onAbstractEdit(newAbstract: String) { + private fun onAbstractEdit(newAbstract: String) { + ignoreScreenRefreshOnNextDbUpdate = true val updatedData = viewState.data.deepCopy(abstract = newAbstract) updateState { copy(data = updatedData) @@ -801,6 +838,7 @@ class ItemDetailsViewModel @Inject constructor( } private fun endEditing() { + ignoreScreenRefreshOnNextDbUpdate = false if (viewState.snapshot == viewState.data) { return } @@ -878,7 +916,8 @@ class ItemDetailsViewModel @Inject constructor( snapshot = null, isEditing = false, type = DetailType.preview(viewState.key), - data = updatedData + data = updatedData, + abstractText = updatedData.abstract ?: "" ) } } @@ -2036,6 +2075,7 @@ data class ItemDetailsViewState( val longPressOptionsHolder: LongPressOptionsHolder? = null, val fieldFocusKey: String? = null, val fieldFocusText: String = "", + val abstractText: String = "", ) : ViewState sealed class ItemDetailsViewEffect : ViewEffect { diff --git a/app/src/main/java/org/zotero/android/screens/itemdetails/ItemDetailsViewScreen.kt b/app/src/main/java/org/zotero/android/screens/itemdetails/ItemDetailsViewScreen.kt index d7fe3e24..29fb599f 100644 --- a/app/src/main/java/org/zotero/android/screens/itemdetails/ItemDetailsViewScreen.kt +++ b/app/src/main/java/org/zotero/android/screens/itemdetails/ItemDetailsViewScreen.kt @@ -62,7 +62,7 @@ internal fun ItemDetailsViewScreen( showDivider = false ) - if (!viewState.data.isAttachment) { + if (!viewState.data.isAttachment && !viewState.data.abstract.isNullOrBlank()) { CustomDivider( modifier = Modifier .padding(top = 4.dp) diff --git a/buildSrc/src/main/kotlin/BuildConfig.kt b/buildSrc/src/main/kotlin/BuildConfig.kt index ba525948..614668fe 100644 --- a/buildSrc/src/main/kotlin/BuildConfig.kt +++ b/buildSrc/src/main/kotlin/BuildConfig.kt @@ -4,7 +4,7 @@ object BuildConfig { const val compileSdkVersion = 34 const val targetSdk = 34 - val versionCode = 85 // Must be updated on every build + val versionCode = 86 // Must be updated on every build val version = Version( major = 1, minor = 0,