diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt index 526a0f461..6e99728be 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt @@ -8,12 +8,14 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.MangaCategory +import eu.kanade.tachiyomi.data.database.models.toMangaInfo import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.model.toSManga import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.browse.source.filter.AutoComplete import eu.kanade.tachiyomi.ui.browse.source.filter.AutoCompleteSectionItem @@ -31,9 +33,15 @@ import eu.kanade.tachiyomi.ui.browse.source.filter.TextSectionItem import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateItem import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateSectionItem import eu.kanade.tachiyomi.util.chapter.ChapterSettingsHelper +import eu.kanade.tachiyomi.util.lang.launchIO +import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.removeCovers import exh.savedsearches.EXHSavedSearch import exh.savedsearches.JsonSavedSearch +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.isActive import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject @@ -44,7 +52,6 @@ import rx.Observable import rx.Subscription import rx.android.schedulers.AndroidSchedulers import rx.schedulers.Schedulers -import rx.subjects.PublishSubject import timber.log.Timber import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -100,9 +107,9 @@ open class BrowseSourcePresenter( private lateinit var pager: Pager /** - * Subject that initializes a list of manga. + * Flow of manga list to initialize. */ - private val mangaDetailSubject = PublishSubject.create>() + private val mangaDetailsFlow = MutableStateFlow>(emptyList()) /** * Subscription for the pager. @@ -121,9 +128,9 @@ open class BrowseSourcePresenter( // SY <-- /** - * Subscription to initialize manga details. + * Job to initialize manga details. */ - private var initializerSubscription: Subscription? = null + private var initializerJob: Job? = null override fun onCreate(savedState: Bundle?) { super.onCreate(savedState) @@ -162,7 +169,7 @@ open class BrowseSourcePresenter( this.query = query this.appliedFilters = filters - subscribeToMangaInitializer() + initializeManga() // Create a new pager. pager = createPager(query, filters) @@ -222,24 +229,22 @@ open class BrowseSourcePresenter( /** * Subscribes to the initializer of manga details and updates the view if needed. */ - private fun subscribeToMangaInitializer() { - initializerSubscription?.let { remove(it) } - initializerSubscription = mangaDetailSubject.observeOn(Schedulers.io()) - .flatMap { Observable.from(it) } - .filter { it.thumbnail_url == null && !it.initialized } - .concatMap { getMangaDetailsObservable(it) } - .onBackpressureBuffer() - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { manga -> - @Suppress("DEPRECATION") - view?.onMangaInitialized(manga) - }, - { error -> - Timber.e(error) + private fun initializeManga() { + initializerJob?.cancel() + initializerJob = launchIO { + mangaDetailsFlow + .onEach { mangas -> + if (!isActive) return@onEach + + try { + mangas.filter { it.thumbnail_url == null && !it.initialized } + .map { getMangaDetails(it) } + .forEach { launchUI { view?.onMangaInitialized(it) } } + } catch (error: Exception) { + launchUI { Timber.e(error) } + } } - ) - .apply { add(this) } + } } /** @@ -267,24 +272,27 @@ open class BrowseSourcePresenter( * @param mangas the list of manga to initialize. */ fun initializeMangas(mangas: List) { - mangaDetailSubject.onNext(mangas) + launchIO { mangaDetailsFlow.emit(mangas) } } /** - * Returns an observable of manga that initializes the given manga. + * Returns the initialized manga. * * @param manga the manga to initialize. - * @return an observable of the manga to initialize + * @return the initialized manga */ - private fun getMangaDetailsObservable(manga: Manga): Observable { - return source.fetchMangaDetails(manga) - .flatMap { networkManga -> - manga.copyFrom(networkManga) - manga.initialized = true - db.insertManga(manga).executeAsBlocking() - Observable.just(manga) - } - .onErrorResumeNext { Observable.just(manga) } + private suspend fun getMangaDetails(manga: Manga): Manga { + return try { + source.getMangaDetails(manga.toMangaInfo()) + .let { networkManga -> + manga.copyFrom(networkManga.toSManga()) + manga.initialized = true + db.insertManga(manga).executeAsBlocking() + manga + } + } catch (e: Exception) { + manga + } } /** @@ -312,7 +320,7 @@ open class BrowseSourcePresenter( * Refreshes the active display mode. */ fun refreshDisplayMode() { - subscribeToMangaInitializer() + initializeManga() } /**