Use Compose on BrowseSourceScreens (#7901)

(cherry picked from commit d4b764fa31)

# Conflicts:
#	app/src/main/java/eu/kanade/presentation/library/components/LibraryGridCover.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchController.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/browse/Pager.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceComfortableGridHolder.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceCompactGridHolder.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceHolder.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceItem.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceListHolder.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/latest/LatestUpdatesController.kt
#	app/src/main/res/layout/source_comfortable_grid_item.xml
#	app/src/main/res/layout/source_compact_grid_item.xml
#	app/src/main/res/menu/source_browse.xml
This commit is contained in:
Andreas
2022-08-31 20:41:35 +02:00
committed by Jobobby04
parent f4b75fc08a
commit 16ea8aa3b7
71 changed files with 2820 additions and 2536 deletions
@@ -1,12 +1,20 @@
package exh.md.follows
import android.os.Bundle
import android.view.Menu
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.core.os.bundleOf
import eu.kanade.tachiyomi.R
import eu.kanade.presentation.browse.BrowseMangadexFollowsScreen
import eu.kanade.presentation.browse.components.RemoveMangaDialog
import eu.kanade.presentation.components.ChangeCategoryDialog
import eu.kanade.presentation.components.DuplicateMangaDialog
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.ui.base.controller.pushController
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter
import eu.kanade.tachiyomi.ui.category.CategoryController
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.lang.launchIO
/**
* Controller that shows the latest manga from the catalogue. Inherit [BrowseSourceController].
@@ -19,22 +27,69 @@ class MangaDexFollowsController(bundle: Bundle) : BrowseSourceController(bundle)
),
)
override fun getTitle(): String? {
return view?.context?.getString(R.string.mangadex_follows)
}
override fun createPresenter(): BrowseSourcePresenter {
return MangaDexFollowsPresenter(args.getLong(SOURCE_ID_KEY))
}
override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu)
menu.findItem(R.id.action_search).isVisible = false
menu.findItem(R.id.action_open_in_web_view).isVisible = false
menu.findItem(R.id.action_settings).isVisible = false
@Composable
override fun ComposeContent() {
val scope = rememberCoroutineScope()
BrowseMangadexFollowsScreen(
presenter = presenter,
navigateUp = { router.popCurrentController() },
onDisplayModeChange = { presenter.displayMode = (it) },
onMangaClick = {
router.pushController(MangaController(it.id, true))
},
onMangaLongClick = { manga ->
scope.launchIO {
val duplicateManga = presenter.getDuplicateLibraryManga(manga)
when {
manga.favorite -> presenter.dialog = BrowseSourcePresenter.Dialog.RemoveManga(manga)
duplicateManga != null -> presenter.dialog = BrowseSourcePresenter.Dialog.AddDuplicateManga(manga, duplicateManga)
else -> presenter.addFavorite(manga)
}
}
},
)
val onDismissRequest = { presenter.dialog = null }
when (val dialog = presenter.dialog) {
is BrowseSourcePresenter.Dialog.AddDuplicateManga -> {
DuplicateMangaDialog(
onDismissRequest = onDismissRequest,
onConfirm = { presenter.addFavorite(dialog.manga) },
onOpenManga = { router.pushController(MangaController(dialog.duplicate.id)) },
duplicateFrom = presenter.getSourceOrStub(dialog.duplicate),
)
}
is BrowseSourcePresenter.Dialog.RemoveManga -> {
RemoveMangaDialog(
onDismissRequest = onDismissRequest,
onConfirm = {
presenter.changeMangaFavorite(dialog.manga)
},
)
}
is BrowseSourcePresenter.Dialog.ChangeMangaCategory -> {
ChangeCategoryDialog(
initialSelection = dialog.initialSelection,
onDismissRequest = onDismissRequest,
onEditCategories = {
router.pushController(CategoryController())
},
onConfirm = { include, _ ->
presenter.changeMangaFavorite(dialog.manga)
presenter.moveMangaToCategories(dialog.manga, include)
},
)
}
null -> {}
}
}
override fun initFilterSheet() {
// No-op: we don't allow filtering in latest
// No-op: we don't allow filtering in mangadex follows
}
}
@@ -1,14 +0,0 @@
package exh.md.follows
import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.ui.browse.source.browse.Pager
/**
* LatestUpdatesPager inherited from the general Pager.
*/
class MangaDexFollowsPager(val source: MangaDex) : Pager() {
override suspend fun requestNextPage() {
onPageReceived(source.fetchFollows(currentPage))
}
}
@@ -0,0 +1,15 @@
package exh.md.follows
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowsePagingSource
/**
* LatestUpdatesPager inherited from the general Pager.
*/
class MangaDexFollowsPagingSource(val source: MangaDex) : BrowsePagingSource() {
override suspend fun requestNextPage(currentPage: Int): MangasPage {
return source.fetchFollows(currentPage)
}
}
@@ -1,9 +1,16 @@
package exh.md.follows
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.paging.PagingSource
import eu.kanade.domain.manga.model.Manga
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter
import eu.kanade.tachiyomi.ui.browse.source.browse.Pager
import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.source.getMainSource
/**
@@ -11,8 +18,12 @@ import exh.source.getMainSource
*/
class MangaDexFollowsPresenter(sourceId: Long) : BrowseSourcePresenter(sourceId) {
override fun createPager(query: String, filters: FilterList): Pager {
val sourceAsMangaDex = source.getMainSource() as MangaDex
return MangaDexFollowsPager(sourceAsMangaDex)
override fun createPager(query: String, filters: FilterList): PagingSource<Long, Pair<SManga, RaisedSearchMetadata?>> {
return MangaDexFollowsPagingSource(source!!.getMainSource() as MangaDex)
}
@Composable
override fun getRaisedSearchMetadata(manga: Manga, initialMetadata: RaisedSearchMetadata?): State<RaisedSearchMetadata?> {
return remember { mutableStateOf(initialMetadata) }
}
}
@@ -1,13 +1,17 @@
package exh.md.similar
import android.os.Bundle
import android.view.Menu
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.core.os.bundleOf
import eu.kanade.domain.manga.model.Manga
import eu.kanade.presentation.browse.BrowseRecommendationsScreen
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.ui.base.controller.pushController
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter
import eu.kanade.tachiyomi.ui.manga.MangaController
/**
* Controller that shows the latest manga from the catalogue. Inherit [BrowseSourceController].
@@ -22,36 +26,28 @@ class MangaDexSimilarController(bundle: Bundle) : BrowseSourceController(bundle)
),
)
private val mangaTitle = args.getString(MANGA_TITLE)
override fun getTitle(): String? {
return view?.context?.getString(R.string.similar, mangaTitle)
}
private val mangaTitle = args.getString(MANGA_TITLE, "")
override fun createPresenter(): BrowseSourcePresenter {
return MangaDexSimilarPresenter(args.getLong(MANGA_ID), args.getLong(SOURCE_ID_KEY))
}
override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu)
menu.findItem(R.id.action_search).isVisible = false
menu.findItem(R.id.action_open_in_web_view).isVisible = false
menu.findItem(R.id.action_settings).isVisible = false
@Composable
override fun ComposeContent() {
BrowseRecommendationsScreen(
presenter = presenter,
navigateUp = { router.popCurrentController() },
title = stringResource(R.string.similar, mangaTitle),
onMangaClick = {
router.pushController(MangaController(it.id, true))
},
)
}
override fun initFilterSheet() {
// No-op: we don't allow filtering in similar
}
override fun onItemLongClick(position: Int) {
return
}
override fun onAddPageError(error: Throwable) {
super.onAddPageError(error)
binding.emptyView.show(activity!!.getString(R.string.similar_no_results))
}
companion object {
const val MANGA_ID = "manga_id"
const val MANGA_TITLE = "manga_title"
@@ -1,19 +1,20 @@
package exh.md.similar
import eu.kanade.domain.manga.model.Manga
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.MetadataMangasPage
import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowsePagingSource
import eu.kanade.tachiyomi.ui.browse.source.browse.NoResultsException
import eu.kanade.tachiyomi.ui.browse.source.browse.Pager
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
/**
* MangaDexSimilarPager inherited from the general Pager.
* MangaDexSimilarPagingSource inherited from the general Pager.
*/
class MangaDexSimilarPager(val manga: Manga, val source: MangaDex) : Pager() {
class MangaDexSimilarPagingSource(val manga: Manga, val source: MangaDex) : BrowsePagingSource() {
override suspend fun requestNextPage() {
override suspend fun requestNextPage(currentPage: Int): MangasPage {
val mangasPage = coroutineScope {
val similarPageDef = async { source.getMangaSimilar(manga.toSManga()) }
val relatedPageDef = async { source.getMangaRelated(manga.toSManga()) }
@@ -27,10 +28,6 @@ class MangaDexSimilarPager(val manga: Manga, val source: MangaDex) : Pager() {
)
}
if (mangasPage.mangas.isNotEmpty()) {
onPageReceived(mangasPage)
} else {
throw NoResultsException()
}
return mangasPage.takeIf { it.mangas.isNotEmpty() } ?: throw NoResultsException()
}
}
@@ -1,11 +1,18 @@
package exh.md.similar
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.paging.PagingSource
import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.model.Manga
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter
import eu.kanade.tachiyomi.ui.browse.source.browse.Pager
import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.source.getMainSource
import kotlinx.coroutines.runBlocking
import uy.kohesive.injekt.Injekt
@@ -22,9 +29,17 @@ class MangaDexSimilarPresenter(
var manga: Manga? = null
override fun createPager(query: String, filters: FilterList): Pager {
val sourceAsMangaDex = source.getMainSource() as MangaDex
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
this.manga = runBlocking { getManga.await(mangaId) }
return MangaDexSimilarPager(manga!!, sourceAsMangaDex)
}
override fun createPager(query: String, filters: FilterList): PagingSource<Long, Pair<SManga, RaisedSearchMetadata?>> {
return MangaDexSimilarPagingSource(manga!!, source!!.getMainSource() as MangaDex)
}
@Composable
override fun getRaisedSearchMetadata(manga: Manga, initialMetadata: RaisedSearchMetadata?): State<RaisedSearchMetadata?> {
return remember { mutableStateOf(initialMetadata) }
}
}
@@ -1,16 +1,14 @@
package exh.recs
import android.os.Bundle
import android.view.Menu
import android.view.View
import androidx.compose.runtime.Composable
import androidx.core.os.bundleOf
import eu.kanade.domain.manga.model.Manga
import eu.kanade.tachiyomi.R
import eu.kanade.presentation.browse.BrowseRecommendationsScreen
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.ui.base.controller.pushController
import eu.kanade.tachiyomi.ui.browse.source.SourcesController
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
import eu.kanade.tachiyomi.ui.browse.source.browse.SourceItem
/**
* Controller that shows the latest manga from the catalogue. Inherit [BrowseSourceController].
@@ -24,31 +22,26 @@ class RecommendsController(bundle: Bundle) : BrowseSourceController(bundle) {
),
)
override fun getTitle(): String? {
return (presenter as? RecommendsPresenter)?.manga?.title
}
override fun createPresenter(): RecommendsPresenter {
return RecommendsPresenter(args.getLong(MANGA_ID), args.getLong(SOURCE_ID_KEY))
}
override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu)
menu.findItem(R.id.action_search).isVisible = false
menu.findItem(R.id.action_open_in_web_view).isVisible = false
menu.findItem(R.id.action_settings).isVisible = false
@Composable
override fun ComposeContent() {
BrowseRecommendationsScreen(
presenter = presenter,
navigateUp = { router.popCurrentController() },
title = (presenter as RecommendsPresenter).manga!!.title,
onMangaClick = { manga ->
openSmartSearch(manga.ogTitle)
},
)
}
override fun initFilterSheet() {
// No-op: we don't allow filtering in recs
}
override fun onItemClick(view: View, position: Int): Boolean {
val item = adapter?.getItem(position) as? SourceItem ?: return false
openSmartSearch(item.manga.ogTitle)
return true
}
private fun openSmartSearch(title: String) {
val smartSearchConfig = SourcesController.SmartSearchConfig(title)
router.pushController(
@@ -60,10 +53,6 @@ class RecommendsController(bundle: Bundle) : BrowseSourceController(bundle) {
)
}
override fun onItemLongClick(position: Int) {
return
}
companion object {
const val MANGA_ID = "manga_id"
}
@@ -8,12 +8,13 @@ import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.parseAs
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowsePagingSource
import eu.kanade.tachiyomi.ui.browse.source.browse.NoResultsException
import eu.kanade.tachiyomi.ui.browse.source.browse.Pager
import eu.kanade.tachiyomi.util.lang.withIOContext
import eu.kanade.tachiyomi.util.system.logcat
import exh.util.MangaType
import exh.util.mangaType
import exh.util.nullIfEmpty
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
@@ -191,12 +192,12 @@ class Anilist : API("https://graphql.anilist.co/") {
}
}
open class RecommendsPager(
open class RecommendsPagingSource(
private val manga: Manga,
private val smart: Boolean = true,
private var preferredApi: API = API.MYANIMELIST,
) : Pager() {
override suspend fun requestNextPage() {
) : BrowsePagingSource() {
override suspend fun requestNextPage(currentPage: Int): MangasPage {
if (smart) preferredApi = if (manga.mangaType() != MangaType.TYPE_MANGA) API.ANILIST else preferredApi
val apiList = API_MAP.toList().sortedByDescending { it.first == preferredApi }
@@ -210,15 +211,9 @@ open class RecommendsPager(
logcat(LogPriority.ERROR, e) { key.toString() }
null
}
}.orEmpty()
}?.nullIfEmpty() ?: throw NoResultsException()
val mangasPage = MangasPage(recs, false)
if (mangasPage.mangas.isNotEmpty()) {
onPageReceived(mangasPage)
} else {
throw NoResultsException()
}
return MangasPage(recs, false)
}
companion object {
@@ -1,10 +1,13 @@
package exh.recs
import android.os.Bundle
import androidx.paging.PagingSource
import eu.kanade.domain.manga.interactor.GetManga
import eu.kanade.domain.manga.model.Manga
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourcePresenter
import eu.kanade.tachiyomi.ui.browse.source.browse.Pager
import exh.metadata.metadata.base.RaisedSearchMetadata
import kotlinx.coroutines.runBlocking
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@@ -20,8 +23,12 @@ class RecommendsPresenter(
var manga: Manga? = null
override fun createPager(query: String, filters: FilterList): Pager {
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
this.manga = runBlocking { getManga.await(mangaId) }
return RecommendsPager(manga!!)
}
override fun createPager(query: String, filters: FilterList): PagingSource<Long, Pair<SManga, RaisedSearchMetadata?>> {
return RecommendsPagingSource(manga!!)
}
}