Move app state banner to the very top (#8706)

This moves the banners to the root composable and so eliminates the need to
track the app states in every screen.
This commit is contained in:
Ivan Iskandar 2022-12-09 23:20:13 +07:00 committed by GitHub
parent a61e2799db
commit d97eab0328
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 217 additions and 295 deletions

View file

@ -182,6 +182,7 @@ dependencies {
implementation(compose.accompanist.flowlayout)
implementation(compose.accompanist.permissions)
implementation(compose.accompanist.themeadapter)
implementation(compose.accompanist.systemuicontroller)
implementation(androidx.paging.runtime)
implementation(androidx.paging.compose)

View file

@ -63,9 +63,6 @@ fun AppBar(
actionModeCounter: Int = 0,
onCancelActionMode: () -> Unit = {},
actionModeActions: @Composable RowScope.() -> Unit = {},
// Banners
downloadedOnlyMode: Boolean = false,
incognitoMode: Boolean = false,
scrollBehavior: TopAppBarScrollBehavior? = null,
) {
@ -93,8 +90,6 @@ fun AppBar(
},
isActionMode = isActionMode,
onCancelActionMode = onCancelActionMode,
downloadedOnlyMode = downloadedOnlyMode,
incognitoMode = incognitoMode,
scrollBehavior = scrollBehavior,
)
}
@ -112,9 +107,6 @@ fun AppBar(
// Action mode
isActionMode: Boolean = false,
onCancelActionMode: () -> Unit = {},
// Banners
downloadedOnlyMode: Boolean = false,
incognitoMode: Boolean = false,
scrollBehavior: TopAppBarScrollBehavior? = null,
) {
@ -150,8 +142,6 @@ fun AppBar(
),
scrollBehavior = scrollBehavior,
)
AppStateBanners(downloadedOnlyMode, incognitoMode)
}
}
@ -236,8 +226,6 @@ fun SearchToolbar(
onSearch: (String) -> Unit = {},
onClickCloseSearch: () -> Unit = { onChangeSearchQuery(null) },
actions: @Composable RowScope.() -> Unit = {},
incognitoMode: Boolean = false,
downloadedOnlyMode: Boolean = false,
scrollBehavior: TopAppBarScrollBehavior? = null,
visualTransformation: VisualTransformation = VisualTransformation.None,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
@ -326,8 +314,6 @@ fun SearchToolbar(
key("actions") { actions() }
},
isActionMode = false,
downloadedOnlyMode = downloadedOnlyMode,
incognitoMode = incognitoMode,
scrollBehavior = scrollBehavior,
)
LaunchedEffect(searchClickCount) {

View file

@ -3,8 +3,13 @@ package eu.kanade.presentation.components
import androidx.annotation.StringRes
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -37,24 +42,34 @@ fun AppStateBanners(
incognitoMode: Boolean,
modifier: Modifier = Modifier,
) {
val insets = WindowInsets.systemBars.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
Column(modifier = modifier) {
if (downloadedOnlyMode) {
DownloadedOnlyModeBanner()
DownloadedOnlyModeBanner(
modifier = Modifier.windowInsetsPadding(insets),
)
}
if (incognitoMode) {
IncognitoModeBanner()
IncognitoModeBanner(
modifier = if (!downloadedOnlyMode) {
Modifier.windowInsetsPadding(insets)
} else {
Modifier
},
)
}
}
}
@Composable
private fun DownloadedOnlyModeBanner() {
private fun DownloadedOnlyModeBanner(modifier: Modifier = Modifier) {
Text(
text = stringResource(R.string.label_downloaded_only),
modifier = Modifier
.background(color = MaterialTheme.colorScheme.tertiary)
.fillMaxWidth()
.padding(4.dp),
.padding(4.dp)
.then(modifier),
color = MaterialTheme.colorScheme.onTertiary,
textAlign = TextAlign.Center,
style = MaterialTheme.typography.labelMedium,
@ -62,13 +77,14 @@ private fun DownloadedOnlyModeBanner() {
}
@Composable
private fun IncognitoModeBanner() {
private fun IncognitoModeBanner(modifier: Modifier = Modifier) {
Text(
text = stringResource(R.string.pref_incognito_mode),
modifier = Modifier
.background(color = MaterialTheme.colorScheme.primary)
.fillMaxWidth()
.padding(4.dp),
.padding(4.dp)
.then(modifier),
color = MaterialTheme.colorScheme.onPrimary,
textAlign = TextAlign.Center,
style = MaterialTheme.typography.labelMedium,

View file

@ -29,8 +29,6 @@ fun TabbedScreen(
startIndex: Int? = null,
searchQuery: String? = null,
onChangeSearchQuery: (String?) -> Unit = {},
incognitoMode: Boolean,
downloadedOnlyMode: Boolean,
) {
val scope = rememberCoroutineScope()
val state = rememberPagerState()
@ -78,8 +76,6 @@ fun TabbedScreen(
}
}
AppStateBanners(downloadedOnlyMode, incognitoMode)
HorizontalPager(
count = tabs.size,
modifier = Modifier.fillMaxSize(),

View file

@ -26,8 +26,6 @@ import java.util.Date
fun HistoryScreen(
state: HistoryState,
snackbarHostState: SnackbarHostState,
incognitoMode: Boolean,
downloadedOnlyMode: Boolean,
onSearchQueryChange: (String?) -> Unit,
onClickCover: (mangaId: Long) -> Unit,
onClickResume: (mangaId: Long, chapterId: Long) -> Unit,
@ -47,8 +45,6 @@ fun HistoryScreen(
)
}
},
downloadedOnlyMode = downloadedOnlyMode,
incognitoMode = incognitoMode,
scrollBehavior = scrollBehavior,
)
},

View file

@ -45,8 +45,6 @@ fun LibraryContent(
getDisplayModeForPage: @Composable (Int) -> LibraryDisplayMode,
getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>,
getLibraryForPage: (Int) -> List<LibraryItem>,
isDownloadOnly: Boolean,
isIncognitoMode: Boolean,
) {
Column(
modifier = Modifier.padding(
@ -65,8 +63,6 @@ fun LibraryContent(
LibraryTabs(
categories = categories,
currentPageIndex = pagerState.currentPage,
isDownloadOnly = isDownloadOnly,
isIncognitoMode = isIncognitoMode,
getNumberOfMangaForCategory = getNumberOfMangaForCategory,
) { scope.launch { pagerState.animateScrollToPage(it) } }
}

View file

@ -8,7 +8,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
import eu.kanade.domain.category.model.Category
import eu.kanade.presentation.category.visualName
import eu.kanade.presentation.components.AppStateBanners
import eu.kanade.presentation.components.Divider
import eu.kanade.presentation.components.TabIndicator
import eu.kanade.presentation.components.TabText
@ -17,8 +16,6 @@ import eu.kanade.presentation.components.TabText
fun LibraryTabs(
categories: List<Category>,
currentPageIndex: Int,
isDownloadOnly: Boolean,
isIncognitoMode: Boolean,
getNumberOfMangaForCategory: (Category) -> Int?,
onTabItemClick: (Int) -> Unit,
) {
@ -47,7 +44,5 @@ fun LibraryTabs(
}
Divider()
AppStateBanners(isDownloadOnly, isIncognitoMode)
}
}

View file

@ -32,8 +32,6 @@ fun LibraryToolbar(
hasActiveFilters: Boolean,
selectedCount: Int,
title: LibraryToolbarTitle,
incognitoMode: Boolean,
downloadedOnlyMode: Boolean,
onClickUnselectAll: () -> Unit,
onClickSelectAll: () -> Unit,
onClickInvertSelection: () -> Unit,
@ -46,8 +44,6 @@ fun LibraryToolbar(
) = when {
selectedCount > 0 -> LibrarySelectionToolbar(
selectedCount = selectedCount,
incognitoMode = incognitoMode,
downloadedOnlyMode = downloadedOnlyMode,
onClickUnselectAll = onClickUnselectAll,
onClickSelectAll = onClickSelectAll,
onClickInvertSelection = onClickInvertSelection,
@ -55,8 +51,6 @@ fun LibraryToolbar(
else -> LibraryRegularToolbar(
title = title,
hasFilters = hasActiveFilters,
incognitoMode = incognitoMode,
downloadedOnlyMode = downloadedOnlyMode,
searchQuery = searchQuery,
onSearchQueryChange = onSearchQueryChange,
onClickFilter = onClickFilter,
@ -70,8 +64,6 @@ fun LibraryToolbar(
fun LibraryRegularToolbar(
title: LibraryToolbarTitle,
hasFilters: Boolean,
incognitoMode: Boolean,
downloadedOnlyMode: Boolean,
searchQuery: String?,
onSearchQueryChange: (String?) -> Unit,
onClickFilter: () -> Unit,
@ -123,8 +115,6 @@ fun LibraryRegularToolbar(
)
}
},
incognitoMode = incognitoMode,
downloadedOnlyMode = downloadedOnlyMode,
scrollBehavior = scrollBehavior,
)
}
@ -132,8 +122,6 @@ fun LibraryRegularToolbar(
@Composable
fun LibrarySelectionToolbar(
selectedCount: Int,
incognitoMode: Boolean,
downloadedOnlyMode: Boolean,
onClickUnselectAll: () -> Unit,
onClickSelectAll: () -> Unit,
onClickInvertSelection: () -> Unit,
@ -150,8 +138,6 @@ fun LibrarySelectionToolbar(
},
isActionMode = true,
onCancelActionMode = onClickUnselectAll,
incognitoMode = incognitoMode,
downloadedOnlyMode = downloadedOnlyMode,
)
}

View file

@ -238,8 +238,6 @@ private fun MangaScreenSmallImpl(
titleAlphaProvider = { animatedTitleAlpha },
backgroundAlphaProvider = { animatedBgAlpha },
hasFilters = state.manga.chaptersFiltered(),
incognitoMode = state.isIncognitoMode,
downloadedOnlyMode = state.isDownloadedOnlyMode,
onBackClicked = internalOnBackPressed,
onClickFilter = onFilterClicked,
onClickShare = onShareClicked,
@ -450,8 +448,6 @@ fun MangaScreenLargeImpl(
titleAlphaProvider = { if (chapters.fastAny { it.selected }) 1f else 0f },
backgroundAlphaProvider = { 1f },
hasFilters = state.manga.chaptersFiltered(),
incognitoMode = state.isIncognitoMode,
downloadedOnlyMode = state.isDownloadedOnlyMode,
onBackClicked = internalOnBackPressed,
onClickFilter = onFilterButtonClicked,
onClickShare = onShareClicked,

View file

@ -26,7 +26,6 @@ import androidx.compose.ui.draw.alpha
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.components.AppStateBanners
import eu.kanade.presentation.components.DownloadDropdownMenu
import eu.kanade.presentation.components.OverflowMenu
import eu.kanade.presentation.manga.DownloadAction
@ -40,8 +39,6 @@ fun MangaToolbar(
titleAlphaProvider: () -> Float,
backgroundAlphaProvider: () -> Float = titleAlphaProvider,
hasFilters: Boolean,
incognitoMode: Boolean,
downloadedOnlyMode: Boolean,
onBackClicked: () -> Unit,
onClickFilter: () -> Unit,
onClickShare: (() -> Unit)?,
@ -151,7 +148,5 @@ fun MangaToolbar(
.copy(alpha = if (isActionMode) 1f else backgroundAlphaProvider()),
),
)
AppStateBanners(downloadedOnlyMode, incognitoMode)
}
}

View file

@ -1,7 +1,13 @@
package eu.kanade.presentation.more
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.CloudOff
import androidx.compose.material.icons.outlined.GetApp
@ -18,8 +24,8 @@ import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import eu.kanade.presentation.components.AppStateBanners
import eu.kanade.presentation.components.Divider
import eu.kanade.presentation.components.Scaffold
import eu.kanade.presentation.components.ScrollbarLazyColumn
import eu.kanade.presentation.components.WarningBanner
import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
@ -45,125 +51,125 @@ fun MoreScreen(
) {
val uriHandler = LocalUriHandler.current
ScrollbarLazyColumn(
modifier = Modifier.systemBarsPadding(),
) {
if (isFDroid) {
Scaffold(
topBar = {
Column(
modifier = Modifier.windowInsetsPadding(
WindowInsets.systemBars.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
),
) {
if (isFDroid) {
WarningBanner(
textRes = R.string.fdroid_warning,
modifier = Modifier.clickable {
uriHandler.openUri("https://tachiyomi.org/help/faq/#how-do-i-migrate-from-the-f-droid-version")
},
)
}
}
},
) { contentPadding ->
ScrollbarLazyColumn(
modifier = Modifier.padding(contentPadding),
) {
item {
WarningBanner(
textRes = R.string.fdroid_warning,
modifier = Modifier.clickable {
uriHandler.openUri("https://tachiyomi.org/help/faq/#how-do-i-migrate-from-the-f-droid-version")
LogoHeader()
}
item {
SwitchPreferenceWidget(
title = stringResource(R.string.label_downloaded_only),
subtitle = stringResource(R.string.downloaded_only_summary),
icon = Icons.Outlined.CloudOff,
checked = downloadedOnly,
onCheckedChanged = onDownloadedOnlyChange,
)
}
item {
SwitchPreferenceWidget(
title = stringResource(R.string.pref_incognito_mode),
subtitle = stringResource(R.string.pref_incognito_mode_summary),
icon = ImageVector.vectorResource(R.drawable.ic_glasses_24dp),
checked = incognitoMode,
onCheckedChanged = onIncognitoModeChange,
)
}
item { Divider() }
item {
val downloadQueueState = downloadQueueStateProvider()
TextPreferenceWidget(
title = stringResource(R.string.label_download_queue),
subtitle = when (downloadQueueState) {
DownloadQueueState.Stopped -> null
is DownloadQueueState.Paused -> {
val pending = downloadQueueState.pending
if (pending == 0) {
stringResource(R.string.paused)
} else {
"${stringResource(R.string.paused)}${
pluralStringResource(
id = R.plurals.download_queue_summary,
count = pending,
pending,
)
}"
}
}
is DownloadQueueState.Downloading -> {
val pending = downloadQueueState.pending
pluralStringResource(id = R.plurals.download_queue_summary, count = pending, pending)
}
},
icon = Icons.Outlined.GetApp,
onPreferenceClick = onClickDownloadQueue,
)
}
item {
TextPreferenceWidget(
title = stringResource(R.string.categories),
icon = Icons.Outlined.Label,
onPreferenceClick = onClickCategories,
)
}
item {
TextPreferenceWidget(
title = stringResource(R.string.label_stats),
icon = Icons.Outlined.QueryStats,
onPreferenceClick = onClickStats,
)
}
item {
TextPreferenceWidget(
title = stringResource(R.string.label_backup),
icon = Icons.Outlined.SettingsBackupRestore,
onPreferenceClick = onClickBackupAndRestore,
)
}
item { Divider() }
item {
TextPreferenceWidget(
title = stringResource(R.string.label_settings),
icon = Icons.Outlined.Settings,
onPreferenceClick = onClickSettings,
)
}
item {
TextPreferenceWidget(
title = stringResource(R.string.pref_category_about),
icon = Icons.Outlined.Info,
onPreferenceClick = onClickAbout,
)
}
item {
TextPreferenceWidget(
title = stringResource(R.string.label_help),
icon = Icons.Outlined.HelpOutline,
onPreferenceClick = { uriHandler.openUri(Constants.URL_HELP) },
)
}
}
item {
LogoHeader()
}
item {
AppStateBanners(
downloadedOnlyMode = downloadedOnly,
incognitoMode = incognitoMode,
)
}
item {
SwitchPreferenceWidget(
title = stringResource(R.string.label_downloaded_only),
subtitle = stringResource(R.string.downloaded_only_summary),
icon = Icons.Outlined.CloudOff,
checked = downloadedOnly,
onCheckedChanged = onDownloadedOnlyChange,
)
}
item {
SwitchPreferenceWidget(
title = stringResource(R.string.pref_incognito_mode),
subtitle = stringResource(R.string.pref_incognito_mode_summary),
icon = ImageVector.vectorResource(R.drawable.ic_glasses_24dp),
checked = incognitoMode,
onCheckedChanged = onIncognitoModeChange,
)
}
item { Divider() }
item {
val downloadQueueState = downloadQueueStateProvider()
TextPreferenceWidget(
title = stringResource(R.string.label_download_queue),
subtitle = when (downloadQueueState) {
DownloadQueueState.Stopped -> null
is DownloadQueueState.Paused -> {
val pending = downloadQueueState.pending
if (pending == 0) {
stringResource(R.string.paused)
} else {
"${stringResource(R.string.paused)}${
pluralStringResource(
id = R.plurals.download_queue_summary,
count = pending,
pending,
)
}"
}
}
is DownloadQueueState.Downloading -> {
val pending = downloadQueueState.pending
pluralStringResource(id = R.plurals.download_queue_summary, count = pending, pending)
}
},
icon = Icons.Outlined.GetApp,
onPreferenceClick = onClickDownloadQueue,
)
}
item {
TextPreferenceWidget(
title = stringResource(R.string.categories),
icon = Icons.Outlined.Label,
onPreferenceClick = onClickCategories,
)
}
item {
TextPreferenceWidget(
title = stringResource(R.string.label_stats),
icon = Icons.Outlined.QueryStats,
onPreferenceClick = onClickStats,
)
}
item {
TextPreferenceWidget(
title = stringResource(R.string.label_backup),
icon = Icons.Outlined.SettingsBackupRestore,
onPreferenceClick = onClickBackupAndRestore,
)
}
item { Divider() }
item {
TextPreferenceWidget(
title = stringResource(R.string.label_settings),
icon = Icons.Outlined.Settings,
onPreferenceClick = onClickSettings,
)
}
item {
TextPreferenceWidget(
title = stringResource(R.string.pref_category_about),
icon = Icons.Outlined.Info,
onPreferenceClick = onClickAbout,
)
}
item {
TextPreferenceWidget(
title = stringResource(R.string.label_help),
icon = Icons.Outlined.HelpOutline,
onPreferenceClick = { uriHandler.openUri(Constants.URL_HELP) },
)
}
}
}

View file

@ -43,8 +43,6 @@ import kotlin.time.Duration.Companion.seconds
fun UpdateScreen(
state: UpdatesState,
snackbarHostState: SnackbarHostState,
incognitoMode: Boolean,
downloadedOnlyMode: Boolean,
lastUpdated: Long,
relativeTime: Int,
onClickCover: (UpdatesItem) -> Unit,
@ -65,8 +63,6 @@ fun UpdateScreen(
Scaffold(
topBar = { scrollBehavior ->
UpdatesAppBar(
incognitoMode = incognitoMode,
downloadedOnlyMode = downloadedOnlyMode,
onUpdateLibrary = { onUpdateLibrary() },
actionModeCounter = state.selected.size,
onSelectAll = { onSelectAll(true) },
@ -136,8 +132,6 @@ fun UpdateScreen(
@Composable
private fun UpdatesAppBar(
modifier: Modifier = Modifier,
incognitoMode: Boolean,
downloadedOnlyMode: Boolean,
onUpdateLibrary: () -> Unit,
// For action mode
actionModeCounter: Int,
@ -173,8 +167,6 @@ private fun UpdatesAppBar(
)
}
},
downloadedOnlyMode = downloadedOnlyMode,
incognitoMode = incognitoMode,
scrollBehavior = scrollBehavior,
)
}

View file

@ -9,13 +9,9 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import cafe.adriel.voyager.core.model.ScreenModel
import cafe.adriel.voyager.core.model.coroutineScope
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
import cafe.adriel.voyager.navigator.tab.TabOptions
import eu.kanade.core.prefs.asState
import eu.kanade.domain.base.BasePreferences
import eu.kanade.presentation.components.TabbedScreen
import eu.kanade.presentation.util.Tab
import eu.kanade.tachiyomi.R
@ -25,8 +21,6 @@ import eu.kanade.tachiyomi.ui.browse.migration.sources.migrateSourceTab
import eu.kanade.tachiyomi.ui.browse.source.sourcesTab
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.util.storage.DiskUtil
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
data class BrowseTab(
private val toExtensions: Boolean = false,
@ -47,7 +41,6 @@ data class BrowseTab(
@Composable
override fun Content() {
val context = LocalContext.current
val screenModel = rememberScreenModel { BrowseScreenModel() }
// Hoisted for extensions tab's search bar
val extensionsScreenModel = rememberScreenModel { ExtensionsScreenModel() }
@ -63,8 +56,6 @@ data class BrowseTab(
startIndex = 1.takeIf { toExtensions },
searchQuery = extensionsQuery,
onChangeSearchQuery = extensionsScreenModel::search,
incognitoMode = screenModel.isIncognitoMode,
downloadedOnlyMode = screenModel.isDownloadOnly,
)
// For local source
@ -75,10 +66,3 @@ data class BrowseTab(
}
}
}
private class BrowseScreenModel(
preferences: BasePreferences = Injekt.get(),
) : ScreenModel {
val isDownloadOnly: Boolean by preferences.downloadedOnly().asState(coroutineScope)
val isIncognitoMode: Boolean by preferences.incognitoMode().asState(coroutineScope)
}

View file

@ -43,7 +43,6 @@ import eu.kanade.domain.source.interactor.GetRemoteManga
import eu.kanade.presentation.browse.BrowseSourceContent
import eu.kanade.presentation.browse.components.BrowseSourceToolbar
import eu.kanade.presentation.browse.components.RemoveMangaDialog
import eu.kanade.presentation.components.AppStateBanners
import eu.kanade.presentation.components.ChangeCategoryDialog
import eu.kanade.presentation.components.Divider
import eu.kanade.presentation.components.DuplicateMangaDialog
@ -167,8 +166,6 @@ data class BrowseSourceScreen(
}
Divider()
AppStateBanners(screenModel.isDownloadOnly, screenModel.isIncognitoMode)
}
},
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },

View file

@ -17,7 +17,6 @@ import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.core.prefs.CheckboxState
import eu.kanade.core.prefs.asState
import eu.kanade.core.prefs.mapAsCheckboxState
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.category.interactor.GetCategories
import eu.kanade.domain.category.interactor.SetMangaCategories
import eu.kanade.domain.category.model.Category
@ -82,7 +81,6 @@ class BrowseSourceScreenModel(
private val sourceId: Long,
searchQuery: String?,
private val sourceManager: SourceManager = Injekt.get(),
preferences: BasePreferences = Injekt.get(),
sourcePreferences: SourcePreferences = Injekt.get(),
private val libraryPreferences: LibraryPreferences = Injekt.get(),
private val coverCache: CoverCache = Injekt.get(),
@ -103,9 +101,6 @@ class BrowseSourceScreenModel(
var displayMode by sourcePreferences.sourceDisplayMode().asState(coroutineScope)
val isDownloadOnly: Boolean by preferences.downloadedOnly().asState(coroutineScope)
val isIncognitoMode: Boolean by preferences.incognitoMode().asState(coroutineScope)
val source = sourceManager.get(sourceId) as CatalogueSource
/**

View file

@ -1,12 +1,9 @@
package eu.kanade.tachiyomi.ui.history
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.getValue
import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.core.prefs.asState
import eu.kanade.core.util.insertSeparators
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.chapter.model.Chapter
import eu.kanade.domain.history.interactor.GetHistory
import eu.kanade.domain.history.interactor.GetNextChapters
@ -37,15 +34,11 @@ class HistoryScreenModel(
private val getHistory: GetHistory = Injekt.get(),
private val getNextChapters: GetNextChapters = Injekt.get(),
private val removeHistory: RemoveHistory = Injekt.get(),
preferences: BasePreferences = Injekt.get(),
) : StateScreenModel<HistoryState>(HistoryState()) {
private val _events: Channel<Event> = Channel(Channel.UNLIMITED)
val events: Flow<Event> = _events.receiveAsFlow()
val isDownloadOnly: Boolean by preferences.downloadedOnly().asState(coroutineScope)
val isIncognitoMode: Boolean by preferences.incognitoMode().asState(coroutineScope)
init {
coroutineScope.launch {
state.map { it.searchQuery }

View file

@ -62,8 +62,6 @@ object HistoryTab : Tab {
HistoryScreen(
state = state,
snackbarHostState = snackbarHostState,
incognitoMode = screenModel.isIncognitoMode,
downloadedOnlyMode = screenModel.isDownloadOnly,
onSearchQueryChange = screenModel::updateSearchQuery,
onClickCover = { navigator.push(MangaScreen(it)) },
onClickResume = screenModel::getNextChapterForManga,

View file

@ -89,9 +89,6 @@ class LibraryScreenModel(
var activeCategoryIndex: Int by libraryPreferences.lastUsedCategory().asState(coroutineScope)
val isDownloadOnly: Boolean by preferences.downloadedOnly().asState(coroutineScope)
val isIncognitoMode: Boolean by preferences.incognitoMode().asState(coroutineScope)
init {
coroutineScope.launchIO {
combine(

View file

@ -112,8 +112,6 @@ object LibraryTab : Tab {
hasActiveFilters = state.hasActiveFilters,
selectedCount = state.selection.size,
title = title,
incognitoMode = !tabVisible && screenModel.isIncognitoMode,
downloadedOnlyMode = !tabVisible && screenModel.isDownloadOnly,
onClickUnselectAll = screenModel::clearSelection,
onClickSelectAll = { screenModel.selectAll(screenModel.activeCategoryIndex) },
onClickInvertSelection = { screenModel.invertSelection(screenModel.activeCategoryIndex) },
@ -197,10 +195,7 @@ object LibraryTab : Tab {
getNumberOfMangaForCategory = { state.getMangaCountForCategory(it) },
getDisplayModeForPage = { state.categories[it].display },
getColumnsForOrientation = { screenModel.getColumnsPreferenceForCurrentOrientation(it) },
getLibraryForPage = { state.getLibraryItemsByPage(it) },
isDownloadOnly = screenModel.isDownloadOnly,
isIncognitoMode = screenModel.isIncognitoMode,
)
) { state.getLibraryItemsByPage(it) }
}
}
}

View file

@ -10,6 +10,12 @@ import android.view.View
import android.view.Window
import android.widget.Toast
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.statusBars
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
@ -20,6 +26,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.core.animation.doOnEnd
@ -36,12 +43,14 @@ import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.NavigatorDisposeBehavior
import cafe.adriel.voyager.navigator.currentOrThrow
import cafe.adriel.voyager.transitions.ScreenTransition
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.category.model.Category
import eu.kanade.domain.library.service.LibraryPreferences
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.domain.ui.UiPreferences
import eu.kanade.presentation.components.AppStateBanners
import eu.kanade.presentation.util.Transition
import eu.kanade.presentation.util.collectAsState
import eu.kanade.tachiyomi.BuildConfig
@ -142,47 +151,73 @@ class MainActivity : BaseActivity() {
.launchIn(lifecycleScope)
setComposeContent {
Navigator(
screen = HomeScreen,
disposeBehavior = NavigatorDisposeBehavior(disposeNestedNavigators = false, disposeSteps = true),
) { navigator ->
if (navigator.size == 1) {
ConfirmExit()
val incognito by preferences.incognitoMode().collectAsState()
val download by preferences.downloadedOnly().collectAsState()
Column {
AppStateBanners(
downloadedOnlyMode = download,
incognitoMode = incognito,
)
val systemUiController = rememberSystemUiController()
val active = incognito || download
val useDarkIcons = if (isSystemInDarkTheme()) active else !active
LaunchedEffect(systemUiController, useDarkIcons) {
systemUiController.setStatusBarColor(
color = androidx.compose.ui.graphics.Color.Transparent,
darkIcons = useDarkIcons,
)
}
LaunchedEffect(navigator) {
this@MainActivity.navigator = navigator
if (savedInstanceState == null) {
// Set start screen
handleIntentAction(intent)
// Reset Incognito Mode on relaunch
preferences.incognitoMode().set(false)
Navigator(
screen = HomeScreen,
disposeBehavior = NavigatorDisposeBehavior(disposeNestedNavigators = false, disposeSteps = true),
) { navigator ->
if (navigator.size == 1) {
ConfirmExit()
}
}
// Shows current screen
ScreenTransition(navigator = navigator, transition = { Transition.OneWayFade })
LaunchedEffect(navigator) {
this@MainActivity.navigator = navigator
// Pop source-related screens when incognito mode is turned off
LaunchedEffect(Unit) {
preferences.incognitoMode().changes()
.drop(1)
.onEach {
if (!it) {
val currentScreen = navigator.lastItem
if (currentScreen is BrowseSourceScreen ||
(currentScreen is MangaScreen && currentScreen.fromSource)
) {
navigator.popUntilRoot()
if (savedInstanceState == null) {
// Set start screen
handleIntentAction(intent)
// Reset Incognito Mode on relaunch
preferences.incognitoMode().set(false)
}
}
// Consume insets already used by app state banners
val boxModifier = if (incognito || download) {
Modifier.consumeWindowInsets(WindowInsets.statusBars)
} else {
Modifier
}
Box(modifier = boxModifier) {
// Shows current screen
ScreenTransition(navigator = navigator, transition = { Transition.OneWayFade })
}
// Pop source-related screens when incognito mode is turned off
LaunchedEffect(Unit) {
preferences.incognitoMode().changes()
.drop(1)
.onEach {
if (!it) {
val currentScreen = navigator.lastItem
if (currentScreen is BrowseSourceScreen ||
(currentScreen is MangaScreen && currentScreen.fromSource)
) {
navigator.popUntilRoot()
}
}
}
}
.launchIn(this)
}
.launchIn(this)
}
CheckForUpdate()
CheckForUpdate()
}
}
var showChangelog by remember { mutableStateOf(didMigration && !BuildConfig.DEBUG) }

View file

@ -10,7 +10,6 @@ import eu.kanade.core.prefs.CheckboxState
import eu.kanade.core.prefs.mapAsCheckboxState
import eu.kanade.core.util.addOrRemove
import eu.kanade.data.chapter.NoChaptersException
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.category.interactor.GetCategories
import eu.kanade.domain.category.interactor.SetMangaCategories
import eu.kanade.domain.category.model.Category
@ -53,7 +52,6 @@ import eu.kanade.tachiyomi.util.lang.launchNonCancellable
import eu.kanade.tachiyomi.util.lang.toRelativeString
import eu.kanade.tachiyomi.util.lang.withIOContext
import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.preference.asHotFlow
import eu.kanade.tachiyomi.util.removeCovers
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
import eu.kanade.tachiyomi.util.system.logcat
@ -64,7 +62,6 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.isActive
@ -81,7 +78,6 @@ class MangaInfoScreenModel(
val context: Context,
val mangaId: Long,
private val isFromSource: Boolean,
basePreferences: BasePreferences = Injekt.get(),
private val downloadPreferences: DownloadPreferences = Injekt.get(),
private val libraryPreferences: LibraryPreferences = Injekt.get(),
private val uiPreferences: UiPreferences = Injekt.get(),
@ -130,17 +126,6 @@ class MangaInfoScreenModel(
mutableState.update { if (it is MangaScreenState.Success) func(it) else it }
}
private var incognitoMode = false
set(value) {
updateSuccessState { it.copy(isIncognitoMode = value) }
field = value
}
private var downloadedOnlyMode = false
set(value) {
updateSuccessState { it.copy(isDownloadedOnlyMode = value) }
field = value
}
init {
val toChapterItemsParams: List<Chapter>.(manga: Manga) -> List<ChapterItem> = { manga ->
toChapterItems(
@ -189,8 +174,6 @@ class MangaInfoScreenModel(
isFromSource = isFromSource,
chapters = chapters,
isRefreshingData = needRefreshInfo || needRefreshChapter,
isIncognitoMode = incognitoMode,
isDownloadedOnlyMode = downloadedOnlyMode,
dialog = null,
)
}
@ -210,14 +193,6 @@ class MangaInfoScreenModel(
// Initial loading finished
updateSuccessState { it.copy(isRefreshingData = false) }
}
basePreferences.incognitoMode()
.asHotFlow { incognitoMode = it }
.launchIn(coroutineScope)
basePreferences.downloadedOnly()
.asHotFlow { downloadedOnlyMode = it }
.launchIn(coroutineScope)
}
fun fetchAllFromSource(manualFetch: Boolean = true) {
@ -1037,8 +1012,6 @@ sealed class MangaScreenState {
val chapters: List<ChapterItem>,
val trackItems: List<TrackItem> = emptyList(),
val isRefreshingData: Boolean = false,
val isIncognitoMode: Boolean = false,
val isDownloadedOnlyMode: Boolean = false,
val dialog: MangaInfoScreenModel.Dialog? = null,
) : MangaScreenState() {

View file

@ -11,7 +11,6 @@ import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.core.prefs.asState
import eu.kanade.core.util.addOrRemove
import eu.kanade.core.util.insertSeparators
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.chapter.interactor.GetChapter
import eu.kanade.domain.chapter.interactor.SetReadStatus
import eu.kanade.domain.chapter.interactor.UpdateChapter
@ -62,16 +61,12 @@ class UpdatesScreenModel(
private val getChapter: GetChapter = Injekt.get(),
private val libraryPreferences: LibraryPreferences = Injekt.get(),
val snackbarHostState: SnackbarHostState = SnackbarHostState(),
basePreferences: BasePreferences = Injekt.get(),
uiPreferences: UiPreferences = Injekt.get(),
) : StateScreenModel<UpdatesState>(UpdatesState()) {
private val _events: Channel<Event> = Channel(Int.MAX_VALUE)
val events: Flow<Event> = _events.receiveAsFlow()
val isDownloadOnly: Boolean by basePreferences.downloadedOnly().asState(coroutineScope)
val isIncognitoMode: Boolean by basePreferences.incognitoMode().asState(coroutineScope)
val lastUpdated by libraryPreferences.libraryUpdateLastTimestamp().asState(coroutineScope)
val relativeTime: Int by uiPreferences.relativeTime().asState(coroutineScope)

View file

@ -56,8 +56,6 @@ object UpdatesTab : Tab {
UpdateScreen(
state = state,
snackbarHostState = screenModel.snackbarHostState,
incognitoMode = screenModel.isIncognitoMode,
downloadedOnlyMode = screenModel.isDownloadOnly,
lastUpdated = screenModel.lastUpdated,
relativeTime = screenModel.relativeTime,
onClickCover = { item -> navigator.push(MangaScreen(item.update.mangaId)) },

View file

@ -23,4 +23,5 @@ material-core = { module = "androidx.compose.material:material", version = "1.4.
accompanist-webview = { module = "com.google.accompanist:accompanist-webview", version.ref = "accompanist" }
accompanist-flowlayout = { module = "com.google.accompanist:accompanist-flowlayout", version.ref = "accompanist" }
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist" }
accompanist-themeadapter = { module = "com.google.accompanist:accompanist-themeadapter-material3", version.ref = "accompanist" }
accompanist-themeadapter = { module = "com.google.accompanist:accompanist-themeadapter-material3", version.ref = "accompanist" }
accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist" }