Merge Latest and Browse into one screen (#7921)

* Merge Latest and Browse into one

* Add back Latest button

* Change context to IO instead of launching a job

* Use loading screen when loading initial page

(cherry picked from commit cc6aef693e)

# Conflicts:
#	app/src/main/java/eu/kanade/data/source/SourceRepositoryImpl.kt
#	app/src/main/java/eu/kanade/presentation/browse/BrowseLatestScreen.kt
#	app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt
#	app/src/main/java/eu/kanade/presentation/browse/SourceSearchScreen.kt
#	app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt
#	app/src/main/java/eu/kanade/presentation/browse/components/BrowseLatestToolbar.kt
#	app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceComfortableGrid.kt
#	app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchController.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesTab.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowsePagingSource.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/latest/LatestUpdatesController.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/latest/LatestUpdatesPresenter.kt
This commit is contained in:
Andreas
2022-09-03 16:16:30 +02:00
committed by Jobobby04
parent 3ba39f6557
commit 99d5d8b91f
43 changed files with 483 additions and 561 deletions
@@ -0,0 +1,63 @@
package eu.kanade.data.source
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.MetadataMangasPage
import eu.kanade.tachiyomi.util.lang.awaitSingle
abstract class EHentaiPagingSource(source: CatalogueSource) : SourcePagingSource(source) {
private var lastMangaLink: String? = null
abstract suspend fun fetchNextPage(currentPage: Int): MangasPage
override suspend fun requestNextPage(currentPage: Int): MangasPage {
val lastMangaLink = lastMangaLink
val mangasPage = fetchNextPage(currentPage)
mangasPage.mangas.lastOrNull()?.let {
this.lastMangaLink = it.url
}
return if (lastMangaLink != null) {
val index = mangasPage.mangas.indexOfFirst { it.url == lastMangaLink }
if (index != -1) {
val lastIndex = mangasPage.mangas.size
val startIndex = (index + 1).coerceAtMost(mangasPage.mangas.lastIndex)
if (mangasPage is MetadataMangasPage) {
mangasPage.copy(
mangas = mangasPage.mangas.subList(startIndex, lastIndex),
mangasMetadata = mangasPage.mangasMetadata.subList(startIndex, lastIndex),
)
} else {
mangasPage.copy(
mangas = mangasPage.mangas.subList(startIndex, lastIndex),
)
}
} else {
mangasPage
}
} else {
mangasPage
}
}
}
class EHentaiSearchPagingSource(source: CatalogueSource, val query: String, val filters: FilterList) : EHentaiPagingSource(source) {
override suspend fun fetchNextPage(currentPage: Int): MangasPage {
return source.fetchSearchManga(currentPage, query, filters).awaitSingle()
}
}
class EHentaiPopularPagingSource(source: CatalogueSource) : EHentaiPagingSource(source) {
override suspend fun fetchNextPage(currentPage: Int): MangasPage {
return source.fetchPopularManga(currentPage).awaitSingle()
}
}
class EHentaiLatestPagingSource(source: CatalogueSource) : EHentaiPagingSource(source) {
override suspend fun fetchNextPage(currentPage: Int): MangasPage {
return source.fetchLatestUpdates(currentPage).awaitSingle()
}
}
@@ -0,0 +1,3 @@
package eu.kanade.data.source
class NoResultsException : Exception()
@@ -0,0 +1,72 @@
package eu.kanade.data.source
import androidx.paging.PagingState
import eu.kanade.domain.source.model.SourcePagingSourceType
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.MetadataMangasPage
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.lang.awaitSingle
import eu.kanade.tachiyomi.util.lang.withIOContext
import exh.metadata.metadata.base.RaisedSearchMetadata
abstract class SourcePagingSource(
protected val source: CatalogueSource,
) : SourcePagingSourceType() {
abstract suspend fun requestNextPage(currentPage: Int): MangasPage
override suspend fun load(params: LoadParams<Long>): LoadResult<Long, /*SY --> */ Pair<SManga, RaisedSearchMetadata?>/*SY <-- */> {
val page = params.key ?: 1
val mangasPage = try {
withIOContext {
requestNextPage(page.toInt())
.takeIf { it.mangas.isNotEmpty() }
?: throw NoResultsException()
}
} catch (e: Exception) {
return LoadResult.Error(e)
}
// SY -->
val metadata = if (mangasPage is MetadataMangasPage) {
mangasPage.mangasMetadata
} else emptyList()
// SY <--
return LoadResult.Page(
data = mangasPage.mangas
// SY -->
.mapIndexed { index, sManga -> sManga to metadata.getOrNull(index) },
// SY <--
prevKey = null,
nextKey = if (mangasPage.hasNextPage) page + 1 else null,
)
}
override fun getRefreshKey(state: PagingState<Long, /*SY --> */ Pair<SManga, RaisedSearchMetadata?>/*SY <-- */>): Long? {
return state.anchorPosition?.let { anchorPosition ->
val anchorPage = state.closestPageToPosition(anchorPosition)
anchorPage?.prevKey ?: anchorPage?.nextKey
}
}
}
class SourceSearchPagingSource(source: CatalogueSource, val query: String, val filters: FilterList) : SourcePagingSource(source) {
override suspend fun requestNextPage(currentPage: Int): MangasPage {
return source.fetchSearchManga(currentPage, query, filters).awaitSingle()
}
}
class SourcePopularPagingSource(source: CatalogueSource) : SourcePagingSource(source) {
override suspend fun requestNextPage(currentPage: Int): MangasPage {
return source.fetchPopularManga(currentPage).awaitSingle()
}
}
class SourceLatestPagingSource(source: CatalogueSource) : SourcePagingSource(source) {
override suspend fun requestNextPage(currentPage: Int): MangasPage {
return source.fetchLatestUpdates(currentPage).awaitSingle()
}
}
@@ -2,11 +2,15 @@ package eu.kanade.data.source
import eu.kanade.data.DatabaseHandler
import eu.kanade.domain.source.model.Source
import eu.kanade.domain.source.model.SourcePagingSourceType
import eu.kanade.domain.source.model.SourceWithCount
import eu.kanade.domain.source.repository.SourceRepository
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.FilterList
import exh.source.MERGED_SOURCE_ID
import exh.source.isEhBasedSource
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
@@ -50,4 +54,38 @@ class SourceRepositoryImpl(
}
}
}
override fun search(
sourceId: Long,
query: String,
filterList: FilterList,
): SourcePagingSourceType {
val source = sourceManager.get(sourceId) as CatalogueSource
// SY -->
if (source.isEhBasedSource()) {
return EHentaiSearchPagingSource(source, query, filterList)
}
// SY <--
return SourceSearchPagingSource(source, query, filterList)
}
override fun getPopular(sourceId: Long): SourcePagingSourceType {
val source = sourceManager.get(sourceId) as CatalogueSource
// SY -->
if (source.isEhBasedSource()) {
return EHentaiPopularPagingSource(source)
}
// SY <--
return SourcePopularPagingSource(source)
}
override fun getLatest(sourceId: Long): SourcePagingSourceType {
val source = sourceManager.get(sourceId) as CatalogueSource
// SY -->
if (source.isEhBasedSource()) {
return EHentaiLatestPagingSource(source)
}
// SY <--
return SourceLatestPagingSource(source)
}
}