From f8d74a6b2f832199720a8a7cd5a29d6766953172 Mon Sep 17 00:00:00 2001 From: Ahmad Ansori Palembani Date: Mon, 16 Dec 2024 20:55:15 +0700 Subject: [PATCH] revert: "refactor(manga): Slowly using flow" I'll just redo this in the morning --- .../ui/manga/MangaDetailsController.kt | 12 +- .../ui/manga/MangaDetailsPresenter.kt | 244 +++++++++--------- .../tachiyomi/domain/manga/models/Manga.kt | 23 -- 3 files changed, 134 insertions(+), 145 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt index f5dfe23e52..01a980fa31 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt @@ -194,7 +194,7 @@ class MangaDetailsController : } } - private val manga: Manga? get() = presenter.currentManga.value + private val manga: Manga? get() = if (presenter.isMangaLateInitInitialized()) presenter.manga else null private var colorAnimator: ValueAnimator? = null override val presenter: MangaDetailsPresenter private var coverColor: Int? = null @@ -674,7 +674,7 @@ class MangaDetailsController : returningFromReader = false runBlocking { val itemAnimator = binding.recycler.itemAnimator - val chapters = withTimeoutOrNull(1000) { presenter.setAndGetChapters() } ?: return@runBlocking + val chapters = withTimeoutOrNull(1000) { presenter.getChaptersNow() } ?: return@runBlocking binding.recycler.itemAnimator = null tabletAdapter?.notifyItemChanged(0) adapter?.setChapters(chapters) @@ -821,15 +821,15 @@ class MangaDetailsController : updateMenuVisibility(activityBinding?.toolbar?.menu) } - fun updateChapters(fetchFromSource: Boolean = false) { + fun updateChapters(chapters: List) { view ?: return binding.swipeRefresh.isRefreshing = presenter.isLoading - if (presenter.chapters.isEmpty() && fromCatalogue && !presenter.hasRequested && fetchFromSource) { + if (presenter.chapters.isEmpty() && fromCatalogue && !presenter.hasRequested) { launchUI { binding.swipeRefresh.isRefreshing = true } - presenter.refreshChapters() + presenter.fetchChaptersFromSource() } tabletAdapter?.notifyItemChanged(0) - adapter?.setChapters(presenter.chapters) + adapter?.setChapters(chapters) addMangaHeader() colorToolbar(binding.recycler.canScrollVertically(-1)) updateMenuVisibility(activityBinding?.toolbar?.menu) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt index f49dfd10f7..67ed320378 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt @@ -34,7 +34,6 @@ import eu.kanade.tachiyomi.data.track.EnhancedTrackService import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.domain.manga.models.Manga -import eu.kanade.tachiyomi.network.HttpException import eu.kanade.tachiyomi.network.NetworkPreferences import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.Source @@ -77,15 +76,11 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.flow.updateAndGet import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext @@ -132,15 +127,11 @@ class MangaDetailsPresenter( private val networkPreferences: NetworkPreferences by injectLazy() - private val currentMangaInternal = MutableStateFlow(null) - val currentManga = currentMangaInternal.asStateFlow() +// private val currentMangaInternal: MutableStateFlow = MutableStateFlow(null) +// val currentManga get() = currentMangaInternal.asStateFlow() - /** - * Unsafe, call only after currentManga is no longer null - */ - var manga: Manga - get() = currentManga.value!! - set(value) { currentMangaInternal.value = value } + lateinit var manga: Manga + fun isMangaLateInitInitialized() = ::manga.isInitialized private val customMangaManager: CustomMangaManager by injectLazy() private val mangaShortcutManager: MangaShortcutManager by injectLazy() @@ -160,12 +151,8 @@ class MangaDetailsPresenter( var trackList: List = emptyList() - private val currentChaptersInternal = MutableStateFlow>(emptyList()) - val currentChapters = currentChaptersInternal.asStateFlow() - - var chapters: List - get() = currentChapters.value - private set(value) { currentChaptersInternal.value = value } + var chapters: List = emptyList() + private set var allChapters: List = emptyList() private set @@ -199,7 +186,7 @@ class MangaDetailsPresenter( val controller = view ?: return isLockedFromSearch = controller.shouldLockIfNeeded && SecureActivityDelegate.shouldBeLocked() - if (currentManga.value == null) runBlocking { refreshMangaFromDb() } + if (!::manga.isInitialized) runBlocking { refreshMangaFromDb() } syncData() presenterScope.launchUI { @@ -217,22 +204,6 @@ class MangaDetailsPresenter( presenterScope.launchIO { downloadManager.queueState.collectLatest(::onQueueUpdate) } - presenterScope.launchUI { - currentManga.collectLatest { - if (it == null) return@collectLatest - } - } - presenterScope.launchIO { - currentChapters.collectLatest { chapters -> - allChapters = if (!isScanlatorFiltered()) chapters else getChapter.awaitAll(mangaId, false).map { it.toModel() } - - allChapterScanlators = allChapters.mapNotNull { it.chapter.scanlator }.toSet() - - withUIContext { - controller.updateChapters(allChapters.isEmpty()) - } - } - } runBlocking { tracks = getTrack.awaitAllByMangaId(mangaId) @@ -258,7 +229,8 @@ class MangaDetailsPresenter( controller.updateHeader() refreshAll() } else { - runBlocking { chapters = getChapters() } + runBlocking { getChapters() } + controller.updateChapters(this.chapters) getHistory() } @@ -271,14 +243,16 @@ class MangaDetailsPresenter( fun fetchChapters(andTracking: Boolean = true) { presenterScope.launch { - setCurrentChapters(getChapters()) + getChapters() if (andTracking) fetchTracks() + withContext(Dispatchers.Main) { view?.updateChapters(chapters) } getHistory() } } fun setCurrentManga(manga: Manga?) { - currentMangaInternal.update { manga } +// currentMangaInternal.update { manga } + this.manga = manga!! } // TODO: Use flow to "sync" data instead @@ -290,18 +264,20 @@ class MangaDetailsPresenter( } } - // TODO: Use getChapter.subscribe() flow instead - suspend fun setAndGetChapters(): List { - return currentChaptersInternal.updateAndGet { getChapters() } + suspend fun getChaptersNow(): List { + getChapters() + return chapters } - // TODO: Use getChapter.subscribe() flow instead - private fun setCurrentChapters(chapters: List) { - currentChaptersInternal.update { chapters } - } + private suspend fun getChapters(queue: List = downloadManager.queueState.value) { + val chapters = getChapter.awaitAll(mangaId, isScanlatorFiltered()).map { it.toModel() } + allChapters = if (!isScanlatorFiltered()) chapters else getChapter.awaitAll(mangaId, false).map { it.toModel() } - private suspend fun getChapters(): List { - return getChapter.awaitAll(mangaId, isScanlatorFiltered()).map { it.toModel() } + // Find downloaded chapters + setDownloadedChapters(chapters, queue) + allChapterScanlators = allChapters.mapNotNull { it.chapter.scanlator }.toSet() + + this.chapters = applyChapterFilters(chapters) } private fun getHistory() { @@ -418,7 +394,7 @@ class MangaDetailsPresenter( download = null } - view?.updateChapters() + view?.updateChapters(this.chapters) downloadManager.deleteChapters(listOf(chapter), manga, source, true) } @@ -436,7 +412,7 @@ class MangaDetailsPresenter( } } - if (update) view?.updateChapters() + if (update) view?.updateChapters(this.chapters) if (isEverything) { downloadManager.deleteManga(manga, source) @@ -456,93 +432,126 @@ class MangaDetailsPresenter( if (view?.isNotOnline() == true && !manga.isLocal()) return presenterScope.launch { isLoading = true - val tasks = listOf( - async { fetchMangaFromSource() }, - async { fetchChaptersFromSource() }, - ) - tasks.awaitAll() - isLoading = false - } - } + var mangaError: java.lang.Exception? = null + var chapterError: java.lang.Exception? = null + val chapters = async(Dispatchers.IO) { + try { + source.getChapterList(manga.copy()) + } catch (e: Exception) { + chapterError = e + emptyList() + } + } + val nManga = async(Dispatchers.IO) { + try { + source.getMangaDetails(manga.copy()) + } catch (e: java.lang.Exception) { + mangaError = e + null + } + } - private suspend fun fetchMangaFromSource() { - try { - val manga = manga.copy() - val networkManga = source.getMangaDetails(manga) + val networkManga = nManga.await() + if (networkManga != null) { + manga.prepareCoverUpdate(coverCache, networkManga, false) + manga.copyFrom(networkManga) + manga.initialized = true - manga.prepareCoverUpdate(coverCache, networkManga, false) - manga.copyFrom(networkManga) - manga.initialized = true + updateManga.await(manga.toMangaUpdate()) - updateManga.await(manga.toMangaUpdate()) + launchIO { + val request = + ImageRequest.Builder(preferences.context).data(manga.cover()) + .memoryCachePolicy(CachePolicy.DISABLED) + .diskCachePolicy(CachePolicy.WRITE_ONLY) + .build() - setCurrentManga(manga) - - presenterScope.launchNonCancellableIO { - val request = - ImageRequest.Builder(preferences.context).data(manga.cover()) - .memoryCachePolicy(CachePolicy.DISABLED) - .diskCachePolicy(CachePolicy.WRITE_ONLY) - .build() - - if (preferences.context.imageLoader.execute(request) is SuccessResult) { - withContext(Dispatchers.Main) { - view?.setPaletteColor() + if (preferences.context.imageLoader.execute(request) is SuccessResult) { + withContext(Dispatchers.Main) { + view?.setPaletteColor() + } } } } - } catch (e: Exception) { - if (e is HttpException && e.code == 103) return - - withUIContext { - view?.showError(trimException(e)) - } - } - } - - private suspend fun fetchChaptersFromSource(manualFetch: Boolean = true) { - try { - withIOContext { - val chapters = source.getChapterList(manga.copy()) - val (added, removed) = syncChaptersWithSource(chapters, manga, source) - if (added.isNotEmpty() && manualFetch) { + val finChapters = chapters.await() + if (finChapters.isNotEmpty()) { + val newChapters = withIOContext { syncChaptersWithSource(finChapters, manga, source) } + if (newChapters.first.isNotEmpty()) { if (manga.shouldDownloadNewChapters(preferences)) { - downloadChapters(added.sortedBy { it.chapter_number }.map { it.toModel() }) - } - withUIContext { - view?.view?.context?.let { mangaShortcutManager.updateShortcuts(it) } + downloadChapters( + newChapters.first.sortedBy { it.chapter_number } + .map { it.toModel() }, + ) } + view?.view?.context?.let { mangaShortcutManager.updateShortcuts(it) } } - if (removed.isNotEmpty() && manualFetch) { - val removedChaptersId = removed.map { it.id } + if (newChapters.second.isNotEmpty()) { + val removedChaptersId = newChapters.second.map { it.id } val removedChapters = this@MangaDetailsPresenter.chapters.filter { it.id in removedChaptersId && it.isDownloaded } if (removedChapters.isNotEmpty()) { - withUIContext { - view?.showChaptersRemovedPopup(removedChapters) + withContext(Dispatchers.Main) { + view?.showChaptersRemovedPopup( + removedChapters, + ) } } } - setCurrentChapters(getChapters()) - getHistory() + getChapters() } - } catch (e: Exception) { - withUIContext { - view?.showError(trimException(e)) + isLoading = false + if (chapterError == null) { + withContext(Dispatchers.Main) { + view?.updateChapters(this@MangaDetailsPresenter.chapters) + } } + if (chapterError != null) { + withContext(Dispatchers.Main) { + view?.showError( + trimException(chapterError!!), + ) + } + return@launch + } else if (mangaError != null) { + withContext(Dispatchers.Main) { + view?.showError( + trimException(mangaError!!), + ) + } + } + getHistory() } } /** * Requests an updated list of chapters from the source. */ - fun refreshChapters() { - presenterScope.launchUI { - hasRequested = true - isLoading = true - fetchChaptersFromSource(true) + fun fetchChaptersFromSource() { + hasRequested = true + isLoading = true + + presenterScope.launch(Dispatchers.IO) { + val chapters = try { + source.getChapterList(manga.copy()) + } catch (e: Exception) { + withContext(Dispatchers.Main) { view?.showError(trimException(e)) } + return@launch + } isLoading = false + try { + syncChaptersWithSource(chapters, manga, source) + + getChapters() + withContext(Dispatchers.Main) { + view?.updateChapters(this@MangaDetailsPresenter.chapters) + } + getHistory() + } catch (e: java.lang.Exception) { + withContext(Dispatchers.Main) { + view?.showError(trimException(e)) + } + } } } @@ -570,7 +579,8 @@ class MangaDetailsPresenter( it.toProgressUpdate() } updateChapter.awaitAll(updates) - setCurrentChapters(getChapters()) + getChapters() + withContext(Dispatchers.Main) { view?.updateChapters(chapters) } } } @@ -599,7 +609,8 @@ class MangaDetailsPresenter( if (read && deleteNow && preferences.removeAfterMarkedAsRead().get()) { deleteChapters(selectedChapters, false) } - setCurrentChapters(getChapters()) + getChapters() + withContext(Dispatchers.Main) { view?.updateChapters(chapters) } if (read && deleteNow) { val latestReadChapter = selectedChapters.maxByOrNull { it.chapter_number.toInt() }?.chapter updateTrackChapterMarkedAsRead(preferences, latestReadChapter, manga.id) { @@ -730,7 +741,8 @@ class MangaDetailsPresenter( private suspend fun asyncUpdateMangaAndChapters(justChapters: Boolean = false) { if (!justChapters) updateManga.await(MangaUpdate(manga.id!!, chapterFlags = manga.chapter_flags)) - setCurrentChapters(getChapters()) + getChapters() + withUIContext { view?.updateChapters(chapters) } } private fun isScanlatorFiltered() = manga.filtered_scanlators?.isNotEmpty() == true @@ -1146,9 +1158,9 @@ class MangaDetailsPresenter( } private suspend fun onQueueUpdate(queue: List) = withIOContext { - setDownloadedChapters(chapters, queue) + getChapters(queue) withUIContext { - view?.updateChapters() + view?.updateChapters(chapters) } } diff --git a/domain/src/commonMain/kotlin/eu/kanade/tachiyomi/domain/manga/models/Manga.kt b/domain/src/commonMain/kotlin/eu/kanade/tachiyomi/domain/manga/models/Manga.kt index 58e8edcc13..da8d99f67b 100644 --- a/domain/src/commonMain/kotlin/eu/kanade/tachiyomi/domain/manga/models/Manga.kt +++ b/domain/src/commonMain/kotlin/eu/kanade/tachiyomi/domain/manga/models/Manga.kt @@ -35,29 +35,6 @@ interface Manga : SManga { var cover_last_modified: Long - override fun copy(): Manga { - return (super.copy() as Manga).also { - it.id = this.id - it.source = this.source - it.favorite = this.favorite - it.last_update = this.last_update - it.date_added = this.date_added - it.viewer_flags = this.viewer_flags - it.chapter_flags = this.chapter_flags - it.hide_title = this.hide_title - it.filtered_scanlators = this.filtered_scanlators - - it.ogTitle = this.ogTitle - it.ogAuthor = this.ogAuthor - it.ogArtist = this.ogArtist - it.ogDesc = this.ogDesc - it.ogGenre = this.ogGenre - it.ogStatus = this.ogStatus - - it.cover_last_modified = this.cover_last_modified - } - } - @Deprecated("Use ogTitle directly instead", ReplaceWith("ogTitle")) val originalTitle: String get() = ogTitle