Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: unify recommendation screens and add more sources #1376

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
29 changes: 3 additions & 26 deletions app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ import eu.kanade.presentation.manga.components.SetIntervalDialog
import eu.kanade.presentation.util.AssistContentScreen
import eu.kanade.presentation.util.Screen
import eu.kanade.presentation.util.isTabletUi
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.isLocalOrStub
import eu.kanade.tachiyomi.source.online.HttpSource
Expand All @@ -59,13 +58,10 @@ import eu.kanade.tachiyomi.ui.webview.WebViewScreen
import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.toShareIntent
import eu.kanade.tachiyomi.util.system.toast
import exh.md.similar.MangaDexSimilarScreen
import exh.pagepreview.PagePreviewScreen
import exh.pref.DelegateSourcePreferences
import exh.recs.RecommendsScreen
import exh.source.MERGED_SOURCE_ID
import exh.source.getMainSource
import exh.source.isMdBasedSource
import exh.ui.ifSourcesLoaded
import exh.ui.metadata.MetadataViewScreen
import kotlinx.coroutines.CancellationException
Expand Down Expand Up @@ -214,7 +210,7 @@ class MangaScreen(
onMetadataViewerClicked = { openMetadataViewer(navigator, successState.manga) },
onEditInfoClicked = screenModel::showEditMangaInfoDialog,
onRecommendClicked = {
openRecommends(context, navigator, screenModel.source?.getMainSource(), successState.manga)
openRecommends(navigator, screenModel.source?.getMainSource(), successState.manga)
},
onMergedSettingsClicked = screenModel::showEditMergedSettingsDialog,
onMergeClicked = { openSmartSearch(navigator, successState.manga) },
Expand Down Expand Up @@ -550,28 +546,9 @@ class MangaScreen(
// EXH <--

// AZ -->
private fun openRecommends(context: Context, navigator: Navigator, source: Source?, manga: Manga) {
private fun openRecommends(navigator: Navigator, source: Source?, manga: Manga) {
source ?: return
if (source.isMdBasedSource() && Injekt.get<DelegateSourcePreferences>().delegateSources().get()) {
MaterialAlertDialogBuilder(context)
.setTitle(SYMR.strings.az_recommends.getString(context))
.setSingleChoiceItems(
arrayOf(
context.stringResource(SYMR.strings.mangadex_similar),
context.stringResource(SYMR.strings.community_recommendations),
),
-1,
) { dialog, index ->
dialog.dismiss()
when (index) {
0 -> navigator.push(MangaDexSimilarScreen(manga.id, source.id))
1 -> navigator.push(RecommendsScreen(manga.id, source.id))
}
}
.show()
} else if (source is CatalogueSource) {
navigator.push(RecommendsScreen(manga.id, source.id))
}
navigator.push(RecommendsScreen(manga.id, source.id))
}
// AZ <--
}
23 changes: 19 additions & 4 deletions app/src/main/java/exh/md/similar/MangaDexSimilarPagingSource.kt
Original file line number Diff line number Diff line change
@@ -1,24 +1,39 @@
package exh.md.similar

import dev.icerock.moko.resources.StringResource
import eu.kanade.domain.manga.model.toSManga
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.MetadataMangasPage
import eu.kanade.tachiyomi.source.online.all.MangaDex
import exh.recs.sources.RecommendationPagingSource
import exh.source.getMainSource
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import tachiyomi.data.source.NoResultsException
import tachiyomi.data.source.SourcePagingSource
import tachiyomi.domain.manga.model.Manga
import tachiyomi.i18n.sy.SYMR

/**
* MangaDexSimilarPagingSource inherited from the general Pager.
*/
class MangaDexSimilarPagingSource(val manga: Manga, val mangadex: MangaDex) : SourcePagingSource(mangadex) {
class MangaDexSimilarPagingSource(
manga: Manga,
private val mangaDex: MangaDex,
) : RecommendationPagingSource(mangaDex, manga) {

override val name: String
get() = "MangaDex"

override val category: StringResource
get() = SYMR.strings.similar_titles

override val associatedSourceId: Long
get() = mangaDex.getMainSource().id

override suspend fun requestNextPage(currentPage: Int): MangasPage {
val mangasPage = coroutineScope {
val similarPageDef = async { mangadex.getMangaSimilar(manga.toSManga()) }
val relatedPageDef = async { mangadex.getMangaRelated(manga.toSManga()) }
val similarPageDef = async { mangaDex.getMangaSimilar(manga.toSManga()) }
val relatedPageDef = async { mangaDex.getMangaRelated(manga.toSManga()) }
val similarPage = similarPageDef.await()
val relatedPage = relatedPageDef.await()

Expand Down
37 changes: 0 additions & 37 deletions app/src/main/java/exh/md/similar/MangaDexSimilarScreenModel.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
package exh.md.similar
package exh.recs

import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.presentation.browse.BrowseSourceContent
import eu.kanade.presentation.browse.components.BrowseSourceSimpleToolbar
import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.ui.browse.source.SourcesScreen
import eu.kanade.tachiyomi.ui.manga.MangaScreen
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import exh.ui.ifSourcesLoaded
import mihon.presentation.core.util.collectAsLazyPagingItems
import tachiyomi.domain.manga.model.Manga
import tachiyomi.i18n.sy.SYMR
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.screens.LoadingScreen

class MangaDexSimilarScreen(val mangaId: Long, val sourceId: Long) : Screen() {
class BrowseRecommendsScreen(
private val mangaId: Long,
private val sourceId: Long,
private val recommendationSourceName: String,
private val isExternalSource: Boolean,
) : Screen() {

@Composable
override fun Content() {
Expand All @@ -29,20 +36,37 @@ class MangaDexSimilarScreen(val mangaId: Long, val sourceId: Long) : Screen() {
return
}

val screenModel = rememberScreenModel { MangaDexSimilarScreenModel(mangaId, sourceId) }
val context = LocalContext.current
val navigator = LocalNavigator.currentOrThrow

val onMangaClick: (Manga) -> Unit = {
navigator.push(MangaScreen(it.id, true))
val screenModel = rememberScreenModel {
BrowseRecommendsScreenModel(mangaId, sourceId, recommendationSourceName)
}

val snackbarHostState = remember { SnackbarHostState() }

val onClickItem = { manga: Manga ->
navigator.push(
when (isExternalSource) {
true -> SourcesScreen(SourcesScreen.SmartSearchConfig(manga.ogTitle))
false -> MangaScreen(manga.id, true)
},
)
}

val onLongClickItem = { manga: Manga ->
when (isExternalSource) {
true -> WebViewActivity.newIntent(context, manga.url, title = manga.title).let(context::startActivity)
false -> onClickItem(manga)
}
}

Scaffold(
topBar = { scrollBehavior ->
val recSource = screenModel.recommendationSource

BrowseSourceSimpleToolbar(
navigateUp = navigator::pop,
title = stringResource(SYMR.strings.similar, screenModel.manga.title),
title = "${recSource.name} (${stringResource(recSource.category)})",
displayMode = screenModel.displayMode,
onDisplayModeChange = { screenModel.displayMode = it },
scrollBehavior = scrollBehavior,
Expand All @@ -63,8 +87,8 @@ class MangaDexSimilarScreen(val mangaId: Long, val sourceId: Long) : Screen() {
onWebViewClick = null,
onHelpClick = null,
onLocalSourceHelpClick = null,
onMangaClick = onMangaClick,
onMangaLongClick = onMangaClick,
onMangaClick = onClickItem,
onMangaLongClick = onLongClickItem,
)
}
}
Expand Down
32 changes: 32 additions & 0 deletions app/src/main/java/exh/recs/BrowseRecommendsScreenModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package exh.recs

import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel
import exh.recs.sources.RecommendationPagingSource
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.runBlocking
import tachiyomi.domain.manga.interactor.GetManga
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get

class BrowseRecommendsScreenModel(
val mangaId: Long,
sourceId: Long,
private val recommendationSourceName: String,
private val getManga: GetManga = Injekt.get(),
) : BrowseSourceScreenModel(sourceId, null) {

val manga = runBlocking { getManga.await(mangaId) }!!

val recommendationSource: RecommendationPagingSource
get() = RecommendationPagingSource.createSources(manga, source as CatalogueSource).first {
it::class.qualifiedName == recommendationSourceName
}

override fun createSourcePagingSource(query: String, filters: FilterList) = recommendationSource

init {
mutableState.update { it.copy(filterable = false) }
}
}
87 changes: 42 additions & 45 deletions app/src/main/java/exh/recs/RecommendsScreen.kt
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
package exh.recs

import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.LocalContext
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.presentation.browse.BrowseSourceContent
import eu.kanade.presentation.browse.components.BrowseSourceSimpleToolbar
import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.ui.browse.source.SourcesScreen
import eu.kanade.tachiyomi.ui.manga.MangaScreen
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import exh.recs.components.RecommendsScreen
import exh.ui.ifSourcesLoaded
import mihon.presentation.core.util.collectAsLazyPagingItems
import tachiyomi.domain.manga.model.Manga
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.screens.LoadingScreen

class RecommendsScreen(val mangaId: Long, val sourceId: Long) : Screen() {
Expand All @@ -28,48 +25,48 @@ class RecommendsScreen(val mangaId: Long, val sourceId: Long) : Screen() {
return
}

val screenModel = rememberScreenModel { RecommendsScreenModel(mangaId, sourceId) }
val context = LocalContext.current
val navigator = LocalNavigator.currentOrThrow

val onMangaClick: (Manga) -> Unit = { manga ->
openSmartSearch(navigator, manga.ogTitle)
val screenModel = rememberScreenModel {
RecommendsScreenModel(mangaId = mangaId, sourceId = sourceId)
}
val state by screenModel.state.collectAsState()

val snackbarHostState = remember { SnackbarHostState() }

Scaffold(
topBar = { scrollBehavior ->
BrowseSourceSimpleToolbar(
navigateUp = navigator::pop,
title = screenModel.manga.title,
displayMode = screenModel.displayMode,
onDisplayModeChange = { screenModel.displayMode = it },
scrollBehavior = scrollBehavior,
)
},
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
) { paddingValues ->
BrowseSourceContent(
source = screenModel.source,
mangaList = screenModel.mangaPagerFlowFlow.collectAsLazyPagingItems(),
columns = screenModel.getColumnsPreference(LocalConfiguration.current.orientation),
// SY -->
ehentaiBrowseDisplayMode = false,
// SY <--
displayMode = screenModel.displayMode,
snackbarHostState = snackbarHostState,
contentPadding = paddingValues,
onWebViewClick = null,
onHelpClick = null,
onLocalSourceHelpClick = null,
onMangaClick = onMangaClick,
onMangaLongClick = onMangaClick,
val onClickItem = { manga: Manga ->
navigator.push(
when (manga.source) {
-1L -> SourcesScreen(SourcesScreen.SmartSearchConfig(manga.ogTitle))
else -> MangaScreen(manga.id, true)
},
)
}
}

private fun openSmartSearch(navigator: Navigator, title: String) {
val smartSearchConfig = SourcesScreen.SmartSearchConfig(title)
navigator.push(SourcesScreen(smartSearchConfig))
val onLongClickItem = { manga: Manga ->
when (manga.source) {
-1L -> WebViewActivity.newIntent(context, manga.url, title = manga.title).let(context::startActivity)
else -> onClickItem(manga)
}
}

RecommendsScreen(
manga = screenModel.manga,
state = state,
navigateUp = navigator::pop,
getManga = @Composable { manga: Manga -> screenModel.getManga(manga) },
onClickSource = { pagingSource ->
// Pass class name of paging source as screens need to be serializable
navigator.push(
BrowseRecommendsScreen(
mangaId,
sourceId,
pagingSource::class.qualifiedName!!,
pagingSource.associatedSourceId == null,
),
)
},
onClickItem = onClickItem,
onLongClickItem = onLongClickItem,
)
}
}
Loading
Loading