diff --git a/app/src/main/java/eu/kanade/presentation/components/AppBar.kt b/app/src/main/java/eu/kanade/presentation/components/AppBar.kt index 255860e315..05206f476c 100644 --- a/app/src/main/java/eu/kanade/presentation/components/AppBar.kt +++ b/app/src/main/java/eu/kanade/presentation/components/AppBar.kt @@ -2,11 +2,7 @@ package eu.kanade.presentation.components import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.RowScope -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.WindowInsetsSides -import androidx.compose.foundation.layout.only -import androidx.compose.foundation.layout.systemBars -import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.Close @@ -18,6 +14,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SmallTopAppBar import androidx.compose.material3.Text import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue @@ -51,19 +48,63 @@ fun AppBar( // Banners downloadedOnlyMode: Boolean = false, incognitoMode: Boolean = false, + + scrollBehavior: TopAppBarScrollBehavior? = null, ) { val isActionMode by derivedStateOf { actionModeCounter > 0 } - val backgroundColor = if (isActionMode) { - TopAppBarDefaults.smallTopAppBarColors().containerColor(1f).value - } else { - MaterialTheme.colorScheme.surface - } + AppBar( + modifier = modifier, + titleContent = { + if (isActionMode) { + AppBarTitle(actionModeCounter.toString()) + } else { + AppBarTitle(title, subtitle) + } + }, + navigateUp = navigateUp, + navigationIcon = navigationIcon, + actions = { + if (isActionMode) { + actionModeActions() + } else { + actions() + } + }, + isActionMode = isActionMode, + onCancelActionMode = onCancelActionMode, + downloadedOnlyMode = downloadedOnlyMode, + incognitoMode = incognitoMode, + scrollBehavior = scrollBehavior, + ) +} + +@Composable +fun AppBar( + modifier: Modifier = Modifier, + // Title + titleContent: @Composable () -> Unit, + // Up button + navigateUp: (() -> Unit)? = null, + navigationIcon: ImageVector = Icons.Default.ArrowBack, + // Menu + actions: @Composable RowScope.() -> Unit = {}, + // Action mode + isActionMode: Boolean, + onCancelActionMode: () -> Unit = {}, + // Banners + downloadedOnlyMode: Boolean = false, + incognitoMode: Boolean = false, + + scrollBehavior: TopAppBarScrollBehavior? = null, +) { + val scrollFraction = if (isActionMode) 1f else scrollBehavior?.state?.overlappedFraction ?: 0f + val backgroundColor by TopAppBarDefaults.smallTopAppBarColors().containerColor(scrollFraction) Column( modifier = modifier.drawBehind { drawRect(backgroundColor) }, ) { SmallTopAppBar( - modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Top)), + modifier = Modifier.statusBarsPadding(), navigationIcon = { if (isActionMode) { IconButton(onClick = onCancelActionMode) { @@ -83,25 +124,14 @@ fun AppBar( } } }, - title = { - if (isActionMode) { - AppBarTitle(actionModeCounter.toString()) - } else { - AppBarTitle(title, subtitle) - } - }, - actions = { - if (isActionMode) { - actionModeActions() - } else { - actions() - } - }, + title = titleContent, + actions = actions, // Background handled by parent colors = TopAppBarDefaults.smallTopAppBarColors( containerColor = Color.Transparent, scrolledContainerColor = Color.Transparent, ), + scrollBehavior = scrollBehavior, ) if (downloadedOnlyMode) { diff --git a/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt b/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt index 84c4e21d04..8b28d7c796 100644 --- a/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt @@ -1,10 +1,18 @@ package eu.kanade.presentation.history -import androidx.compose.foundation.layout.safeDrawingPadding +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides +import androidx.compose.foundation.layout.asPaddingValues +import androidx.compose.foundation.layout.navigationBars +import androidx.compose.foundation.layout.only +import androidx.compose.foundation.layout.padding import androidx.compose.material3.Scaffold +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.paging.LoadState import eu.kanade.domain.history.model.HistoryWithRelations @@ -30,10 +38,21 @@ fun HistoryScreen( onClickResume: (HistoryWithRelations) -> Unit, ) { val context = LocalContext.current + val insetPaddingValue = WindowInsets.navigationBars + .only(WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom) + .asPaddingValues() + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) Scaffold( - modifier = Modifier.safeDrawingPadding(), + modifier = Modifier + .padding(insetPaddingValue) + .nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { - HistoryToolbar(state = presenter) + HistoryToolbar( + state = presenter, + incognitoMode = presenter.isIncognitoMode, + downloadedOnlyMode = presenter.isDownloadOnly, + scrollBehavior = scrollBehavior, + ) }, ) { contentPadding -> val items = presenter.getLazyHistory() diff --git a/app/src/main/java/eu/kanade/presentation/history/components/HistoryToolbar.kt b/app/src/main/java/eu/kanade/presentation/history/components/HistoryToolbar.kt index cd8ae5891c..00638ac33a 100644 --- a/app/src/main/java/eu/kanade/presentation/history/components/HistoryToolbar.kt +++ b/app/src/main/java/eu/kanade/presentation/history/components/HistoryToolbar.kt @@ -9,8 +9,7 @@ import androidx.compose.material.icons.outlined.Search import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.SmallTopAppBar -import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember @@ -19,6 +18,7 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.res.stringResource +import eu.kanade.presentation.components.AppBar import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.recent.history.HistoryPresenter import eu.kanade.tachiyomi.ui.recent.history.HistoryState @@ -27,17 +27,25 @@ import kotlinx.coroutines.delay @Composable fun HistoryToolbar( state: HistoryState, + scrollBehavior: TopAppBarScrollBehavior, + incognitoMode: Boolean, + downloadedOnlyMode: Boolean, ) { if (state.searchQuery == null) { HistoryRegularToolbar( onClickSearch = { state.searchQuery = "" }, onClickDelete = { state.dialog = HistoryPresenter.Dialog.DeleteAll }, + incognitoMode = incognitoMode, + downloadedOnlyMode = downloadedOnlyMode, + scrollBehavior = scrollBehavior, ) } else { HistorySearchToolbar( searchQuery = state.searchQuery!!, onChangeSearchQuery = { state.searchQuery = it }, onClickCloseSearch = { state.searchQuery = null }, + incognitoMode = incognitoMode, + downloadedOnlyMode = downloadedOnlyMode, ) } } @@ -46,11 +54,12 @@ fun HistoryToolbar( fun HistoryRegularToolbar( onClickSearch: () -> Unit, onClickDelete: () -> Unit, + incognitoMode: Boolean, + downloadedOnlyMode: Boolean, + scrollBehavior: TopAppBarScrollBehavior, ) { - SmallTopAppBar( - title = { - Text(text = stringResource(id = R.string.history)) - }, + AppBar( + title = stringResource(id = R.string.history), actions = { IconButton(onClick = onClickSearch) { Icon(Icons.Outlined.Search, contentDescription = "search") @@ -59,6 +68,9 @@ fun HistoryRegularToolbar( Icon(Icons.Outlined.DeleteSweep, contentDescription = "delete") } }, + downloadedOnlyMode = downloadedOnlyMode, + incognitoMode = incognitoMode, + scrollBehavior = scrollBehavior, ) } @@ -67,15 +79,12 @@ fun HistorySearchToolbar( searchQuery: String, onChangeSearchQuery: (String) -> Unit, onClickCloseSearch: () -> Unit, + incognitoMode: Boolean, + downloadedOnlyMode: Boolean, ) { val focusRequester = remember { FocusRequester.Default } - SmallTopAppBar( - navigationIcon = { - IconButton(onClick = onClickCloseSearch) { - Icon(Icons.Outlined.ArrowBack, contentDescription = "delete") - } - }, - title = { + AppBar( + titleContent = { BasicTextField( value = searchQuery, onValueChange = onChangeSearchQuery, @@ -87,6 +96,11 @@ fun HistorySearchToolbar( cursorBrush = SolidColor(MaterialTheme.colorScheme.onBackground), ) }, + navigationIcon = Icons.Outlined.ArrowBack, + navigateUp = onClickCloseSearch, + isActionMode = false, + downloadedOnlyMode = downloadedOnlyMode, + incognitoMode = incognitoMode, ) LaunchedEffect(focusRequester) { // TODO: https://issuetracker.google.com/issues/204502668 diff --git a/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt b/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt index 477d1063ab..ed61d0c78e 100644 --- a/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt @@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons @@ -20,9 +19,13 @@ import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.SelectAll import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.TopAppBarScrollBehavior +import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.stringResource @@ -57,7 +60,9 @@ fun UpdateScreen( onDownloadChapter: (List, ChapterDownloadAction) -> Unit, ) { val updatesListState = rememberLazyListState() - val insetPaddingValue = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal).asPaddingValues() + val insetPaddingValue = WindowInsets.navigationBars + .only(WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom) + .asPaddingValues() val internalOnBackPressed = { if (presenter.selectionMode) { @@ -76,9 +81,11 @@ fun UpdateScreen( } } + val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) Scaffold( modifier = Modifier - .padding(insetPaddingValue), + .padding(insetPaddingValue) + .nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { UpdatesAppBar( incognitoMode = presenter.isIncognitoMode, @@ -88,6 +95,7 @@ fun UpdateScreen( onSelectAll = { presenter.toggleAllSelection(true) }, onInvertSelection = { presenter.invertSelection() }, onCancelActionMode = { presenter.toggleAllSelection(false) }, + scrollBehavior = scrollBehavior, ) }, bottomBar = { @@ -185,6 +193,7 @@ fun UpdatesAppBar( onSelectAll: () -> Unit, onInvertSelection: () -> Unit, onCancelActionMode: () -> Unit, + scrollBehavior: TopAppBarScrollBehavior, ) { AppBar( modifier = modifier, @@ -215,6 +224,7 @@ fun UpdatesAppBar( }, downloadedOnlyMode = downloadedOnlyMode, incognitoMode = incognitoMode, + scrollBehavior = scrollBehavior, ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryPresenter.kt index d324b16e5e..04e9a5fa44 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recent/history/HistoryPresenter.kt @@ -22,6 +22,7 @@ import eu.kanade.domain.history.interactor.RemoveHistoryByMangaId import eu.kanade.domain.history.model.HistoryWithRelations import eu.kanade.presentation.history.HistoryUiModel import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchUI @@ -45,11 +46,16 @@ class HistoryPresenter( private val deleteHistoryTable: DeleteHistoryTable = Injekt.get(), private val removeHistoryById: RemoveHistoryById = Injekt.get(), private val removeHistoryByMangaId: RemoveHistoryByMangaId = Injekt.get(), + preferences: PreferencesHelper = Injekt.get(), ) : BasePresenter(), HistoryState by state { private val _events: Channel = Channel(Int.MAX_VALUE) val events: Flow = _events.receiveAsFlow() + val isDownloadOnly: Boolean by preferences.downloadedOnly().asState() + + val isIncognitoMode: Boolean by preferences.incognitoMode().asState() + @Composable fun getLazyHistory(): LazyPagingItems { val scope = rememberCoroutineScope()