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