feat: Open source settings from Browse & Manga screen (#799)

* Add a toolbar option to open source settings from manga info screen

This commit adds a new option in the manga info screen to directly open the extension settings.
This option will only be shown if the source is not a local or a stub source.

(cherry picked from commit 2304152368ecccfaa8fddaf0cfa2e82977a789db)

* rework Source settings on BrowseSource, SourceFeed & MangaScreen

* spotless

---------

Co-authored-by: Jery <jery99961@gmail.com>
This commit is contained in:
Cuong-Tran 2025-03-27 23:30:09 +07:00 committed by GitHub
parent dc7a2d9328
commit cc375e28e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 80 additions and 23 deletions

View file

@ -39,7 +39,9 @@ fun BrowseSourceToolbar(
navigateUp: () -> Unit,
onWebViewClick: () -> Unit,
onHelpClick: () -> Unit,
onSettingsClick: () -> Unit,
// KMK -->
onSettingsClick: (() -> Unit)?,
// KMK <--
onSearch: (String) -> Unit,
scrollBehavior: TopAppBarScrollBehavior? = null,
// KMK -->
@ -117,7 +119,9 @@ fun BrowseSourceToolbar(
}
}
// SY <--
if (isConfigurableSource) {
// KMK -->
onSettingsClick?.let {
// KMK <--
add(
AppBar.OverflowAction(
title = stringResource(MR.strings.action_settings),

View file

@ -198,6 +198,7 @@ fun MangaScreen(
// KMK -->
getMangaState: @Composable (Manga) -> State<Manga>,
onClickSourceSettingsClicked: (() -> Unit)?,
onRelatedMangasScreenClick: () -> Unit,
onRelatedMangaClick: (Manga) -> Unit,
onRelatedMangaLongClick: (Manga) -> Unit,
@ -263,6 +264,7 @@ fun MangaScreen(
onInvertSelection = onInvertSelection,
// KMK -->
getMangaState = getMangaState,
onClickSourceSettingsClicked = onClickSourceSettingsClicked,
onRelatedMangasScreenClick = onRelatedMangasScreenClick,
onRelatedMangaClick = onRelatedMangaClick,
onRelatedMangaLongClick = onRelatedMangaLongClick,
@ -321,6 +323,7 @@ fun MangaScreen(
onInvertSelection = onInvertSelection,
// KMK -->
getMangaState = getMangaState,
onClickSourceSettingsClicked = onClickSourceSettingsClicked,
onRelatedMangasScreenClick = onRelatedMangasScreenClick,
onRelatedMangaClick = onRelatedMangaClick,
onRelatedMangaLongClick = onRelatedMangaLongClick,
@ -396,6 +399,7 @@ private fun MangaScreenSmallImpl(
// KMK -->
getMangaState: @Composable ((Manga) -> State<Manga>),
onClickSourceSettingsClicked: (() -> Unit)?,
onRelatedMangasScreenClick: () -> Unit,
onRelatedMangaClick: (Manga) -> Unit,
onRelatedMangaLongClick: (Manga) -> Unit,
@ -476,6 +480,7 @@ private fun MangaScreenSmallImpl(
// SY -->
onClickEditInfo = onEditInfoClicked.takeIf { state.manga.favorite },
// KMK -->
onClickSourceSettings = onClickSourceSettingsClicked,
onClickRelatedMangas = onRelatedMangasScreenClick.takeIf {
!expandRelatedMangas &&
showRelatedMangasInOverflow &&
@ -849,6 +854,7 @@ private fun MangaScreenLargeImpl(
// KMK -->
getMangaState: @Composable ((Manga) -> State<Manga>),
onClickSourceSettingsClicked: (() -> Unit)?,
onRelatedMangasScreenClick: () -> Unit,
onRelatedMangaClick: (Manga) -> Unit,
onRelatedMangaLongClick: (Manga) -> Unit,
@ -921,6 +927,7 @@ private fun MangaScreenLargeImpl(
// SY -->
onClickEditInfo = onEditInfoClicked.takeIf { state.manga.favorite },
// KMK -->
onClickSourceSettings = onClickSourceSettingsClicked,
onClickRelatedMangas = onRelatedMangasScreenClick.takeIf {
!expandRelatedMangas &&
showRelatedMangasInOverflow &&

View file

@ -55,6 +55,7 @@ fun MangaToolbar(
onClickRecommend: (() -> Unit)?,
onClickMerge: (() -> Unit)?,
onClickMergedSettings: (() -> Unit)?,
onClickSourceSettings: (() -> Unit)?,
// SY <--
// For action mode
@ -226,6 +227,14 @@ fun MangaToolbar(
}
// SY <--
// KMK -->
if (onClickSourceSettings != null) {
add(
AppBar.OverflowAction(
title = stringResource(MR.strings.source_settings),
onClick = onClickSourceSettings,
),
)
}
if (isDebugBuildType) {
add(
AppBar.OverflowAction(

View file

@ -46,9 +46,11 @@ import eu.kanade.presentation.browse.components.SavedSearchDeleteDialog
import eu.kanade.presentation.category.components.ChangeCategoryDialog
import eu.kanade.presentation.components.BulkSelectionToolbar
import eu.kanade.presentation.manga.DuplicateMangaDialog
import eu.kanade.presentation.more.settings.screen.SettingsEhScreen
import eu.kanade.presentation.util.AssistContentScreen
import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.browse.AllowDuplicateDialog
import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
@ -62,6 +64,7 @@ import eu.kanade.tachiyomi.ui.manga.MangaScreen
import eu.kanade.tachiyomi.ui.webview.WebViewScreen
import eu.kanade.tachiyomi.util.system.toast
import exh.md.follows.MangaDexFollowsScreen
import exh.source.anyIs
import exh.source.isEhBasedSource
import exh.source.isMdBasedSource
import exh.ui.smartsearch.SmartSearchScreen
@ -174,7 +177,13 @@ data class BrowseSourceScreen(
// KMK -->
val mangaList = screenModel.mangaPagerFlowFlow.collectAsLazyPagingItems()
val isHentaiEnabled: Boolean = Injekt.get<UnsortedPreferences>().isHentaiEnabled().get()
val isConfigurableSource = screenModel.source.anyIs<ConfigurableSource>() ||
screenModel.source.isEhBasedSource() &&
isHentaiEnabled
// KMK <--
Scaffold(
topBar = {
Column(modifier = Modifier.background(MaterialTheme.colorScheme.surface)) {
@ -215,7 +224,17 @@ data class BrowseSourceScreen(
navigateUp = navigateUp,
onWebViewClick = onWebViewClick,
onHelpClick = onHelpClick,
onSettingsClick = { navigator.push(SourcePreferencesScreen(sourceId)) },
// KMK -->
onSettingsClick = {
when {
screenModel.source.isEhBasedSource() && isHentaiEnabled ->
navigator.push(SettingsEhScreen)
screenModel.source.anyIs<ConfigurableSource>() ->
navigator.push(SourcePreferencesScreen(sourceId))
else -> {}
}
}.takeIf { isConfigurableSource },
// KMK <--
onSearch = screenModel::search,
// KMK -->
toggleSelectionMode = bulkFavoriteScreenModel::toggleSelectionMode,

View file

@ -16,7 +16,6 @@ import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.core.util.ifSourcesLoaded
import eu.kanade.domain.source.model.installedExtension
import eu.kanade.presentation.browse.MissingSourceScreen
import eu.kanade.presentation.browse.SourceFeedOrderScreen
import eu.kanade.presentation.browse.SourceFeedScreen
@ -24,9 +23,10 @@ import eu.kanade.presentation.browse.SourceFeedUI
import eu.kanade.presentation.browse.components.FeedActionsDialog
import eu.kanade.presentation.browse.components.SourceFeedAddDialog
import eu.kanade.presentation.browse.components.SourceFeedDeleteDialog
import eu.kanade.presentation.more.settings.screen.SettingsEhScreen
import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.isLocalOrStub
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.browse.AddDuplicateMangaDialog
import eu.kanade.tachiyomi.ui.browse.AllowDuplicateDialog
@ -34,16 +34,18 @@ import eu.kanade.tachiyomi.ui.browse.BulkFavoriteScreenModel
import eu.kanade.tachiyomi.ui.browse.ChangeMangaCategoryDialog
import eu.kanade.tachiyomi.ui.browse.ChangeMangasCategoryDialog
import eu.kanade.tachiyomi.ui.browse.RemoveMangaDialog
import eu.kanade.tachiyomi.ui.browse.extension.details.ExtensionDetailsScreen
import eu.kanade.tachiyomi.ui.browse.extension.details.SourcePreferencesScreen
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen
import eu.kanade.tachiyomi.ui.browse.source.browse.SourceFilterDialog
import eu.kanade.tachiyomi.ui.manga.MangaScreen
import eu.kanade.tachiyomi.ui.webview.WebViewScreen
import eu.kanade.tachiyomi.util.system.toast
import exh.md.follows.MangaDexFollowsScreen
import exh.source.anyIs
import exh.source.isEhBasedSource
import exh.util.nullIfBlank
import tachiyomi.core.common.util.lang.launchIO
import tachiyomi.domain.UnsortedPreferences
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.source.interactor.GetRemoteManga
import tachiyomi.domain.source.model.SavedSearch
@ -51,7 +53,8 @@ import tachiyomi.domain.source.model.StubSource
import tachiyomi.i18n.kmk.KMR
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.screens.LoadingScreen
import tachiyomi.domain.source.model.Source as ModelSource
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class SourceFeedScreen(val sourceId: Long) : Screen() {
@ -88,6 +91,11 @@ class SourceFeedScreen(val sourceId: Long) : Screen() {
val bulkFavoriteState by bulkFavoriteScreenModel.state.collectAsState()
val showingFeedOrderScreen = rememberSaveable { mutableStateOf(false) }
val isHentaiEnabled: Boolean = Injekt.get<UnsortedPreferences>().isHentaiEnabled().get()
val isConfigurableSource = screenModel.source.anyIs<ConfigurableSource>() ||
screenModel.source.isEhBasedSource() &&
isHentaiEnabled
val haptic = LocalHapticFeedback.current
BackHandler(enabled = bulkFavoriteState.selectionMode || showingFeedOrderScreen.value) {
@ -151,23 +159,14 @@ class SourceFeedScreen(val sourceId: Long) : Screen() {
)
}.takeIf { screenModel.source is HttpSource },
onSourceSettingClick = {
val dummy = ModelSource(
sourceId,
"",
"",
supportsLatest = false,
isStub = false,
)
dummy.installedExtension?.let {
navigator.push(ExtensionDetailsScreen(it.pkgName))
when {
screenModel.source.isEhBasedSource() && isHentaiEnabled ->
navigator.push(SettingsEhScreen)
screenModel.source.anyIs<ConfigurableSource>() ->
navigator.push(SourcePreferencesScreen(screenModel.source.id))
else -> {}
}
}.takeIf {
!screenModel.source.isLocalOrStub() &&
!screenModel.source.isEhBasedSource() &&
screenModel.state.value.items
.filterIsInstance<SourceFeedUI.SourceSavedSearch>()
.isNotEmpty()
},
}.takeIf { isConfigurableSource },
onSortFeedClick = { showingFeedOrderScreen.value = true }
.takeIf {
screenModel.state.value.items

View file

@ -54,10 +54,12 @@ import eu.kanade.presentation.manga.components.DeleteChaptersDialog
import eu.kanade.presentation.manga.components.MangaCoverDialog
import eu.kanade.presentation.manga.components.ScanlatorFilterDialog
import eu.kanade.presentation.manga.components.SetIntervalDialog
import eu.kanade.presentation.more.settings.screen.SettingsEhScreen
import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.presentation.util.AssistContentScreen
import eu.kanade.presentation.util.Screen
import eu.kanade.presentation.util.isTabletUi
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.isLocalOrStub
import eu.kanade.tachiyomi.source.online.HttpSource
@ -68,6 +70,7 @@ import eu.kanade.tachiyomi.ui.browse.ChangeMangaCategoryDialog
import eu.kanade.tachiyomi.ui.browse.ChangeMangasCategoryDialog
import eu.kanade.tachiyomi.ui.browse.RemoveMangaDialog
import eu.kanade.tachiyomi.ui.browse.extension.ExtensionsScreen
import eu.kanade.tachiyomi.ui.browse.extension.details.SourcePreferencesScreen
import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationScreen
import eu.kanade.tachiyomi.ui.browse.source.SourcesScreen
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen
@ -86,7 +89,9 @@ import eu.kanade.tachiyomi.util.system.toast
import exh.pagepreview.PagePreviewScreen
import exh.recs.RecommendsScreen
import exh.source.MERGED_SOURCE_ID
import exh.source.anyIs
import exh.source.getMainSource
import exh.source.isEhBasedSource
import exh.ui.metadata.MetadataViewScreen
import exh.ui.smartsearch.SmartSearchScreen
import kotlinx.coroutines.CancellationException
@ -270,6 +275,11 @@ class MangaScreen(
val coverRatio = remember { mutableFloatStateOf(1f) }
val hazeState = remember { HazeState() }
val fullCoverBackground = MaterialTheme.colorScheme.surfaceTint.blend(MaterialTheme.colorScheme.surface)
val isHentaiEnabled: Boolean = Injekt.get<UnsortedPreferences>().isHentaiEnabled().get()
val isConfigurableSource = successState.source.anyIs<ConfigurableSource>() ||
successState.source.isEhBasedSource() &&
isHentaiEnabled
// KMK <--
MangaScreen(
@ -370,6 +380,15 @@ class MangaScreen(
onInvertSelection = screenModel::invertSelection,
// KMK -->
getMangaState = { screenModel.getManga(initialManga = it) },
onClickSourceSettingsClicked = {
when {
successState.source.isEhBasedSource() && isHentaiEnabled ->
navigator.push(SettingsEhScreen)
successState.source.anyIs<ConfigurableSource>() ->
navigator.push(SourcePreferencesScreen(successState.source.id))
else -> {}
}
}.takeIf { isConfigurableSource },
onRelatedMangasScreenClick = {
if (successState.isRelatedMangasFetched == null) {
scope.launchIO { screenModel.fetchRelatedMangasFromSource(onDemand = true) }