diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaDataRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaDataRepository.kt index dedba6923..68f7c5819 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaDataRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaDataRepository.kt @@ -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()) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedListViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedListViewModel.kt index 7fe1101ca..02c331984 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedListViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/related/RelatedListViewModel.kt @@ -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, +) : MangaListViewModel(settings, mangaDataRepository, localStorageChanges) { private val seed = savedStateHandle.require(AppRouter.KEY_MANGA).manga private val repository = mangaRepositoryFactory.create(seed.source) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt index b748f6481..8bb0c1c76 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt @@ -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, +) : MangaListViewModel(settings, mangaDataRepository, localStorageChanges), QuickFilterListener { val categoryId: Long = savedStateHandle[AppRouter.KEY_ID] ?: NO_ID private val quickFilter = quickFilterFactory.create(categoryId) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt index 03335cf6d..6d1b28313 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt @@ -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, +) : MangaListViewModel(settings, mangaDataRepository, localStorageChanges), QuickFilterListener by quickFilter { private val sortOrder: StateFlow = settings.observeAsStateFlow( scope = viewModelScope + Dispatchers.IO, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListViewModel.kt index a45da3bb1..aaa3158a8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListViewModel.kt @@ -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, ) : BaseViewModel() { abstract val content: StateFlow> @@ -63,7 +68,11 @@ abstract class MangaListViewModel( protected fun observeListModeWithTriggers(): Flow = 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 diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/LocalListViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/LocalListViewModel.kt index 4a15c156c..9b244ee6a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/LocalListViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/LocalListViewModel.kt @@ -45,7 +45,7 @@ class LocalListViewModel @Inject constructor( mangaListMapper: MangaListMapper, private val deleteLocalMangaUseCase: DeleteLocalMangaUseCase, exploreRepository: ExploreRepository, - @LocalStorageChanges private val localStorageChanges: SharedFlow, + @param:LocalStorageChanges private val localStorageChanges: SharedFlow, 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() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/picker/ui/manga/MangaPickerViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/picker/ui/manga/MangaPickerViewModel.kt index a8b692989..ac7e73d70 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/picker/ui/manga/MangaPickerViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/picker/ui/manga/MangaPickerViewModel.kt @@ -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, +) : MangaListViewModel(settings, mangaDataRepository, localStorageChanges) { override val content: StateFlow> get() = flow { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt index f072bc87d..ea6b513ff 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt @@ -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 +) : MangaListViewModel(settings, mangaDataRepository, localStorageChanges), FilterCoordinator.Owner { val source = MangaSource(savedStateHandle[RemoteListFragment.ARG_SOURCE]) val isRandomLoading = MutableStateFlow(false) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsViewModel.kt index bfccc2d20..4945986f5 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/suggestions/ui/SuggestionsViewModel.kt @@ -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, +) : 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) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/updates/UpdatesViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/updates/UpdatesViewModel.kt index c0db14a1c..e43231fa9 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/updates/UpdatesViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/tracker/ui/updates/UpdatesViewModel.kt @@ -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, +) : MangaListViewModel(settings, mangaDataRepository, localStorageChanges), QuickFilterListener by quickFilter { override val content = combine( quickFilter.appliedOptions.flatMapLatest { filterOptions ->