manga-refresh-state (#8090)
(cherry picked from commit 98a4f6cccb85d39fb37c95aa99da71bb7abfd134) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt
This commit is contained in:
parent
1f444185da
commit
ca36adca29
@ -24,4 +24,8 @@ class GetMangaWithChapters(
|
|||||||
suspend fun awaitManga(id: Long): Manga {
|
suspend fun awaitManga(id: Long): Manga {
|
||||||
return mangaRepository.getMangaById(id)
|
return mangaRepository.getMangaById(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun awaitChapters(id: Long): List<Chapter> {
|
||||||
|
return chapterRepository.getChapterByMangaId(id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -369,7 +369,7 @@ private fun MangaScreenSmallImpl(
|
|||||||
val topPadding = contentPadding.calculateTopPadding()
|
val topPadding = contentPadding.calculateTopPadding()
|
||||||
|
|
||||||
SwipeRefresh(
|
SwipeRefresh(
|
||||||
state = rememberSwipeRefreshState(state.isRefreshingInfo || state.isRefreshingChapter),
|
state = rememberSwipeRefreshState(state.isRefreshingData),
|
||||||
onRefresh = onRefresh,
|
onRefresh = onRefresh,
|
||||||
swipeEnabled = !chapters.any { it.selected },
|
swipeEnabled = !chapters.any { it.selected },
|
||||||
indicatorPadding = contentPadding,
|
indicatorPadding = contentPadding,
|
||||||
@ -565,7 +565,7 @@ fun MangaScreenLargeImpl(
|
|||||||
val insetPadding = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal).asPaddingValues()
|
val insetPadding = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal).asPaddingValues()
|
||||||
val (topBarHeight, onTopBarHeightChanged) = remember { mutableStateOf(0) }
|
val (topBarHeight, onTopBarHeightChanged) = remember { mutableStateOf(0) }
|
||||||
SwipeRefresh(
|
SwipeRefresh(
|
||||||
state = rememberSwipeRefreshState(state.isRefreshingInfo || state.isRefreshingChapter),
|
state = rememberSwipeRefreshState(state.isRefreshingData),
|
||||||
onRefresh = onRefresh,
|
onRefresh = onRefresh,
|
||||||
swipeEnabled = !chapters.any { it.selected },
|
swipeEnabled = !chapters.any { it.selected },
|
||||||
indicatorPadding = PaddingValues(
|
indicatorPadding = PaddingValues(
|
||||||
|
@ -69,6 +69,7 @@ import eu.kanade.tachiyomi.util.chapter.getChapterSort
|
|||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import eu.kanade.tachiyomi.util.lang.launchNonCancellable
|
import eu.kanade.tachiyomi.util.lang.launchNonCancellable
|
||||||
import eu.kanade.tachiyomi.util.lang.toRelativeString
|
import eu.kanade.tachiyomi.util.lang.toRelativeString
|
||||||
|
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||||
import eu.kanade.tachiyomi.util.preference.asHotFlow
|
import eu.kanade.tachiyomi.util.preference.asHotFlow
|
||||||
import eu.kanade.tachiyomi.util.removeCovers
|
import eu.kanade.tachiyomi.util.removeCovers
|
||||||
@ -109,6 +110,7 @@ import kotlinx.coroutines.flow.launchIn
|
|||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.supervisorScope
|
import kotlinx.coroutines.supervisorScope
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@ -129,7 +131,6 @@ class MangaPresenter(
|
|||||||
val isFromSource: Boolean,
|
val isFromSource: Boolean,
|
||||||
val smartSearched: Boolean,
|
val smartSearched: Boolean,
|
||||||
private val basePreferences: BasePreferences = Injekt.get(),
|
private val basePreferences: BasePreferences = Injekt.get(),
|
||||||
private val uiPreferences: UiPreferences = Injekt.get(),
|
|
||||||
private val downloadPreferences: DownloadPreferences = Injekt.get(),
|
private val downloadPreferences: DownloadPreferences = Injekt.get(),
|
||||||
private val libraryPreferences: LibraryPreferences = Injekt.get(),
|
private val libraryPreferences: LibraryPreferences = Injekt.get(),
|
||||||
private val trackManager: TrackManager = Injekt.get(),
|
private val trackManager: TrackManager = Injekt.get(),
|
||||||
@ -137,6 +138,7 @@ class MangaPresenter(
|
|||||||
private val downloadManager: DownloadManager = Injekt.get(),
|
private val downloadManager: DownloadManager = Injekt.get(),
|
||||||
private val getMangaAndChapters: GetMangaWithChapters = Injekt.get(),
|
private val getMangaAndChapters: GetMangaWithChapters = Injekt.get(),
|
||||||
// SY -->
|
// SY -->
|
||||||
|
private val uiPreferences: UiPreferences = Injekt.get(),
|
||||||
private val readerPreferences: ReaderPreferences = Injekt.get(),
|
private val readerPreferences: ReaderPreferences = Injekt.get(),
|
||||||
private val getManga: GetManga = Injekt.get(),
|
private val getManga: GetManga = Injekt.get(),
|
||||||
private val setMangaFilteredScanlators: SetMangaFilteredScanlators = Injekt.get(),
|
private val setMangaFilteredScanlators: SetMangaFilteredScanlators = Injekt.get(),
|
||||||
@ -172,9 +174,6 @@ class MangaPresenter(
|
|||||||
private val successState: MangaScreenState.Success?
|
private val successState: MangaScreenState.Success?
|
||||||
get() = state.value as? MangaScreenState.Success
|
get() = state.value as? MangaScreenState.Success
|
||||||
|
|
||||||
private var fetchMangaJob: Job? = null
|
|
||||||
private var fetchChaptersJob: Job? = null
|
|
||||||
|
|
||||||
private var observeDownloadsStatusJob: Job? = null
|
private var observeDownloadsStatusJob: Job? = null
|
||||||
private var observeDownloadsPageJob: Job? = null
|
private var observeDownloadsPageJob: Job? = null
|
||||||
|
|
||||||
@ -246,44 +245,26 @@ class MangaPresenter(
|
|||||||
override fun onCreate(savedState: Bundle?) {
|
override fun onCreate(savedState: Bundle?) {
|
||||||
super.onCreate(savedState)
|
super.onCreate(savedState)
|
||||||
|
|
||||||
presenterScope.launchIO {
|
val toChapterItemsParams: List<DomainChapter>.(manga: DomainManga /* SY --> */, mergedData: MergedMangaData? /* SY <-- */) -> List<ChapterItem> = { manga, /* SY --> */mergedData /* SY <-- */ ->
|
||||||
val manga = getMangaAndChapters.awaitManga(mangaId)
|
val uiPreferences = Injekt.get<UiPreferences>()
|
||||||
|
toChapterItems(
|
||||||
if (!manga.favorite) {
|
context = view?.activity ?: Injekt.get<Application>(),
|
||||||
setMangaDefaultChapterFlags.await(manga)
|
manga = manga,
|
||||||
}
|
// SY -->
|
||||||
|
dateRelativeTime = if (manga.isEhBasedManga()) 0 else uiPreferences.relativeTime().get(),
|
||||||
// Show what we have earlier.
|
dateFormat = if (manga.isEhBasedManga()) {
|
||||||
// Defaults set by the block above won't apply until next update but it doesn't matter
|
MetadataUtil.EX_DATE_FORMAT
|
||||||
// since we don't have any chapter yet.
|
} else {
|
||||||
_state.update {
|
UiPreferences.dateFormat(uiPreferences.dateFormat().get())
|
||||||
val source = sourceManager.getOrStub(manga.source)
|
},
|
||||||
MangaScreenState.Success(
|
mergedData = mergedData,
|
||||||
manga = manga,
|
alwaysShowReadingProgress = readerPreferences.preserveReadingPosition().get() && manga.isEhBasedManga(),
|
||||||
source = source,
|
// SY <--
|
||||||
isFromSource = isFromSource,
|
)
|
||||||
trackingAvailable = trackManager.hasLoggedServices(),
|
}
|
||||||
chapters = emptyList(),
|
|
||||||
isRefreshingChapter = true,
|
|
||||||
isIncognitoMode = incognitoMode,
|
|
||||||
isDownloadedOnlyMode = downloadedOnlyMode,
|
|
||||||
dialog = null,
|
|
||||||
// SY -->
|
|
||||||
showRecommendationsInOverflow = uiPreferences.recommendsInOverflow().get(),
|
|
||||||
showMergeInOverflow = uiPreferences.mergeInOverflow().get(),
|
|
||||||
showMergeWithAnother = smartSearched,
|
|
||||||
mergedData = null,
|
|
||||||
meta = null,
|
|
||||||
pagePreviewsState = if (source.getMainSource() is PagePreviewSource) {
|
|
||||||
getPagePreviews(manga, source)
|
|
||||||
PagePreviewState.Loading
|
|
||||||
} else {
|
|
||||||
PagePreviewState.Unused
|
|
||||||
},
|
|
||||||
// SY <--
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// For UI changes
|
||||||
|
presenterScope.launch {
|
||||||
getMangaAndChapters.subscribe(mangaId)
|
getMangaAndChapters.subscribe(mangaId)
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
// SY -->
|
// SY -->
|
||||||
@ -352,27 +333,12 @@ class MangaPresenter(
|
|||||||
state.copy(mergedData = mergedData)
|
state.copy(mergedData = mergedData)
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
.collectLatest { (manga, chapters, flatMetadata, mergedData) ->
|
.collectLatest { (manga, chapters /* SY --> */, flatMetadata, mergedData /* SY <-- */) ->
|
||||||
val chapterItems = chapters.toChapterItems(
|
val chapterItems = chapters.toChapterItemsParams(manga /* SY --> */, mergedData /* SY <-- */)
|
||||||
context = view?.activity ?: Injekt.get<Application>(),
|
|
||||||
manga = manga,
|
|
||||||
// SY -->
|
|
||||||
dateRelativeTime = if (manga.isEhBasedManga()) 0 else uiPreferences.relativeTime().get(),
|
|
||||||
dateFormat = if (manga.isEhBasedManga()) {
|
|
||||||
MetadataUtil.EX_DATE_FORMAT
|
|
||||||
} else {
|
|
||||||
UiPreferences.dateFormat(uiPreferences.dateFormat().get())
|
|
||||||
},
|
|
||||||
mergedData = mergedData,
|
|
||||||
alwaysShowReadingProgress = readerPreferences.preserveReadingPosition().get() && manga.isEhBasedManga(),
|
|
||||||
// SY <--
|
|
||||||
)
|
|
||||||
updateSuccessState {
|
updateSuccessState {
|
||||||
it.copy(
|
it.copy(
|
||||||
manga = manga,
|
manga = manga,
|
||||||
chapters = chapterItems,
|
chapters = chapterItems,
|
||||||
isRefreshingChapter = false,
|
|
||||||
isRefreshingInfo = false,
|
|
||||||
// SY -->
|
// SY -->
|
||||||
meta = raiseMetadata(flatMetadata, it.source),
|
meta = raiseMetadata(flatMetadata, it.source),
|
||||||
mergedData = mergedData,
|
mergedData = mergedData,
|
||||||
@ -380,15 +346,7 @@ class MangaPresenter(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
observeTrackers()
|
|
||||||
observeTrackingCount()
|
|
||||||
observeDownloads()
|
observeDownloads()
|
||||||
|
|
||||||
if (!manga.initialized) {
|
|
||||||
fetchAllFromSource(manualFetch = false)
|
|
||||||
} else if (chapterItems.isEmpty()) {
|
|
||||||
fetchChaptersFromSource()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,11 +357,88 @@ class MangaPresenter(
|
|||||||
basePreferences.downloadedOnly()
|
basePreferences.downloadedOnly()
|
||||||
.asHotFlow { downloadedOnlyMode = it }
|
.asHotFlow { downloadedOnlyMode = it }
|
||||||
.launchIn(presenterScope)
|
.launchIn(presenterScope)
|
||||||
|
|
||||||
|
// This block runs once on create
|
||||||
|
presenterScope.launchIO {
|
||||||
|
val manga = getMangaAndChapters.awaitManga(mangaId)
|
||||||
|
// SY -->
|
||||||
|
val chapters = (if (manga.source == MERGED_SOURCE_ID) getMergedChapterByMangaId.await(mangaId) else getMangaAndChapters.awaitChapters(mangaId))
|
||||||
|
.toChapterItemsParams(manga, null)
|
||||||
|
val mergedData = getMergedReferencesById.await(mangaId).let { references ->
|
||||||
|
MergedMangaData(
|
||||||
|
references,
|
||||||
|
getMergedMangaById.await(mangaId).associateBy { it.id },
|
||||||
|
references.map { it.mangaSourceId }.distinct()
|
||||||
|
.map { sourceManager.getOrStub(it) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val meta = getFlatMetadata.await(mangaId)
|
||||||
|
// SY <--
|
||||||
|
|
||||||
|
if (!manga.favorite) {
|
||||||
|
setMangaDefaultChapterFlags.await(manga)
|
||||||
|
}
|
||||||
|
|
||||||
|
val needRefreshInfo = !manga.initialized
|
||||||
|
val needRefreshChapter = chapters.isEmpty()
|
||||||
|
|
||||||
|
// Show what we have earlier.
|
||||||
|
_state.update {
|
||||||
|
val source = sourceManager.getOrStub(manga.source)
|
||||||
|
MangaScreenState.Success(
|
||||||
|
manga = manga,
|
||||||
|
source = source,
|
||||||
|
isFromSource = isFromSource,
|
||||||
|
trackingAvailable = trackManager.hasLoggedServices(),
|
||||||
|
chapters = chapters,
|
||||||
|
isRefreshingData = needRefreshInfo || needRefreshChapter,
|
||||||
|
isIncognitoMode = incognitoMode,
|
||||||
|
isDownloadedOnlyMode = downloadedOnlyMode,
|
||||||
|
dialog = null,
|
||||||
|
// SY -->
|
||||||
|
showRecommendationsInOverflow = uiPreferences.recommendsInOverflow().get(),
|
||||||
|
showMergeInOverflow = uiPreferences.mergeInOverflow().get(),
|
||||||
|
showMergeWithAnother = smartSearched,
|
||||||
|
mergedData = mergedData,
|
||||||
|
meta = raiseMetadata(meta, source),
|
||||||
|
pagePreviewsState = if (source.getMainSource() is PagePreviewSource) {
|
||||||
|
getPagePreviews(manga, source)
|
||||||
|
PagePreviewState.Loading
|
||||||
|
} else {
|
||||||
|
PagePreviewState.Unused
|
||||||
|
},
|
||||||
|
// SY <--
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start observe tracking since it only needs mangaId
|
||||||
|
observeTrackers()
|
||||||
|
observeTrackingCount()
|
||||||
|
|
||||||
|
// Fetch info-chapters when needed
|
||||||
|
if (presenterScope.isActive) {
|
||||||
|
val fetchFromSourceTasks = listOf(
|
||||||
|
async { if (needRefreshInfo) fetchMangaFromSource() },
|
||||||
|
async { if (needRefreshChapter) fetchChaptersFromSource() },
|
||||||
|
)
|
||||||
|
fetchFromSourceTasks.awaitAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial loading finished
|
||||||
|
updateSuccessState { it.copy(isRefreshingData = false) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fetchAllFromSource(manualFetch: Boolean = true) {
|
fun fetchAllFromSource(manualFetch: Boolean = true) {
|
||||||
fetchMangaFromSource(manualFetch)
|
presenterScope.launch {
|
||||||
fetchChaptersFromSource(manualFetch)
|
updateSuccessState { it.copy(isRefreshingData = true) }
|
||||||
|
val fetchFromSourceTasks = listOf(
|
||||||
|
async { fetchMangaFromSource(manualFetch) },
|
||||||
|
async { fetchChaptersFromSource(manualFetch) },
|
||||||
|
)
|
||||||
|
fetchFromSourceTasks.awaitAll()
|
||||||
|
updateSuccessState { it.copy(isRefreshingData = false) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manga info - start
|
// Manga info - start
|
||||||
@ -411,10 +446,8 @@ class MangaPresenter(
|
|||||||
/**
|
/**
|
||||||
* Fetch manga information from source.
|
* Fetch manga information from source.
|
||||||
*/
|
*/
|
||||||
private fun fetchMangaFromSource(manualFetch: Boolean = false) {
|
private suspend fun fetchMangaFromSource(manualFetch: Boolean = false) {
|
||||||
if (fetchMangaJob?.isActive == true) return
|
withIOContext {
|
||||||
fetchMangaJob = presenterScope.launchIO {
|
|
||||||
updateSuccessState { it.copy(isRefreshingInfo = true) }
|
|
||||||
try {
|
try {
|
||||||
successState?.let {
|
successState?.let {
|
||||||
val networkManga = it.source.getMangaDetails(it.manga.toSManga())
|
val networkManga = it.source.getMangaDetails(it.manga.toSManga())
|
||||||
@ -424,7 +457,6 @@ class MangaPresenter(
|
|||||||
this@MangaPresenter.xLogE("Error getting manga details", e)
|
this@MangaPresenter.xLogE("Error getting manga details", e)
|
||||||
withUIContext { view?.onFetchMangaInfoError(e) }
|
withUIContext { view?.onFetchMangaInfoError(e) }
|
||||||
}
|
}
|
||||||
updateSuccessState { it.copy(isRefreshingInfo = false) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -462,7 +494,7 @@ class MangaPresenter(
|
|||||||
ogStatus = status ?: 0,
|
ogStatus = status ?: 0,
|
||||||
)
|
)
|
||||||
(sourceManager.get(LocalSource.ID) as LocalSource).updateMangaInfo(manga.toSManga())
|
(sourceManager.get(LocalSource.ID) as LocalSource).updateMangaInfo(manga.toSManga())
|
||||||
launchIO {
|
presenterScope.launchNonCancellable {
|
||||||
updateManga.await(
|
updateManga.await(
|
||||||
MangaUpdate(
|
MangaUpdate(
|
||||||
manga.id,
|
manga.id,
|
||||||
@ -560,7 +592,7 @@ class MangaPresenter(
|
|||||||
while (existingManga != null) {
|
while (existingManga != null) {
|
||||||
if (existingManga.favorite) {
|
if (existingManga.favorite) {
|
||||||
throw IllegalArgumentException(context.getString(R.string.merge_duplicate))
|
throw IllegalArgumentException(context.getString(R.string.merge_duplicate))
|
||||||
} else if (!existingManga.favorite) {
|
} else {
|
||||||
withContext(NonCancellable) {
|
withContext(NonCancellable) {
|
||||||
existingManga?.id?.let {
|
existingManga?.id?.let {
|
||||||
deleteByMergeId.await(it)
|
deleteByMergeId.await(it)
|
||||||
@ -633,7 +665,7 @@ class MangaPresenter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun updateMergeSettings(mergedMangaReferences: List<MergedMangaReference>) {
|
fun updateMergeSettings(mergedMangaReferences: List<MergedMangaReference>) {
|
||||||
launchIO {
|
presenterScope.launchNonCancellable {
|
||||||
if (mergedMangaReferences.isNotEmpty()) {
|
if (mergedMangaReferences.isNotEmpty()) {
|
||||||
updateMergedSettings.awaitAll(
|
updateMergedSettings.awaitAll(
|
||||||
mergedMangaReferences.map {
|
mergedMangaReferences.map {
|
||||||
@ -995,10 +1027,8 @@ class MangaPresenter(
|
|||||||
/**
|
/**
|
||||||
* Requests an updated list of chapters from the source.
|
* Requests an updated list of chapters from the source.
|
||||||
*/
|
*/
|
||||||
private fun fetchChaptersFromSource(manualFetch: Boolean = false) {
|
private suspend fun fetchChaptersFromSource(manualFetch: Boolean = false) {
|
||||||
if (fetchChaptersJob?.isActive == true) return
|
withIOContext {
|
||||||
fetchChaptersJob = presenterScope.launchIO {
|
|
||||||
updateSuccessState { it.copy(isRefreshingChapter = true) }
|
|
||||||
try {
|
try {
|
||||||
successState?.let { successState ->
|
successState?.let { successState ->
|
||||||
if (successState.source !is MergedSource) {
|
if (successState.source !is MergedSource) {
|
||||||
@ -1021,7 +1051,6 @@ class MangaPresenter(
|
|||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
withUIContext { view?.onFetchChaptersError(e) }
|
withUIContext { view?.onFetchChaptersError(e) }
|
||||||
}
|
}
|
||||||
updateSuccessState { it.copy(isRefreshingChapter = false) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1623,8 +1652,7 @@ sealed class MangaScreenState {
|
|||||||
val chapters: List<ChapterItem>,
|
val chapters: List<ChapterItem>,
|
||||||
val trackingAvailable: Boolean = false,
|
val trackingAvailable: Boolean = false,
|
||||||
val trackingCount: Int = 0,
|
val trackingCount: Int = 0,
|
||||||
val isRefreshingInfo: Boolean = false,
|
val isRefreshingData: Boolean = false,
|
||||||
val isRefreshingChapter: Boolean = false,
|
|
||||||
val isIncognitoMode: Boolean = false,
|
val isIncognitoMode: Boolean = false,
|
||||||
val isDownloadedOnlyMode: Boolean = false,
|
val isDownloadedOnlyMode: Boolean = false,
|
||||||
val dialog: MangaPresenter.Dialog? = null,
|
val dialog: MangaPresenter.Dialog? = null,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user