MangaPresenter: Fix state updates when opening a new manga entry (#7379)
(cherry picked from commit 0e0c1dcdc5f42b0a63d5b865c9aba29db4ab18a6) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt
This commit is contained in:
parent
a3dd6b523a
commit
3fa1c24f8d
@ -32,7 +32,6 @@ import eu.kanade.tachiyomi.data.download.DownloadManager
|
|||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
import eu.kanade.tachiyomi.data.library.CustomMangaManager
|
import eu.kanade.tachiyomi.data.library.CustomMangaManager
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.data.saver.ImageSaver
|
|
||||||
import eu.kanade.tachiyomi.data.track.EnhancedTrackService
|
import eu.kanade.tachiyomi.data.track.EnhancedTrackService
|
||||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||||
import eu.kanade.tachiyomi.data.track.TrackService
|
import eu.kanade.tachiyomi.data.track.TrackService
|
||||||
@ -84,6 +83,7 @@ import kotlinx.coroutines.flow.collectLatest
|
|||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.supervisorScope
|
import kotlinx.coroutines.supervisorScope
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
@ -152,8 +152,6 @@ class MangaPresenter(
|
|||||||
|
|
||||||
private val loggedServices by lazy { trackManager.services.filter { it.isLogged } }
|
private val loggedServices by lazy { trackManager.services.filter { it.isLogged } }
|
||||||
|
|
||||||
private val imageSaver: ImageSaver by injectLazy()
|
|
||||||
|
|
||||||
private var trackSubscription: Subscription? = null
|
private var trackSubscription: Subscription? = null
|
||||||
private var searchTrackerJob: Job? = null
|
private var searchTrackerJob: Job? = null
|
||||||
private var refreshTrackersJob: Job? = null
|
private var refreshTrackersJob: Job? = null
|
||||||
@ -194,6 +192,13 @@ class MangaPresenter(
|
|||||||
this(pair.first, pair.second, flatMetadata)
|
this(pair.first, pair.second, flatMetadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to update the UI state only if it's currently in success state
|
||||||
|
*/
|
||||||
|
private fun updateSuccessState(func: (MangaScreenState.Success) -> MangaScreenState.Success) {
|
||||||
|
_state.update { if (it is MangaScreenState.Success) func(it) else it }
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedState: Bundle?) {
|
override fun onCreate(savedState: Bundle?) {
|
||||||
super.onCreate(savedState)
|
super.onCreate(savedState)
|
||||||
|
|
||||||
@ -253,49 +258,50 @@ class MangaPresenter(
|
|||||||
// SY <--
|
// SY <--
|
||||||
.collectLatest { (manga, chapters, flatMetadata, mergedData) ->
|
.collectLatest { (manga, chapters, flatMetadata, mergedData) ->
|
||||||
val chapterItems = chapters.toChapterItems(manga, mergedData)
|
val chapterItems = chapters.toChapterItems(manga, mergedData)
|
||||||
val currentState = _state.value
|
_state.update { currentState ->
|
||||||
_state.value = when (currentState) {
|
when (currentState) {
|
||||||
// Initialize success state
|
// Initialize success state
|
||||||
MangaScreenState.Loading -> {
|
MangaScreenState.Loading -> {
|
||||||
val source = Injekt.get<SourceManager>().getOrStub(manga.source)
|
val source = Injekt.get<SourceManager>().getOrStub(manga.source)
|
||||||
MangaScreenState.Success(
|
MangaScreenState.Success(
|
||||||
manga = manga,
|
manga = manga,
|
||||||
source = source,
|
source = source,
|
||||||
dateRelativeTime = if (source.isEhBasedSource()) 0 else preferences.relativeTime().get(),
|
dateRelativeTime = if (source.isEhBasedSource()) 0 else preferences.relativeTime().get(),
|
||||||
dateFormat = if (source.isEhBasedSource()) {
|
dateFormat = if (source.isEhBasedSource()) {
|
||||||
MetadataUtil.EX_DATE_FORMAT
|
MetadataUtil.EX_DATE_FORMAT
|
||||||
} else {
|
} else {
|
||||||
preferences.dateFormat()
|
preferences.dateFormat()
|
||||||
},
|
},
|
||||||
isFromSource = isFromSource,
|
isFromSource = isFromSource,
|
||||||
trackingAvailable = trackManager.hasLoggedServices(),
|
trackingAvailable = trackManager.hasLoggedServices(),
|
||||||
chapters = chapterItems,
|
chapters = chapterItems,
|
||||||
meta = raiseMetadata(flatMetadata, source),
|
meta = raiseMetadata(flatMetadata, source),
|
||||||
mergedData = mergedData,
|
mergedData = mergedData,
|
||||||
showRecommendationsInOverflow = preferences.recommendsInOverflow().get(),
|
showRecommendationsInOverflow = preferences.recommendsInOverflow().get(),
|
||||||
showMergeWithAnother = smartSearched,
|
showMergeWithAnother = smartSearched,
|
||||||
).also {
|
).also {
|
||||||
getTrackingObservable(manga)
|
getTrackingObservable(manga)
|
||||||
.subscribeLatestCache(
|
.subscribeLatestCache(
|
||||||
{ _, count ->
|
{ _, count ->
|
||||||
successState?.let {
|
successState?.let {
|
||||||
_state.value = it.copy(trackingCount = count)
|
_state.value = it.copy(trackingCount = count)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ _, error -> logcat(LogPriority.ERROR, error) },
|
{ _, error -> logcat(LogPriority.ERROR, error) },
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Update state
|
// Update state
|
||||||
is MangaScreenState.Success -> currentState.copy(
|
is MangaScreenState.Success -> currentState.copy(
|
||||||
manga = manga,
|
manga = manga,
|
||||||
chapters = chapterItems,
|
chapters = chapterItems,
|
||||||
// SY -->
|
// SY -->
|
||||||
meta = raiseMetadata(flatMetadata, currentState.source),
|
meta = raiseMetadata(flatMetadata, currentState.source),
|
||||||
mergedData = mergedData,
|
mergedData = mergedData,
|
||||||
// SY <--
|
// SY <--
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchTrackers()
|
fetchTrackers()
|
||||||
@ -309,17 +315,13 @@ class MangaPresenter(
|
|||||||
|
|
||||||
preferences.incognitoMode()
|
preferences.incognitoMode()
|
||||||
.asImmediateFlow { incognito ->
|
.asImmediateFlow { incognito ->
|
||||||
successState?.let {
|
updateSuccessState { it.copy(isIncognitoMode = incognito) }
|
||||||
_state.value = it.copy(isIncognitoMode = incognito)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.launchIn(presenterScope)
|
.launchIn(presenterScope)
|
||||||
|
|
||||||
preferences.downloadedOnly()
|
preferences.downloadedOnly()
|
||||||
.asImmediateFlow { downloadedOnly ->
|
.asImmediateFlow { downloadedOnly ->
|
||||||
successState?.let {
|
updateSuccessState { it.copy(isDownloadedOnlyMode = downloadedOnly) }
|
||||||
_state.value = it.copy(isDownloadedOnlyMode = downloadedOnly)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.launchIn(presenterScope)
|
.launchIn(presenterScope)
|
||||||
}
|
}
|
||||||
@ -353,17 +355,18 @@ class MangaPresenter(
|
|||||||
*/
|
*/
|
||||||
private fun fetchMangaFromSource(manualFetch: Boolean = false) {
|
private fun fetchMangaFromSource(manualFetch: Boolean = false) {
|
||||||
if (fetchMangaJob?.isActive == true) return
|
if (fetchMangaJob?.isActive == true) return
|
||||||
val successState = successState ?: return
|
|
||||||
fetchMangaJob = presenterScope.launchIO {
|
fetchMangaJob = presenterScope.launchIO {
|
||||||
_state.value = successState.copy(isRefreshingInfo = true)
|
updateSuccessState { it.copy(isRefreshingInfo = true) }
|
||||||
try {
|
try {
|
||||||
val networkManga = successState.source.getMangaDetails(successState.manga.toMangaInfo())
|
successState?.let {
|
||||||
updateManga.awaitUpdateFromSource(successState.manga, networkManga, manualFetch)
|
val networkManga = it.source.getMangaDetails(it.manga.toMangaInfo())
|
||||||
|
updateManga.awaitUpdateFromSource(it.manga, networkManga, manualFetch)
|
||||||
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
this@MangaPresenter.xLogE("Error getting manga details", e)
|
this@MangaPresenter.xLogE("Error getting manga details", e)
|
||||||
withUIContext { view?.onFetchMangaInfoError(e) }
|
withUIContext { view?.onFetchMangaInfoError(e) }
|
||||||
}
|
}
|
||||||
_state.value = successState.copy(isRefreshingInfo = false)
|
updateSuccessState { it.copy(isRefreshingInfo = false) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,7 +420,9 @@ class MangaPresenter(
|
|||||||
manga = manga.copy()
|
manga = manga.copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
_state.value = state.copy(manga = manga)
|
updateSuccessState { successState ->
|
||||||
|
successState.copy(manga = manga)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun smartSearchMerge(context: Context, manga: DomainManga, originalMangaId: Long): Manga {
|
suspend fun smartSearchMerge(context: Context, manga: DomainManga, originalMangaId: Long): Manga {
|
||||||
@ -750,15 +755,16 @@ class MangaPresenter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateDownloadState(download: Download) {
|
private fun updateDownloadState(download: Download) {
|
||||||
val successState = successState ?: return
|
updateSuccessState { successState ->
|
||||||
val modifiedIndex = successState.chapters.indexOfFirst { it.chapter.id == download.chapter.id }
|
val modifiedIndex = successState.chapters.indexOfFirst { it.chapter.id == download.chapter.id }
|
||||||
if (modifiedIndex >= 0) {
|
if (modifiedIndex < 0) return@updateSuccessState successState
|
||||||
|
|
||||||
val newChapters = successState.chapters.toMutableList().apply {
|
val newChapters = successState.chapters.toMutableList().apply {
|
||||||
val item = removeAt(modifiedIndex)
|
val item = removeAt(modifiedIndex)
|
||||||
.copy(downloadState = download.status, downloadProgress = download.progress)
|
.copy(downloadState = download.status, downloadProgress = download.progress)
|
||||||
add(modifiedIndex, item)
|
add(modifiedIndex, item)
|
||||||
}
|
}
|
||||||
_state.value = successState.copy(chapters = newChapters)
|
successState.copy(chapters = newChapters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -794,32 +800,33 @@ class MangaPresenter(
|
|||||||
*/
|
*/
|
||||||
private fun fetchChaptersFromSource(manualFetch: Boolean = false) {
|
private fun fetchChaptersFromSource(manualFetch: Boolean = false) {
|
||||||
if (fetchChaptersJob?.isActive == true) return
|
if (fetchChaptersJob?.isActive == true) return
|
||||||
val successState = successState ?: return
|
|
||||||
|
|
||||||
fetchChaptersJob = presenterScope.launchIO {
|
fetchChaptersJob = presenterScope.launchIO {
|
||||||
_state.value = successState.copy(isRefreshingChapter = true)
|
updateSuccessState { it.copy(isRefreshingChapter = true) }
|
||||||
try {
|
try {
|
||||||
if (successState.source !is MergedSource) {
|
successState?.let { successState ->
|
||||||
val chapters = successState.source.getChapterList(successState.manga.toMangaInfo())
|
if (successState.source !is MergedSource) {
|
||||||
.map { it.toSChapter() }
|
val chapters = successState.source.getChapterList(successState.manga.toMangaInfo())
|
||||||
|
.map { it.toSChapter() }
|
||||||
|
|
||||||
val (newChapters, _) = syncChaptersWithSource.await(
|
val (newChapters, _) = syncChaptersWithSource.await(
|
||||||
chapters,
|
chapters,
|
||||||
successState.manga,
|
successState.manga,
|
||||||
successState.source,
|
successState.source,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (manualFetch) {
|
if (manualFetch) {
|
||||||
val dbChapters = newChapters.map { it.toDbChapter() }
|
val dbChapters = newChapters.map { it.toDbChapter() }
|
||||||
downloadNewChapters(dbChapters)
|
downloadNewChapters(dbChapters)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
successState.source.fetchChaptersForMergedManga(successState.manga, manualFetch, true, dedupe)
|
||||||
|
Unit
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
successState.source.fetchChaptersForMergedManga(successState.manga, manualFetch, true, dedupe)
|
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
withUIContext { view?.onFetchChaptersError(e) }
|
withUIContext { view?.onFetchChaptersError(e) }
|
||||||
}
|
}
|
||||||
_state.value = successState.copy(isRefreshingChapter = false)
|
updateSuccessState { it.copy(isRefreshingChapter = false) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -926,26 +933,27 @@ class MangaPresenter(
|
|||||||
* @param chapters the list of chapters to delete.
|
* @param chapters the list of chapters to delete.
|
||||||
*/
|
*/
|
||||||
fun deleteChapters(chapters: List<DomainChapter>) {
|
fun deleteChapters(chapters: List<DomainChapter>) {
|
||||||
val successState = successState ?: return
|
|
||||||
launchIO {
|
launchIO {
|
||||||
val chapters2 = chapters.map { it.toDbChapter() }
|
val chapters2 = chapters.map { it.toDbChapter() }
|
||||||
try {
|
try {
|
||||||
val deletedIds = downloadManager
|
updateSuccessState { successState ->
|
||||||
.deleteChapters(chapters2, successState.manga.toDbManga(), successState.source)
|
val deletedIds = downloadManager
|
||||||
.map { it.id }
|
.deleteChapters(chapters2, successState.manga.toDbManga(), successState.source)
|
||||||
val deletedChapters = successState.chapters.filter { deletedIds.contains(it.chapter.id) }
|
.map { it.id }
|
||||||
if (deletedChapters.isEmpty()) return@launchIO
|
val deletedChapters = successState.chapters.filter { deletedIds.contains(it.chapter.id) }
|
||||||
|
if (deletedChapters.isEmpty()) return@updateSuccessState successState
|
||||||
|
|
||||||
// TODO: Don't do this fake status update
|
// TODO: Don't do this fake status update
|
||||||
val newChapters = successState.chapters.toMutableList().apply {
|
val newChapters = successState.chapters.toMutableList().apply {
|
||||||
deletedChapters.forEach {
|
deletedChapters.forEach {
|
||||||
val index = indexOf(it)
|
val index = indexOf(it)
|
||||||
val toAdd = removeAt(index)
|
val toAdd = removeAt(index)
|
||||||
.copy(downloadState = Download.State.NOT_DOWNLOADED, downloadProgress = 0)
|
.copy(downloadState = Download.State.NOT_DOWNLOADED, downloadProgress = 0)
|
||||||
add(index, toAdd)
|
add(index, toAdd)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
successState.copy(chapters = newChapters)
|
||||||
}
|
}
|
||||||
_state.value = successState.copy(chapters = newChapters)
|
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
logcat(LogPriority.ERROR, e)
|
logcat(LogPriority.ERROR, e)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user