feat(incognito): add toggle for incognito mode in BrowseSource & SourceFeed (#902)

* feat(incognito): add `toggle for incognito` mode in BrowseSource & SourceFeed

* fix null check
This commit is contained in:
Cuong-Tran 2025-05-11 15:52:01 +07:00 committed by GitHub
parent a862bda9d5
commit 8a2b063cd9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 94 additions and 24 deletions

View file

@ -104,6 +104,7 @@ fun SourceFeedScreen(
// KMK -->
navigateUp: () -> Unit,
onWebViewClick: (() -> Unit)?,
onToggleIncognito: () -> Unit,
onSourceSettingClick: (() -> Unit?)?,
onSortFeedClick: (() -> Unit)?,
onLongClickManga: (Manga) -> Unit,
@ -145,6 +146,7 @@ fun SourceFeedScreen(
// KMK -->
navigateUp = navigateUp,
onWebViewClick = onWebViewClick,
onToggleIncognito = onToggleIncognito,
onSourceSettingClick = onSourceSettingClick,
onSortFeedClick = onSortFeedClick,
toggleSelectionMode = bulkFavoriteScreenModel::toggleSelectionMode,
@ -298,6 +300,7 @@ fun SourceFeedToolbar(
// KMK -->
navigateUp: () -> Unit,
onWebViewClick: (() -> Unit)?,
onToggleIncognito: () -> Unit,
onSourceSettingClick: (() -> Unit?)?,
onSortFeedClick: (() -> Unit)?,
toggleSelectionMode: () -> Unit,
@ -332,6 +335,15 @@ fun SourceFeedToolbar(
)
}
// KMK -->
add(
AppBar.OverflowAction(
title = stringResource(MR.strings.pref_incognito_mode),
onClick = onToggleIncognito,
),
)
// KMK <--
onSortFeedClick?.let {
add(
AppBar.OverflowAction(

View file

@ -4,7 +4,6 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ViewList
import androidx.compose.material.icons.automirrored.outlined.Help
import androidx.compose.material.icons.filled.ViewModule
import androidx.compose.material.icons.outlined.Public
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
@ -39,6 +38,7 @@ fun BrowseSourceToolbar(
onWebViewClick: () -> Unit,
onHelpClick: () -> Unit,
// KMK -->
onToggleIncognito: () -> Unit,
onSettingsClick: (() -> Unit)?,
// KMK <--
onSearch: (String) -> Unit,
@ -100,22 +100,20 @@ fun BrowseSourceToolbar(
)
}
} else {
if (isConfigurableSource && displayMode != null) {
add(
AppBar.OverflowAction(
title = stringResource(MR.strings.action_web_view),
onClick = onWebViewClick,
),
)
} else {
add(
AppBar.Action(
title = stringResource(MR.strings.action_web_view),
icon = Icons.Outlined.Public,
onClick = onWebViewClick,
),
)
}
// KMK -->
add(
AppBar.OverflowAction(
title = stringResource(MR.strings.pref_incognito_mode),
onClick = onToggleIncognito,
),
)
// KMK <--
add(
AppBar.OverflowAction(
title = stringResource(MR.strings.action_web_view),
onClick = onWebViewClick,
),
)
}
// SY <--
// KMK -->

View file

@ -227,6 +227,7 @@ data class BrowseSourceScreen(
onWebViewClick = onWebViewClick,
onHelpClick = onHelpClick,
// KMK -->
onToggleIncognito = screenModel::toggleIncognitoMode,
onSettingsClick = {
when {
screenModel.source.isEhBasedSource() && isHentaiEnabled ->

View file

@ -4,6 +4,7 @@ import android.content.res.Configuration
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.unit.dp
import androidx.paging.Pager
@ -18,18 +19,22 @@ import eu.kanade.core.preference.asState
import eu.kanade.domain.manga.interactor.UpdateManga
import eu.kanade.domain.source.interactor.GetExhSavedSearch
import eu.kanade.domain.source.interactor.GetIncognitoState
import eu.kanade.domain.source.interactor.ToggleIncognito
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.domain.track.interactor.AddTracks
import eu.kanade.domain.ui.UiPreferences
import eu.kanade.presentation.util.ioCoroutineScope
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.util.removeCovers
import exh.metadata.metadata.RaisedSearchMetadata
import exh.source.EH_PACKAGE
import exh.source.getMainSource
import exh.source.isEhBasedSource
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
@ -72,6 +77,7 @@ import tachiyomi.domain.source.interactor.GetRemoteManga
import tachiyomi.domain.source.interactor.InsertSavedSearch
import tachiyomi.domain.source.model.EXHSavedSearch
import tachiyomi.domain.source.model.SavedSearch
import tachiyomi.domain.source.model.StubSource
import tachiyomi.domain.source.repository.SourcePagingSource
import tachiyomi.domain.source.service.SourceManager
import tachiyomi.i18n.sy.SYMR
@ -101,6 +107,10 @@ open class BrowseSourceScreenModel(
private val updateManga: UpdateManga = Injekt.get(),
private val addTracks: AddTracks = Injekt.get(),
private val getIncognitoState: GetIncognitoState = Injekt.get(),
// KMK -->
private val toggleIncognito: ToggleIncognito = Injekt.get(),
private val extensionManager: ExtensionManager = Injekt.get(),
// KMK <--
// SY -->
unsortedPreferences: UnsortedPreferences = Injekt.get(),
@ -124,6 +134,10 @@ open class BrowseSourceScreenModel(
private val filterSerializer = FilterSerializer()
// SY <--
// KMK -->
var incognitoMode = mutableStateOf(getIncognitoState.await(source.id))
// KMK <--
init {
// KMK -->
screenModelScope.launch {
@ -155,10 +169,6 @@ open class BrowseSourceScreenModel(
}
}.join()
if (!getIncognitoState.await(source.id)) {
sourcePreferences.lastUsedSource().set(source.id)
}
// SY -->
val savedSearchId = savedSearch
val jsonFilters = filtersJson
@ -189,9 +199,31 @@ open class BrowseSourceScreenModel(
}
.launchIn(screenModelScope)
// SY <--
// KMK-->
getIncognitoState.subscribe(sourceId)
.onEach {
if (!it) sourcePreferences.lastUsedSource().set(source.id)
incognitoMode.value = it
}
.launchIn(screenModelScope)
// KMK <--
}
}
// KMK -->
fun toggleIncognitoMode() {
val packageName = when {
source is StubSource -> null
source.isEhBasedSource() -> EH_PACKAGE
else -> extensionManager.getExtensionPackage(sourceId)
}
packageName?.let {
toggleIncognito.await(it, !incognitoMode.value)
}
}
// KMK <--
/**
* Flow of Pager flow tied to [State.listing]
*/

View file

@ -148,6 +148,7 @@ class SourceFeedScreen(val sourceId: Long) : Screen() {
),
)
}.takeIf { screenModel.source is HttpSource },
onToggleIncognito = screenModel::toggleIncognitoMode,
onSourceSettingClick = {
when {
screenModel.source.isEhBasedSource() && isHentaiEnabled ->

View file

@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
@ -11,14 +12,18 @@ import dev.icerock.moko.resources.StringResource
import eu.kanade.core.preference.asState
import eu.kanade.domain.source.interactor.GetExhSavedSearch
import eu.kanade.domain.source.interactor.GetIncognitoState
import eu.kanade.domain.source.interactor.ToggleIncognito
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.domain.ui.UiPreferences
import eu.kanade.presentation.browse.SourceFeedUI
import eu.kanade.tachiyomi.extension.ExtensionManager
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.online.all.MangaDex
import eu.kanade.tachiyomi.ui.browse.feed.MaxFeedItems
import exh.source.EH_PACKAGE
import exh.source.getMainSource
import exh.source.isEhBasedSource
import exh.source.mangaDexSourceIds
import exh.util.nullIfBlank
import kotlinx.collections.immutable.ImmutableList
@ -51,6 +56,7 @@ import tachiyomi.domain.source.interactor.ReorderFeed
import tachiyomi.domain.source.model.EXHSavedSearch
import tachiyomi.domain.source.model.FeedSavedSearch
import tachiyomi.domain.source.model.SavedSearch
import tachiyomi.domain.source.model.StubSource
import tachiyomi.domain.source.service.SourceManager
import tachiyomi.i18n.kmk.KMR
import tachiyomi.i18n.sy.SYMR
@ -75,6 +81,8 @@ open class SourceFeedScreenModel(
// KMK -->
private val reorderFeed: ReorderFeed = Injekt.get(),
private val getIncognitoState: GetIncognitoState = Injekt.get(),
private val toggleIncognito: ToggleIncognito = Injekt.get(),
private val extensionManager: ExtensionManager = Injekt.get(),
sourcePreferences: SourcePreferences = Injekt.get(),
// KMK <--
) : StateScreenModel<SourceFeedState>(SourceFeedState()) {
@ -87,6 +95,10 @@ open class SourceFeedScreenModel(
val startExpanded by uiPreferences.expandFilters().asState(screenModelScope)
// KMK -->
var incognitoMode = mutableStateOf(getIncognitoState.await(source.id))
// KMK <--
init {
// KMK -->
screenModelScope.launch {
@ -104,9 +116,12 @@ open class SourceFeedScreenModel(
// KMK -->
reloadSavedSearches()
if (!getIncognitoState.await(source.id)) {
sourcePreferences.lastUsedSource().set(source.id)
}
getIncognitoState.subscribe(sourceId)
.onEach {
if (!it) sourcePreferences.lastUsedSource().set(source.id)
incognitoMode.value = it
}
.launchIn(screenModelScope)
// KMK <--
getFeedSavedSearchBySourceId.subscribe(source.id)
.onEach {
@ -123,6 +138,17 @@ open class SourceFeedScreenModel(
}
// KMK-->
fun toggleIncognitoMode() {
val packageName = when {
source is StubSource -> null
source.isEhBasedSource() -> EH_PACKAGE
else -> extensionManager.getExtensionPackage(sourceId)
}
packageName?.let {
toggleIncognito.await(it, !incognitoMode.value)
}
}
fun resetFilters() {
val source = source
if (source !is CatalogueSource) return