feat: Realtime Favorite and Storage Badges
This commit is contained in:
committed by
Koitharu
parent
3be7848ad9
commit
17a0725666
@@ -9,6 +9,8 @@ import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||
import org.koitharu.kotatsu.core.db.TABLE_FAVOURITES
|
||||
import org.koitharu.kotatsu.core.db.TABLE_FAVOURITE_CATEGORIES
|
||||
import org.koitharu.kotatsu.core.db.TABLE_PREFERENCES
|
||||
import org.koitharu.kotatsu.core.db.entity.ContentRating
|
||||
import org.koitharu.kotatsu.core.db.entity.MangaPrefsEntity
|
||||
@@ -189,6 +191,11 @@ class MangaDataRepository @Inject constructor(
|
||||
emitInitialState = emitInitialState,
|
||||
)
|
||||
|
||||
fun observeFavoritesTrigger(emitInitialState: Boolean) = db.invalidationTracker.createFlow(
|
||||
tables = arrayOf(TABLE_FAVOURITES, TABLE_FAVOURITE_CATEGORIES),
|
||||
emitInitialState = emitInitialState,
|
||||
)
|
||||
|
||||
private suspend fun Manga.withCachedChaptersIfNeeded(flag: Boolean): Manga = if (flag && !isLocal && chapters.isNullOrEmpty()) {
|
||||
val cachedChapters = db.getChaptersDao().findAll(id)
|
||||
if (cachedChapters.isEmpty()) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
@@ -25,6 +26,8 @@ import org.koitharu.kotatsu.list.ui.MangaListViewModel
|
||||
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.local.data.LocalStorageChanges
|
||||
import org.koitharu.kotatsu.local.domain.model.LocalManga
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -35,7 +38,8 @@ class RelatedListViewModel @Inject constructor(
|
||||
settings: AppSettings,
|
||||
private val mangaListMapper: MangaListMapper,
|
||||
mangaDataRepository: MangaDataRepository,
|
||||
) : MangaListViewModel(settings, mangaDataRepository) {
|
||||
@LocalStorageChanges localStorageChanges: SharedFlow<LocalManga?>,
|
||||
) : MangaListViewModel(settings, mangaDataRepository, localStorageChanges) {
|
||||
|
||||
private val seed = savedStateHandle.require<ParcelableManga>(AppRouter.KEY_MANGA).manga
|
||||
private val repository = mangaRepositoryFactory.create(seed.source)
|
||||
|
||||
@@ -40,6 +40,9 @@ import org.koitharu.kotatsu.list.ui.model.toErrorState
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import javax.inject.Inject
|
||||
import org.koitharu.kotatsu.local.data.LocalStorageChanges
|
||||
import org.koitharu.kotatsu.local.domain.model.LocalManga
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
|
||||
private const val PAGE_SIZE = 16
|
||||
|
||||
@@ -52,7 +55,8 @@ class FavouritesListViewModel @Inject constructor(
|
||||
quickFilterFactory: FavoritesListQuickFilter.Factory,
|
||||
settings: AppSettings,
|
||||
mangaDataRepository: MangaDataRepository,
|
||||
) : MangaListViewModel(settings, mangaDataRepository), QuickFilterListener {
|
||||
@LocalStorageChanges localStorageChanges: SharedFlow<LocalManga?>,
|
||||
) : MangaListViewModel(settings, mangaDataRepository, localStorageChanges), QuickFilterListener {
|
||||
|
||||
val categoryId: Long = savedStateHandle[AppRouter.KEY_ID] ?: NO_ID
|
||||
private val quickFilter = quickFilterFactory.create(categoryId)
|
||||
|
||||
@@ -43,6 +43,9 @@ import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import java.time.Instant
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import javax.inject.Inject
|
||||
import org.koitharu.kotatsu.local.data.LocalStorageChanges
|
||||
import org.koitharu.kotatsu.local.domain.model.LocalManga
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
|
||||
private const val PAGE_SIZE = 16
|
||||
|
||||
@@ -54,7 +57,8 @@ class HistoryListViewModel @Inject constructor(
|
||||
private val markAsReadUseCase: MarkAsReadUseCase,
|
||||
private val quickFilter: HistoryListQuickFilter,
|
||||
mangaDataRepository: MangaDataRepository,
|
||||
) : MangaListViewModel(settings, mangaDataRepository), QuickFilterListener by quickFilter {
|
||||
@LocalStorageChanges localStorageChanges: SharedFlow<LocalManga?>,
|
||||
) : MangaListViewModel(settings, mangaDataRepository, localStorageChanges), QuickFilterListener by quickFilter {
|
||||
|
||||
private val sortOrder: StateFlow<ListSortOrder> = settings.observeAsStateFlow(
|
||||
scope = viewModelScope + Dispatchers.IO,
|
||||
|
||||
@@ -3,10 +3,12 @@ package org.koitharu.kotatsu.list.ui
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.plus
|
||||
@@ -22,10 +24,13 @@ import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
|
||||
import org.koitharu.kotatsu.list.domain.ListFilterOption
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.local.data.LocalStorageChanges
|
||||
import org.koitharu.kotatsu.local.domain.model.LocalManga
|
||||
|
||||
abstract class MangaListViewModel(
|
||||
private val settings: AppSettings,
|
||||
private val mangaDataRepository: MangaDataRepository,
|
||||
@param:LocalStorageChanges private val localStorageChanges: SharedFlow<LocalManga?>,
|
||||
) : BaseViewModel() {
|
||||
|
||||
abstract val content: StateFlow<List<ListModel>>
|
||||
@@ -63,7 +68,11 @@ abstract class MangaListViewModel(
|
||||
|
||||
protected fun observeListModeWithTriggers(): Flow<ListMode> = combine(
|
||||
listMode,
|
||||
mangaDataRepository.observeOverridesTrigger(emitInitialState = true),
|
||||
merge(
|
||||
mangaDataRepository.observeOverridesTrigger(emitInitialState = true),
|
||||
mangaDataRepository.observeFavoritesTrigger(emitInitialState = true),
|
||||
localStorageChanges.onStart { emit(null) },
|
||||
),
|
||||
settings.observeChanges().filter { key ->
|
||||
key == AppSettings.KEY_PROGRESS_INDICATORS
|
||||
|| key == AppSettings.KEY_TRACKER_ENABLED
|
||||
|
||||
@@ -45,7 +45,7 @@ class LocalListViewModel @Inject constructor(
|
||||
mangaListMapper: MangaListMapper,
|
||||
private val deleteLocalMangaUseCase: DeleteLocalMangaUseCase,
|
||||
exploreRepository: ExploreRepository,
|
||||
@LocalStorageChanges private val localStorageChanges: SharedFlow<LocalManga?>,
|
||||
@param:LocalStorageChanges private val localStorageChanges: SharedFlow<LocalManga?>,
|
||||
private val localStorageManager: LocalStorageManager,
|
||||
sourcesRepository: MangaSourcesRepository,
|
||||
mangaDataRepository: MangaDataRepository,
|
||||
@@ -58,6 +58,7 @@ class LocalListViewModel @Inject constructor(
|
||||
exploreRepository = exploreRepository,
|
||||
sourcesRepository = sourcesRepository,
|
||||
mangaDataRepository = mangaDataRepository,
|
||||
localStorageChanges = localStorageChanges,
|
||||
), SharedPreferences.OnSharedPreferenceChangeListener, QuickFilterListener {
|
||||
|
||||
val onMangaRemoved = MutableEventFlow<Unit>()
|
||||
|
||||
@@ -20,6 +20,9 @@ import org.koitharu.kotatsu.list.ui.model.ListHeader
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.list.ui.model.LoadingState
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import org.koitharu.kotatsu.local.data.LocalStorageChanges
|
||||
import org.koitharu.kotatsu.local.domain.model.LocalManga
|
||||
|
||||
@HiltViewModel
|
||||
class MangaPickerViewModel @Inject constructor(
|
||||
@@ -28,7 +31,8 @@ class MangaPickerViewModel @Inject constructor(
|
||||
private val historyRepository: HistoryRepository,
|
||||
private val favouritesRepository: FavouritesRepository,
|
||||
private val mangaListMapper: MangaListMapper,
|
||||
) : MangaListViewModel(settings, mangaDataRepository) {
|
||||
@LocalStorageChanges localStorageChanges: SharedFlow<LocalManga?>,
|
||||
) : MangaListViewModel(settings, mangaDataRepository, localStorageChanges) {
|
||||
|
||||
override val content: StateFlow<List<ListModel>>
|
||||
get() = flow {
|
||||
|
||||
@@ -8,6 +8,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancelAndJoin
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.combine
|
||||
@@ -40,6 +41,8 @@ import org.koitharu.kotatsu.list.ui.model.LoadingFooter
|
||||
import org.koitharu.kotatsu.list.ui.model.LoadingState
|
||||
import org.koitharu.kotatsu.list.ui.model.toErrorFooter
|
||||
import org.koitharu.kotatsu.list.ui.model.toErrorState
|
||||
import org.koitharu.kotatsu.local.data.LocalStorageChanges
|
||||
import org.koitharu.kotatsu.local.domain.model.LocalManga
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.util.sizeOrZero
|
||||
import javax.inject.Inject
|
||||
@@ -55,8 +58,9 @@ open class RemoteListViewModel @Inject constructor(
|
||||
protected val mangaListMapper: MangaListMapper,
|
||||
private val exploreRepository: ExploreRepository,
|
||||
sourcesRepository: MangaSourcesRepository,
|
||||
mangaDataRepository: MangaDataRepository
|
||||
) : MangaListViewModel(settings, mangaDataRepository), FilterCoordinator.Owner {
|
||||
mangaDataRepository: MangaDataRepository,
|
||||
@LocalStorageChanges localStorageChanges: SharedFlow<LocalManga?>
|
||||
) : MangaListViewModel(settings, mangaDataRepository, localStorageChanges), FilterCoordinator.Owner {
|
||||
|
||||
val source = MangaSource(savedStateHandle[RemoteListFragment.ARG_SOURCE])
|
||||
val isRandomLoading = MutableStateFlow(false)
|
||||
|
||||
@@ -24,6 +24,9 @@ import org.koitharu.kotatsu.list.ui.model.toErrorState
|
||||
import org.koitharu.kotatsu.suggestions.domain.SuggestionRepository
|
||||
import org.koitharu.kotatsu.suggestions.domain.SuggestionsListQuickFilter
|
||||
import javax.inject.Inject
|
||||
import org.koitharu.kotatsu.local.data.LocalStorageChanges
|
||||
import org.koitharu.kotatsu.local.domain.model.LocalManga
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
|
||||
@HiltViewModel
|
||||
class SuggestionsViewModel @Inject constructor(
|
||||
@@ -33,7 +36,8 @@ class SuggestionsViewModel @Inject constructor(
|
||||
private val quickFilter: SuggestionsListQuickFilter,
|
||||
private val suggestionsScheduler: SuggestionsWorker.Scheduler,
|
||||
mangaDataRepository: MangaDataRepository,
|
||||
) : MangaListViewModel(settings, mangaDataRepository), QuickFilterListener by quickFilter {
|
||||
@LocalStorageChanges localStorageChanges: SharedFlow<LocalManga?>,
|
||||
) : MangaListViewModel(settings, mangaDataRepository, localStorageChanges), QuickFilterListener by quickFilter {
|
||||
|
||||
override val listMode = settings.observeAsFlow(AppSettings.KEY_LIST_MODE_SUGGESTIONS) { suggestionsListMode }
|
||||
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, settings.suggestionsListMode)
|
||||
|
||||
@@ -31,6 +31,9 @@ import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
import org.koitharu.kotatsu.tracker.domain.UpdatesListQuickFilter
|
||||
import org.koitharu.kotatsu.tracker.domain.model.MangaTracking
|
||||
import javax.inject.Inject
|
||||
import org.koitharu.kotatsu.local.data.LocalStorageChanges
|
||||
import org.koitharu.kotatsu.local.domain.model.LocalManga
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
|
||||
@HiltViewModel
|
||||
class UpdatesViewModel @Inject constructor(
|
||||
@@ -39,7 +42,8 @@ class UpdatesViewModel @Inject constructor(
|
||||
private val mangaListMapper: MangaListMapper,
|
||||
private val quickFilter: UpdatesListQuickFilter,
|
||||
mangaDataRepository: MangaDataRepository,
|
||||
) : MangaListViewModel(settings, mangaDataRepository), QuickFilterListener by quickFilter {
|
||||
@LocalStorageChanges localStorageChanges: SharedFlow<LocalManga?>,
|
||||
) : MangaListViewModel(settings, mangaDataRepository, localStorageChanges), QuickFilterListener by quickFilter {
|
||||
|
||||
override val content = combine(
|
||||
quickFilter.appliedOptions.flatMapLatest { filterOptions ->
|
||||
|
||||
Reference in New Issue
Block a user