Idea needed: How to make viewmodel state - drag&drop in compose list that can be edited

34 views Asked by At

I have a very simple list on the screen. Data goes from viewmodel state from database as observable flow (very ordinary case) List item is very simple: it has id, name, order_ (the order of item in the list). So it ordered by order_ column in the state.

@Immutable
data class CategoryState(
    val id: Long,
    val name: String,
    val order_: Int,
    val default_: Boolean = false,
    val subscriptionsCount: Int = 0
)

The list can be edited: when user clicks on the item button Delete the item is deleted, and user can press FAB Add so the item will be added to the list. Also, list can became long and should be scrollable.

val uiState: StateFlow<CategoriesUiState> =
        combine(subsPreferences, categoryRepository.getEditableCategoriesStream(), _key) { subsPreferences, categories, key ->
            if (categories.isNotEmpty()) {
                CategoriesUiState.Categories(
                    categoryItems = categories.map { CategoryState.fromCategory(it) }.toImmutableList(),
                    isFeatureEnabled = subsPreferences.enableCategoriesFeature,
                )
            } else {
                CategoriesUiState.Empty(isFeatureEnabled = subsPreferences.enableCategoriesFeature)
            }
        }.stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5_000),
            initialValue = CategoriesUiState.Loading
        )

In this case my state works perfect. User change the list, I call method add-delete from viewmodel(UI event) to the repository, database changes data, and flow observer gets my actual data to viewmodel and UI updated.

override fun onDeleteConfirmed() {
_uiDeleteCategoryConfirmationDialogState.value.category?.let {
    viewModelScope.launch {
        try {
            categoryRepository.safetyDeleteCategoryById(it.id)
            _key.update { _key.value + 1 }
        } catch (e: DeleteEntityException) {
            processImpossibleToDeleteCategoryException(e)
        }
    }
    onDismissConfirmationDialog()
}
}

When I add the feature drag&drop the list became editable this way. To swap items in database I should replace order_ in fromItem and toItem when user is dragging the views.

 override fun onPositionChanged(categoryIndexFrom: Int, categoryIndexTo: Int) {
        val state = uiState.value
        if (state is CategoriesUiState.Categories) {
            viewModelScope.launch {
                try {
                    categoryRepository.swapCategories(
                        categoryOrderFrom = state.categoryItems[categoryIndexFrom].order_,
                        categoryOrderTo = state.categoryItems[categoryIndexTo].order_
                    )
                } catch (e: IndexOutOfBoundsException) {

                }
            }
        }
    }

My question is How state should be done right? If I will use the same flow from UI to repository - the UI will be very laggy. If I will have some static list (I can get data ones, in the init block for example) and edit this list and pass the edit command to repository in parallel. So, I will have 2 sources of truth (state list in the viewmodel and actual data in the repo) but UI will not be so laggy. Also, the UI changes during state changes now, this is not right. I think I should somehow swap items in the UI screen layer. and then trigger event that list was changed in parallel.

0

There are 0 answers