diff --git a/app/build.gradle b/app/build.gradle index fd4388643..03e1f5109 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -101,7 +101,7 @@ dependencies { implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0' implementation 'com.tomclaw.cache:cache:1.0' - debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.5' + debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.6' testImplementation 'junit:junit:4.13.1' testImplementation 'org.json:json:20201115' diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt index 7eb584a18..a131d35c7 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsViewModel.kt @@ -77,7 +77,7 @@ class DetailsViewModel( } ) } - }.flowOn(Dispatchers.Default).asLiveData(viewModelScope.coroutineContext) + }.asLiveData(viewModelScope.coroutineContext + Dispatchers.Default) init { launchLoadingJob(Dispatchers.Default) { diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesContainerFragment.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesContainerFragment.kt index 45f637c1b..8a8b740c2 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesContainerFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesContainerFragment.kt @@ -1,7 +1,6 @@ package org.koitharu.kotatsu.favourites.ui import android.os.Bundle -import android.os.Parcelable import android.view.* import androidx.core.graphics.Insets import androidx.core.view.updatePadding @@ -27,7 +26,6 @@ class FavouritesContainerFragment : BaseFragment(), private val editDelegate by lazy(LazyThreadSafetyMode.NONE) { CategoriesEditDelegate(requireContext(), this) } - private var adapterState: Parcelable? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -49,25 +47,6 @@ class FavouritesContainerFragment : BaseFragment(), viewModel.onError.observe(viewLifecycleOwner, ::onError) } - override fun onViewStateRestored(savedInstanceState: Bundle?) { - super.onViewStateRestored(savedInstanceState) - // (savedInstanceState?.getParcelable(KEY_ADAPTER_STATE) ?: adapterState)?.let { - // (binding.pager.adapter as FavouritesPagerAdapter).restoreState(it) - // } - } - - override fun onDestroyView() { - adapterState = (binding.pager.adapter as? FavouritesPagerAdapter)?.saveState() - super.onDestroyView() - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - adapterState = (bindingOrNull()?.pager?.adapter as? FavouritesPagerAdapter)?.saveState() - ?: adapterState - outState.putParcelable(KEY_ADAPTER_STATE, adapterState) - } - override fun onWindowInsetsChanged(insets: Insets) { binding.tabs.updatePadding( left = insets.left, @@ -132,8 +111,6 @@ class FavouritesContainerFragment : BaseFragment(), companion object { - private const val KEY_ADAPTER_STATE = "adapter_state" - fun newInstance() = FavouritesContainerFragment() } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt index 79e19fa58..428bf4864 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt @@ -1,12 +1,11 @@ package org.koitharu.kotatsu.favourites.ui.categories -import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.flowOn import org.koitharu.kotatsu.base.ui.BaseViewModel import org.koitharu.kotatsu.favourites.domain.FavouritesRepository +import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct class FavouritesCategoriesViewModel( private val repository: FavouritesRepository @@ -15,7 +14,7 @@ class FavouritesCategoriesViewModel( private var reorderJob: Job? = null val categories = repository.observeCategories() - .flowOn(Dispatchers.Default).asLiveData(viewModelScope.coroutineContext) + .asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default) fun createCategory(name: String) { launchJob(Dispatchers.Default) { diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/MangaCategoriesViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/MangaCategoriesViewModel.kt index 663a8ffdb..3b900f34c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/MangaCategoriesViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/MangaCategoriesViewModel.kt @@ -1,14 +1,13 @@ package org.koitharu.kotatsu.favourites.ui.categories.select -import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.flowOn import org.koitharu.kotatsu.base.ui.BaseViewModel import org.koitharu.kotatsu.core.model.Manga import org.koitharu.kotatsu.favourites.domain.FavouritesRepository import org.koitharu.kotatsu.favourites.ui.categories.select.model.MangaCategoryItem +import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct class MangaCategoriesViewModel( private val manga: Manga, @@ -26,7 +25,7 @@ class MangaCategoriesViewModel( isChecked = it.id in checked ) } - }.flowOn(Dispatchers.Default).asLiveData(viewModelScope.coroutineContext) + }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default) fun setChecked(categoryId: Long, isChecked: Boolean) { launchJob(Dispatchers.Default) { diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt index b42e99a58..b88e37974 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt @@ -4,7 +4,6 @@ import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.flowOn import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.Manga import org.koitharu.kotatsu.core.prefs.AppSettings @@ -14,7 +13,7 @@ import org.koitharu.kotatsu.list.ui.model.EmptyState import org.koitharu.kotatsu.list.ui.model.LoadingState import org.koitharu.kotatsu.list.ui.model.toErrorState import org.koitharu.kotatsu.list.ui.model.toUi -import org.koitharu.kotatsu.utils.ext.asLiveData +import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import org.koitharu.kotatsu.utils.ext.onFirst class FavouritesListViewModel( @@ -43,7 +42,7 @@ class FavouritesListViewModel( isLoading.postValue(false) }.catch { emit(listOf(it.toErrorState(canRetry = false))) - }.flowOn(Dispatchers.Default).asLiveData(viewModelScope.coroutineContext, listOf(LoadingState)) + }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState)) override fun onRefresh() = Unit diff --git a/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt index f49ff0522..081fdc0e0 100644 --- a/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt @@ -15,7 +15,7 @@ import org.koitharu.kotatsu.history.domain.MangaWithHistory import org.koitharu.kotatsu.list.ui.MangaListViewModel import org.koitharu.kotatsu.list.ui.model.* import org.koitharu.kotatsu.utils.SingleLiveEvent -import org.koitharu.kotatsu.utils.ext.asLiveData +import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import org.koitharu.kotatsu.utils.ext.daysDiff import org.koitharu.kotatsu.utils.ext.onFirst import java.util.* @@ -51,7 +51,7 @@ class HistoryListViewModel( isLoading.postValue(false) }.catch { it.toErrorState(canRetry = false) - }.flowOn(Dispatchers.Default).asLiveData(viewModelScope.coroutineContext, listOf(LoadingState)) + }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState)) override fun onRefresh() = Unit diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt index 73ed41739..1e6bfd4c7 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt @@ -50,6 +50,9 @@ abstract class MangaListFragment : BaseFragment(), private var paginationListener: PaginationScrollListener? = null private val spanResolver = MangaListSpanResolver() private val spanSizeLookup = SpanSizeLookup() + private val listCommitCallback = Runnable { + spanSizeLookup.invalidateCache() + } open val isSwipeRefreshEnabled = true protected abstract val viewModel: MangaListViewModel @@ -148,8 +151,7 @@ abstract class MangaListFragment : BaseFragment(), } private fun onListChanged(list: List) { - spanSizeLookup.invalidateCache() - listAdapter?.items = list + listAdapter?.setItems(list, listCommitCallback) } private fun onError(e: Throwable) { @@ -178,7 +180,7 @@ abstract class MangaListFragment : BaseFragment(), @CallSuper protected open fun onLoadingStateChanged(isLoading: Boolean) { - binding.swipeRefreshLayout.isEnabled = + binding.swipeRefreshLayout.isEnabled = binding.swipeRefreshLayout.isRefreshing || isSwipeRefreshEnabled && !isLoading if (!isLoading) { binding.swipeRefreshLayout.isRefreshing = false diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListViewModel.kt index 4e85fff89..0fa365629 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListViewModel.kt @@ -2,7 +2,6 @@ package org.koitharu.kotatsu.list.ui import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.* @@ -10,6 +9,7 @@ import org.koitharu.kotatsu.base.ui.BaseViewModel import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.ListMode import org.koitharu.kotatsu.list.ui.model.ListModel +import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct abstract class MangaListViewModel( private val settings: AppSettings @@ -21,16 +21,20 @@ abstract class MangaListViewModel( val gridScale = settings.observe() .filter { it == AppSettings.KEY_GRID_SIZE } .map { settings.gridSize / 100f } - .onStart { emit(settings.gridSize / 100f) } - .flowOn(Dispatchers.IO) - .asLiveData(viewModelScope.coroutineContext) + .asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.IO) { + settings.gridSize / 100f + } protected fun createListModeFlow() = settings.observe() .filter { it == AppSettings.KEY_LIST_MODE } .map { settings.listMode } .onStart { emit(settings.listMode) } .distinctUntilChanged() - .onEach { listMode.postValue(it) } + .onEach { + if (listMode.value != it) { + listMode.postValue(it) + } + } abstract fun onRefresh() diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt index e57f3e31f..0ca8ecabc 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt @@ -6,6 +6,7 @@ import coil.ImageLoader import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.model.Manga +import org.koitharu.kotatsu.core.ui.DateTimeAgo import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.MangaGridModel import org.koitharu.kotatsu.list.ui.model.MangaListDetailedModel @@ -38,6 +39,10 @@ class MangaListAdapter( .addDelegate(ITEM_TYPE_EMPTY, emptyStateListAD()) } + fun setItems(list: List, commitCallback: Runnable) { + differ.submitList(list, commitCallback) + } + private class DiffCallback : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: ListModel, newItem: ListModel) = when { @@ -50,6 +55,9 @@ class MangaListAdapter( oldItem is MangaGridModel && newItem is MangaGridModel -> { oldItem.id == newItem.id } + oldItem is DateTimeAgo && newItem is DateTimeAgo -> { + oldItem == newItem + } else -> oldItem.javaClass == newItem.javaClass } diff --git a/app/src/main/java/org/koitharu/kotatsu/local/ui/LocalListViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/local/ui/LocalListViewModel.kt index 1910b3e5f..4a3edd294 100644 --- a/app/src/main/java/org/koitharu/kotatsu/local/ui/LocalListViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/local/ui/LocalListViewModel.kt @@ -2,13 +2,10 @@ package org.koitharu.kotatsu.local.ui import android.content.Context import android.net.Uri -import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.withContext import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.UnsupportedFileException @@ -24,6 +21,7 @@ import org.koitharu.kotatsu.list.ui.model.toUi import org.koitharu.kotatsu.local.domain.LocalMangaRepository import org.koitharu.kotatsu.utils.MediaStoreCompat import org.koitharu.kotatsu.utils.SingleLiveEvent +import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import org.koitharu.kotatsu.utils.ext.safe import org.koitharu.kotatsu.utils.ext.sub import java.io.IOException @@ -51,9 +49,7 @@ class LocalListViewModel( list.isEmpty() -> listOf(EmptyState(R.string.text_local_holder)) else -> list.toUi(mode) } - }.onStart { - emit(listOf(LoadingState)) - }.flowOn(Dispatchers.Default).asLiveData(viewModelScope.coroutineContext) + }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState)) init { onRefresh() diff --git a/app/src/main/java/org/koitharu/kotatsu/main/ui/MainViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/main/ui/MainViewModel.kt index 9fdd7888a..5c9f7a15d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/main/ui/MainViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/main/ui/MainViewModel.kt @@ -1,9 +1,10 @@ package org.koitharu.kotatsu.main.ui -import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart import org.koitharu.kotatsu.base.domain.MangaProviderFactory import org.koitharu.kotatsu.base.ui.BaseViewModel import org.koitharu.kotatsu.core.exceptions.EmptyHistoryException @@ -11,6 +12,7 @@ import org.koitharu.kotatsu.core.model.Manga import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.history.domain.HistoryRepository import org.koitharu.kotatsu.utils.SingleLiveEvent +import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct class MainViewModel( private val historyRepository: HistoryRepository, @@ -24,9 +26,7 @@ class MainViewModel( .filter { it == AppSettings.KEY_SOURCES_ORDER || it == AppSettings.KEY_SOURCES_HIDDEN } .onStart { emit("") } .map { MangaProviderFactory.getSources(settings, includeHidden = false) } - .distinctUntilChanged() - .flowOn(Dispatchers.Default) - .asLiveData(viewModelScope.coroutineContext) + .asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default) fun openLastReader() { launchLoadingJob { diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt index d4232e329..e0a3d236c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt @@ -5,7 +5,6 @@ import android.net.Uri import android.util.LongSparseArray import android.webkit.URLUtil import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import kotlinx.coroutines.* import kotlinx.coroutines.flow.* @@ -58,7 +57,7 @@ class ReaderViewModel( chapterNumber = chapter?.number ?: 0, chaptersTotal = chapters.size() ) - }.flowOn(Dispatchers.Default).asLiveData(viewModelScope.coroutineContext) + }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default) val content = MutableLiveData(ReaderContent(emptyList(), null)) val manga: Manga? @@ -68,9 +67,7 @@ class ReaderViewModel( .filter { it == AppSettings.KEY_READER_ANIMATION } .map { settings.readerAnimation } .onStart { emit(settings.readerAnimation) } - .distinctUntilChanged() - .flowOn(Dispatchers.IO) - .asLiveData(viewModelScope.coroutineContext) + .asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.IO) val onZoomChanged = SingleLiveEvent() diff --git a/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt index 93aa14f8e..b36ead35a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt @@ -5,7 +5,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.flowOn import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.Manga @@ -15,7 +14,7 @@ import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.list.ui.MangaFilterConfig import org.koitharu.kotatsu.list.ui.MangaListViewModel import org.koitharu.kotatsu.list.ui.model.* -import org.koitharu.kotatsu.utils.ext.asLiveData +import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import java.util.* class RemoteListViewModel( @@ -49,7 +48,7 @@ class RemoteListViewModel( result } } - }.flowOn(Dispatchers.Default).asLiveData(viewModelScope.coroutineContext, listOf(LoadingState)) + }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState)) init { loadList(false) diff --git a/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchViewModel.kt index 0ed262f74..14b3711a1 100644 --- a/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchViewModel.kt @@ -5,14 +5,13 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.flowOn import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.Manga import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.list.ui.MangaListViewModel import org.koitharu.kotatsu.list.ui.model.* -import org.koitharu.kotatsu.utils.ext.asLiveData +import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import java.util.* class SearchViewModel( @@ -46,7 +45,7 @@ class SearchViewModel( result } } - }.flowOn(Dispatchers.Default).asLiveData(viewModelScope.coroutineContext, listOf(LoadingState)) + }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState)) init { loadList(append = false) diff --git a/app/src/main/java/org/koitharu/kotatsu/search/ui/global/GlobalSearchViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/search/ui/global/GlobalSearchViewModel.kt index d65ffba62..3486d583f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/search/ui/global/GlobalSearchViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/search/ui/global/GlobalSearchViewModel.kt @@ -11,7 +11,7 @@ import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.list.ui.MangaListViewModel import org.koitharu.kotatsu.list.ui.model.* import org.koitharu.kotatsu.search.domain.MangaSearchRepository -import org.koitharu.kotatsu.utils.ext.asLiveData +import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import org.koitharu.kotatsu.utils.ext.onFirst import java.util.* @@ -46,7 +46,7 @@ class GlobalSearchViewModel( result } } - }.flowOn(Dispatchers.Default).asLiveData(viewModelScope.coroutineContext, listOf(LoadingState)) + }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState)) init { onRefresh() diff --git a/app/src/main/java/org/koitharu/kotatsu/tracker/ui/FeedViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/tracker/ui/FeedViewModel.kt index d91e9d449..c57753ba3 100644 --- a/app/src/main/java/org/koitharu/kotatsu/tracker/ui/FeedViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/tracker/ui/FeedViewModel.kt @@ -8,7 +8,6 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.flowOn import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseViewModel import org.koitharu.kotatsu.core.model.TrackingLogItem @@ -17,7 +16,7 @@ import org.koitharu.kotatsu.list.ui.model.LoadingFooter import org.koitharu.kotatsu.list.ui.model.LoadingState import org.koitharu.kotatsu.tracker.domain.TrackingRepository import org.koitharu.kotatsu.tracker.ui.model.toFeedItem -import org.koitharu.kotatsu.utils.ext.asLiveData +import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import org.koitharu.kotatsu.utils.ext.mapItems class FeedViewModel( @@ -41,7 +40,7 @@ class FeedViewModel( isHasNextPage -> list + LoadingFooter else -> list } - }.flowOn(Dispatchers.Default).asLiveData(viewModelScope.coroutineContext, listOf(LoadingState)) + }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState)) init { loadList(append = false) diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/LiveDataExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/LiveDataExt.kt index 2ce4190c8..7ca6b52fe 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/LiveDataExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/LiveDataExt.kt @@ -26,14 +26,40 @@ fun LiveData.observeWithPrevious(owner: LifecycleOwner, observer: Buffere } } -fun Flow.asLiveData( +fun Flow.asLiveDataDistinct( + context: CoroutineContext = EmptyCoroutineContext +): LiveData = liveData(context) { + collect { + if (it != latestValue) { + emit(it) + } + } +} + +fun Flow.asLiveDataDistinct( context: CoroutineContext = EmptyCoroutineContext, defaultValue: T -): LiveData = liveData(context) { +): LiveData = liveData(context, 0L) { if (latestValue == null) { emit(defaultValue) } collect { - emit(it) + if (it != latestValue) { + emit(it) + } + } +} + +fun Flow.asLiveDataDistinct( + context: CoroutineContext = EmptyCoroutineContext, + defaultValue: suspend () -> T +): LiveData = liveData(context) { + if (latestValue == null) { + emit(defaultValue()) + } + collect { + if (it != latestValue) { + emit(it) + } } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfConfigViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfConfigViewModel.kt index 290135399..a9d5c09f6 100644 --- a/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfConfigViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/widget/shelf/ShelfConfigViewModel.kt @@ -1,13 +1,12 @@ package org.koitharu.kotatsu.widget.shelf -import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.flowOn import org.koitharu.kotatsu.base.ui.BaseViewModel import org.koitharu.kotatsu.favourites.domain.FavouritesRepository +import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import org.koitharu.kotatsu.widget.shelf.model.CategoryItem import java.util.* @@ -27,7 +26,7 @@ class ShelfConfigViewModel( CategoryItem(it.id, it.title, selectedId == it.id) } list - }.flowOn(Dispatchers.Default).asLiveData(viewModelScope.coroutineContext) + }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default) var checkedId: Long by selectedCategoryId::value } \ No newline at end of file