Replace LiveData with StateFlow
This commit is contained in:
@@ -1,9 +1,5 @@
|
||||
package org.koitharu.kotatsu.shelf.domain
|
||||
|
||||
import dagger.Reusable
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
@@ -18,19 +14,18 @@ import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.favourites.data.FavouriteCategoryEntity
|
||||
import org.koitharu.kotatsu.favourites.data.toFavouriteCategory
|
||||
import org.koitharu.kotatsu.favourites.data.toMangaList
|
||||
import org.koitharu.kotatsu.history.domain.HistoryRepository
|
||||
import org.koitharu.kotatsu.local.data.LocalManga
|
||||
import org.koitharu.kotatsu.history.data.HistoryRepository
|
||||
import org.koitharu.kotatsu.local.data.LocalMangaRepository
|
||||
import org.koitharu.kotatsu.local.data.LocalStorageChanges
|
||||
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
|
||||
import org.koitharu.kotatsu.local.domain.model.LocalManga
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
|
||||
import org.koitharu.kotatsu.shelf.domain.model.ShelfContent
|
||||
import org.koitharu.kotatsu.suggestions.domain.SuggestionRepository
|
||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
@Reusable
|
||||
class ShelfRepository @Inject constructor(
|
||||
class ShelfContentObserveUseCase @Inject constructor(
|
||||
private val localMangaRepository: LocalMangaRepository,
|
||||
private val historyRepository: HistoryRepository,
|
||||
private val trackingRepository: TrackingRepository,
|
||||
@@ -39,7 +34,7 @@ class ShelfRepository @Inject constructor(
|
||||
@LocalStorageChanges private val localStorageChanges: SharedFlow<LocalManga?>,
|
||||
) {
|
||||
|
||||
fun observeShelfContent(): Flow<ShelfContent> = combine(
|
||||
operator fun invoke(): Flow<ShelfContent> = combine(
|
||||
historyRepository.observeAllWithHistory(),
|
||||
observeLocalManga(SortOrder.UPDATED),
|
||||
observeFavourites(),
|
||||
@@ -69,23 +64,6 @@ class ShelfRepository @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun deleteLocalManga(ids: Set<Long>) {
|
||||
val list = localMangaRepository.getList(0, null, null)
|
||||
.filter { x -> x.id in ids }
|
||||
coroutineScope {
|
||||
list.map { manga ->
|
||||
async {
|
||||
val original = localMangaRepository.getRemoteManga(manga)
|
||||
if (localMangaRepository.delete(manga)) {
|
||||
runCatchingCancellable {
|
||||
historyRepository.deleteOrSwap(manga, original)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.awaitAll()
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeCategoriesContent(
|
||||
categories: List<FavouriteCategoryEntity>,
|
||||
) = combine<Pair<FavouriteCategory, List<Manga>>, Map<FavouriteCategory, List<Manga>>>(
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.koitharu.kotatsu.shelf.domain
|
||||
package org.koitharu.kotatsu.shelf.domain.model
|
||||
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.history.domain.MangaWithHistory
|
||||
import org.koitharu.kotatsu.history.domain.model.MangaWithHistory
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
|
||||
class ShelfContent(
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.koitharu.kotatsu.shelf.domain
|
||||
package org.koitharu.kotatsu.shelf.domain.model
|
||||
|
||||
enum class ShelfSection {
|
||||
|
||||
@@ -21,6 +21,8 @@ import org.koitharu.kotatsu.core.ui.list.SectionedSelectionController
|
||||
import org.koitharu.kotatsu.core.ui.util.RecyclerViewOwner
|
||||
import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver
|
||||
import org.koitharu.kotatsu.core.util.ext.addMenuProvider
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
||||
import org.koitharu.kotatsu.databinding.FragmentShelfBinding
|
||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||
import org.koitharu.kotatsu.download.ui.worker.DownloadStartedObserver
|
||||
@@ -84,9 +86,9 @@ class ShelfFragment :
|
||||
addMenuProvider(ShelfMenuProvider(binding.root.context, childFragmentManager, viewModel))
|
||||
|
||||
viewModel.content.observe(viewLifecycleOwner, ::onListChanged)
|
||||
viewModel.onError.observe(viewLifecycleOwner, SnackbarErrorObserver(binding.recyclerView, this))
|
||||
viewModel.onActionDone.observe(viewLifecycleOwner, ReversibleActionObserver(binding.recyclerView))
|
||||
viewModel.onDownloadStarted.observe(viewLifecycleOwner, DownloadStartedObserver(binding.recyclerView))
|
||||
viewModel.onError.observeEvent(viewLifecycleOwner, SnackbarErrorObserver(binding.recyclerView, this))
|
||||
viewModel.onActionDone.observeEvent(viewLifecycleOwner, ReversibleActionObserver(binding.recyclerView))
|
||||
viewModel.onDownloadStarted.observeEvent(viewLifecycleOwner, DownloadStartedObserver(binding.recyclerView))
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package org.koitharu.kotatsu.shelf.ui
|
||||
|
||||
import androidx.collection.ArraySet
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.plus
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.core.os.NetworkState
|
||||
@@ -15,13 +18,13 @@ import org.koitharu.kotatsu.core.prefs.ListMode
|
||||
import org.koitharu.kotatsu.core.prefs.observeAsFlow
|
||||
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.core.ui.util.ReversibleAction
|
||||
import org.koitharu.kotatsu.core.util.SingleLiveEvent
|
||||
import org.koitharu.kotatsu.core.util.asFlowLiveData
|
||||
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
|
||||
import org.koitharu.kotatsu.core.util.ext.call
|
||||
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
|
||||
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
|
||||
import org.koitharu.kotatsu.history.domain.HistoryRepository
|
||||
import org.koitharu.kotatsu.history.domain.MangaWithHistory
|
||||
import org.koitharu.kotatsu.history.domain.PROGRESS_NONE
|
||||
import org.koitharu.kotatsu.history.data.HistoryRepository
|
||||
import org.koitharu.kotatsu.history.data.PROGRESS_NONE
|
||||
import org.koitharu.kotatsu.history.domain.model.MangaWithHistory
|
||||
import org.koitharu.kotatsu.list.domain.ListExtraProvider
|
||||
import org.koitharu.kotatsu.list.ui.model.EmptyHint
|
||||
import org.koitharu.kotatsu.list.ui.model.EmptyState
|
||||
@@ -30,11 +33,12 @@ import org.koitharu.kotatsu.list.ui.model.LoadingState
|
||||
import org.koitharu.kotatsu.list.ui.model.toErrorState
|
||||
import org.koitharu.kotatsu.list.ui.model.toGridModel
|
||||
import org.koitharu.kotatsu.list.ui.model.toUi
|
||||
import org.koitharu.kotatsu.local.domain.DeleteLocalMangaUseCase
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.shelf.domain.ShelfContent
|
||||
import org.koitharu.kotatsu.shelf.domain.ShelfRepository
|
||||
import org.koitharu.kotatsu.shelf.domain.ShelfSection
|
||||
import org.koitharu.kotatsu.shelf.domain.ShelfContentObserveUseCase
|
||||
import org.koitharu.kotatsu.shelf.domain.model.ShelfContent
|
||||
import org.koitharu.kotatsu.shelf.domain.model.ShelfSection
|
||||
import org.koitharu.kotatsu.shelf.ui.model.ShelfSectionModel
|
||||
import org.koitharu.kotatsu.sync.domain.SyncController
|
||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
@@ -42,30 +46,31 @@ import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class ShelfViewModel @Inject constructor(
|
||||
private val repository: ShelfRepository,
|
||||
private val historyRepository: HistoryRepository,
|
||||
private val favouritesRepository: FavouritesRepository,
|
||||
private val trackingRepository: TrackingRepository,
|
||||
private val settings: AppSettings,
|
||||
private val downloadScheduler: DownloadWorker.Scheduler,
|
||||
private val deleteLocalMangaUseCase: DeleteLocalMangaUseCase,
|
||||
shelfContentObserveUseCase: ShelfContentObserveUseCase,
|
||||
syncController: SyncController,
|
||||
networkState: NetworkState,
|
||||
) : BaseViewModel(), ListExtraProvider {
|
||||
|
||||
val onActionDone = SingleLiveEvent<ReversibleAction>()
|
||||
val onDownloadStarted = SingleLiveEvent<Unit>()
|
||||
val onActionDone = MutableEventFlow<ReversibleAction>()
|
||||
val onDownloadStarted = MutableEventFlow<Unit>()
|
||||
|
||||
val content: LiveData<List<ListModel>> = combine(
|
||||
val content: StateFlow<List<ListModel>> = combine(
|
||||
settings.observeAsFlow(AppSettings.KEY_SHELF_SECTIONS) { shelfSections },
|
||||
settings.observeAsFlow(AppSettings.KEY_TRACKER_ENABLED) { isTrackerEnabled },
|
||||
settings.observeAsFlow(AppSettings.KEY_SUGGESTIONS) { isSuggestionsEnabled },
|
||||
networkState,
|
||||
repository.observeShelfContent(),
|
||||
shelfContentObserveUseCase(),
|
||||
) { sections, isTrackerEnabled, isSuggestionsEnabled, isConnected, content ->
|
||||
mapList(content, isTrackerEnabled, isSuggestionsEnabled, sections, isConnected)
|
||||
}.catch { e ->
|
||||
emit(listOf(e.toErrorState(canRetry = false)))
|
||||
}.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState))
|
||||
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, listOf(LoadingState))
|
||||
|
||||
init {
|
||||
launchJob(Dispatchers.Default) {
|
||||
@@ -95,7 +100,7 @@ class ShelfViewModel @Inject constructor(
|
||||
}
|
||||
launchJob(Dispatchers.Default) {
|
||||
val handle = favouritesRepository.removeFromCategory(category.id, ids)
|
||||
onActionDone.emitCall(ReversibleAction(R.string.removed_from_favourites, handle))
|
||||
onActionDone.call(ReversibleAction(R.string.removed_from_favourites, handle))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,14 +110,14 @@ class ShelfViewModel @Inject constructor(
|
||||
}
|
||||
launchJob(Dispatchers.Default) {
|
||||
val handle = historyRepository.delete(ids)
|
||||
onActionDone.emitCall(ReversibleAction(R.string.removed_from_history, handle))
|
||||
onActionDone.call(ReversibleAction(R.string.removed_from_history, handle))
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteLocal(ids: Set<Long>) {
|
||||
launchLoadingJob(Dispatchers.Default) {
|
||||
repository.deleteLocalManga(ids)
|
||||
onActionDone.emitCall(ReversibleAction(R.string.removal_completed, null))
|
||||
deleteLocalMangaUseCase(ids)
|
||||
onActionDone.call(ReversibleAction(R.string.removal_completed, null))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,12 +130,12 @@ class ShelfViewModel @Inject constructor(
|
||||
historyRepository.deleteAfter(minDate)
|
||||
R.string.removed_from_history
|
||||
}
|
||||
onActionDone.emitCall(ReversibleAction(stringRes, null))
|
||||
onActionDone.call(ReversibleAction(stringRes, null))
|
||||
}
|
||||
}
|
||||
|
||||
fun getManga(ids: Set<Long>): Set<Manga> {
|
||||
val snapshot = content.value ?: return emptySet()
|
||||
val snapshot = content.value
|
||||
val result = ArraySet<Manga>(ids.size)
|
||||
for (section in snapshot) {
|
||||
if (section !is ShelfSectionModel) {
|
||||
@@ -151,7 +156,7 @@ class ShelfViewModel @Inject constructor(
|
||||
fun download(items: Set<Manga>) {
|
||||
launchJob(Dispatchers.Default) {
|
||||
downloadScheduler.schedule(items)
|
||||
onDownloadStarted.emitCall(Unit)
|
||||
onDownloadStarted.call(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.databinding.ActivityShelfSettingsBinding
|
||||
import com.google.android.material.R as materialR
|
||||
|
||||
@@ -40,8 +41,6 @@ class ShelfSettingsActivity :
|
||||
it.attachToRecyclerView(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
viewModel.content.observe(this) { settingsAdapter.items = it }
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.util.ext.setChecked
|
||||
import org.koitharu.kotatsu.databinding.ItemCategoryCheckableMultipleBinding
|
||||
import org.koitharu.kotatsu.databinding.ItemShelfSectionDraggableBinding
|
||||
import org.koitharu.kotatsu.shelf.domain.ShelfSection
|
||||
import org.koitharu.kotatsu.shelf.domain.model.ShelfSection
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
fun shelfSectionAD(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.koitharu.kotatsu.shelf.ui.config
|
||||
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.shelf.domain.ShelfSection
|
||||
import org.koitharu.kotatsu.shelf.domain.model.ShelfSection
|
||||
|
||||
sealed interface ShelfSettingsItemModel : ListModel {
|
||||
|
||||
@@ -19,9 +19,7 @@ sealed interface ShelfSettingsItemModel : ListModel {
|
||||
other as Section
|
||||
|
||||
if (section != other.section) return false
|
||||
if (isChecked != other.isChecked) return false
|
||||
|
||||
return true
|
||||
return isChecked == other.isChecked
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
@@ -45,9 +43,7 @@ sealed interface ShelfSettingsItemModel : ListModel {
|
||||
|
||||
if (id != other.id) return false
|
||||
if (title != other.title) return false
|
||||
if (isChecked != other.isChecked) return false
|
||||
|
||||
return true
|
||||
return isChecked == other.isChecked
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
|
||||
@@ -4,15 +4,17 @@ import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.plus
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.observeAsFlow
|
||||
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.core.util.asFlowLiveData
|
||||
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
|
||||
import org.koitharu.kotatsu.parsers.util.move
|
||||
import org.koitharu.kotatsu.shelf.domain.ShelfSection
|
||||
import org.koitharu.kotatsu.shelf.domain.model.ShelfSection
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
@@ -27,7 +29,7 @@ class ShelfSettingsViewModel @Inject constructor(
|
||||
favouritesRepository.observeCategories(),
|
||||
) { sections, isTrackerEnabled, categories ->
|
||||
buildList(sections, isTrackerEnabled, categories)
|
||||
}.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, emptyList())
|
||||
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, emptyList())
|
||||
|
||||
private var updateJob: Job? = null
|
||||
|
||||
@@ -57,7 +59,7 @@ class ShelfSettingsViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
fun reorderSections(oldPos: Int, newPos: Int): Boolean {
|
||||
val snapshot = content.value?.toMutableList() ?: return false
|
||||
val snapshot = content.value.toMutableList()
|
||||
snapshot.move(oldPos, newPos)
|
||||
settings.shelfSections = snapshot.sections()
|
||||
return true
|
||||
|
||||
Reference in New Issue
Block a user