diff --git a/app/src/main/java/eu/kanade/presentation/components/AdaptiveSheet.kt b/app/src/main/java/eu/kanade/presentation/components/AdaptiveSheet.kt index 40455667c7..cf44c49c2a 100644 --- a/app/src/main/java/eu/kanade/presentation/components/AdaptiveSheet.kt +++ b/app/src/main/java/eu/kanade/presentation/components/AdaptiveSheet.kt @@ -3,6 +3,9 @@ package eu.kanade.presentation.components import androidx.activity.compose.BackHandler import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.with import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.Orientation @@ -13,6 +16,7 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.asPaddingValues +import androidx.compose.foundation.layout.consumedWindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.offset @@ -48,6 +52,10 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.Velocity import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.core.lifecycle.DisposableEffectIgnoringConfiguration +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.Navigator +import cafe.adriel.voyager.transitions.ScreenTransition import eu.kanade.presentation.util.isTabletUi import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collectLatest @@ -62,6 +70,49 @@ private val SheetAnimationSpec = tween(durationMillis = SheetAnimationDur private const val ScrimAnimationDuration = 350 private val ScrimAnimationSpec = tween(durationMillis = ScrimAnimationDuration) +@Composable +fun NavigatorAdaptiveSheet( + screen: Screen, + tonalElevation: Dp = 1.dp, + enableSwipeDismiss: (Navigator) -> Boolean = { true }, + onDismissRequest: () -> Unit, +) { + Navigator( + screen = screen, + content = { sheetNavigator -> + AdaptiveSheet( + tonalElevation = tonalElevation, + enableSwipeDismiss = enableSwipeDismiss(sheetNavigator), + onDismissRequest = onDismissRequest, + ) { + ScreenTransition( + navigator = sheetNavigator, + transition = { + fadeIn(animationSpec = tween(220, delayMillis = 90)) with + fadeOut(animationSpec = tween(90)) + }, + ) + + BackHandler( + enabled = sheetNavigator.size > 1, + onBack = sheetNavigator::pop, + ) + } + + // Make sure screens are disposed no matter what + if (sheetNavigator.parent?.disposeBehavior?.disposeNestedNavigators == false) { + DisposableEffectIgnoringConfiguration { + onDispose { + sheetNavigator.items + .asReversed() + .forEach(sheetNavigator::dispose) + } + } + } + }, + ) +} + /** * Sheet with adaptive position aligned to bottom on small screen, otherwise aligned to center * and will not be able to dismissed with swipe gesture. @@ -212,6 +263,10 @@ fun AdaptiveSheetImpl( .windowInsetsPadding( WindowInsets.systemBars .only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), + ) + .consumedWindowInsets( + WindowInsets.systemBars + .only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), ), shape = MaterialTheme.shapes.extraLarge.copy(bottomStart = ZeroCornerSize, bottomEnd = ZeroCornerSize), tonalElevation = tonalElevation, diff --git a/app/src/main/java/eu/kanade/presentation/manga/TrackInfoDialogHome.kt b/app/src/main/java/eu/kanade/presentation/manga/TrackInfoDialogHome.kt index 9022829fa7..7c55bf7c67 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/TrackInfoDialogHome.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/TrackInfoDialogHome.kt @@ -8,11 +8,13 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.IntrinsicSize -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.systemBars +import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape @@ -54,7 +56,6 @@ private const val UnsetStatusTextAlpha = 0.5F fun TrackInfoDialogHome( trackItems: List, dateFormat: DateFormat, - contentPadding: PaddingValues = PaddingValues(), onStatusClick: (TrackItem) -> Unit, onChapterClick: (TrackItem) -> Unit, onScoreClick: (TrackItem) -> Unit, @@ -70,7 +71,7 @@ fun TrackInfoDialogHome( .fillMaxWidth() .verticalScroll(rememberScrollState()) .padding(16.dp) - .padding(contentPadding), + .windowInsetsPadding(WindowInsets.systemBars), verticalArrangement = Arrangement.spacedBy(24.dp), ) { trackItems.forEach { item -> diff --git a/app/src/main/java/eu/kanade/presentation/manga/TrackInfoDialogSelector.kt b/app/src/main/java/eu/kanade/presentation/manga/TrackInfoDialogSelector.kt index 637f5f6719..89a3f692a0 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/TrackInfoDialogSelector.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/TrackInfoDialogSelector.kt @@ -3,12 +3,14 @@ package eu.kanade.presentation.manga import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.systemBars +import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.selection.selectable import androidx.compose.foundation.shape.RoundedCornerShape @@ -41,7 +43,6 @@ import java.time.format.TextStyle @Composable fun TrackStatusSelector( - contentPadding: PaddingValues, selection: Int, onSelectionChange: (Int) -> Unit, selections: Map, @@ -49,7 +50,6 @@ fun TrackStatusSelector( onDismissRequest: () -> Unit, ) { BaseSelector( - contentPadding = contentPadding, title = stringResource(R.string.status), content = { val state = rememberLazyListState() @@ -91,7 +91,6 @@ fun TrackStatusSelector( @Composable fun TrackChapterSelector( - contentPadding: PaddingValues, selection: Int, onSelectionChange: (Int) -> Unit, range: Iterable, @@ -99,7 +98,6 @@ fun TrackChapterSelector( onDismissRequest: () -> Unit, ) { BaseSelector( - contentPadding = contentPadding, title = stringResource(R.string.chapters), content = { WheelTextPicker( @@ -119,7 +117,6 @@ fun TrackChapterSelector( @Composable fun TrackScoreSelector( - contentPadding: PaddingValues, selection: String, onSelectionChange: (String) -> Unit, selections: List, @@ -127,7 +124,6 @@ fun TrackScoreSelector( onDismissRequest: () -> Unit, ) { BaseSelector( - contentPadding = contentPadding, title = stringResource(R.string.score), content = { WheelTextPicker( @@ -147,7 +143,6 @@ fun TrackScoreSelector( @Composable fun TrackDateSelector( - contentPadding: PaddingValues, title: String, selection: LocalDate, onSelectionChange: (LocalDate) -> Unit, @@ -156,7 +151,6 @@ fun TrackDateSelector( onDismissRequest: () -> Unit, ) { BaseSelector( - contentPadding = contentPadding, title = title, content = { Row( @@ -198,7 +192,6 @@ fun TrackDateSelector( @Composable private fun BaseSelector( - contentPadding: PaddingValues = PaddingValues(), title: String, content: @Composable BoxScope.() -> Unit, thirdButton: @Composable (RowScope.() -> Unit)? = null, @@ -206,7 +199,7 @@ private fun BaseSelector( onDismissRequest: () -> Unit, ) { AlertDialogContent( - modifier = Modifier.padding(contentPadding), + modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars), title = { Text(text = title) }, text = { Box( diff --git a/app/src/main/java/eu/kanade/presentation/manga/TrackServiceSearch.kt b/app/src/main/java/eu/kanade/presentation/manga/TrackServiceSearch.kt index 8ee590305e..d7f1037d89 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/TrackServiceSearch.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/TrackServiceSearch.kt @@ -16,9 +16,11 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.paddingFromBaseline import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.lazy.items import androidx.compose.foundation.selection.selectable import androidx.compose.foundation.shape.RoundedCornerShape @@ -34,7 +36,6 @@ import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable @@ -47,7 +48,6 @@ import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.platform.LocalFocusManager -import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.capitalize import androidx.compose.ui.text.input.ImeAction @@ -60,6 +60,7 @@ import eu.kanade.presentation.components.Divider import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.components.LoadingScreen import eu.kanade.presentation.components.MangaCover +import eu.kanade.presentation.components.Scaffold import eu.kanade.presentation.components.ScrollbarLazyColumn import eu.kanade.presentation.util.plus import eu.kanade.presentation.util.runOnEnterKeyPressed @@ -69,7 +70,6 @@ import eu.kanade.tachiyomi.data.track.model.TrackSearch @Composable fun TrackServiceSearch( - contentPadding: PaddingValues = PaddingValues(), query: TextFieldValue, onQueryChange: (TextFieldValue) -> Unit, onDispatchQuery: () -> Unit, @@ -87,12 +87,6 @@ fun TrackServiceSearch( } Scaffold( - contentWindowInsets = WindowInsets( - left = contentPadding.calculateLeftPadding(LocalLayoutDirection.current), - top = contentPadding.calculateTopPadding(), - right = contentPadding.calculateRightPadding(LocalLayoutDirection.current), - bottom = contentPadding.calculateBottomPadding(), - ), topBar = { Column { TopAppBar( @@ -161,7 +155,7 @@ fun TrackServiceSearch( onClick = { onConfirmSelection() }, modifier = Modifier .padding(12.dp) - .padding(bottom = contentPadding.calculateBottomPadding()) + .windowInsetsPadding(WindowInsets.navigationBars) .fillMaxWidth(), elevation = ButtonDefaults.elevatedButtonElevation(), ) { diff --git a/app/src/main/java/eu/kanade/presentation/util/Navigator.kt b/app/src/main/java/eu/kanade/presentation/util/Navigator.kt index 39cfa48a09..71a50c284c 100644 --- a/app/src/main/java/eu/kanade/presentation/util/Navigator.kt +++ b/app/src/main/java/eu/kanade/presentation/util/Navigator.kt @@ -1,8 +1,6 @@ package eu.kanade.presentation.util -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.runtime.ProvidableCompositionLocal -import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.staticCompositionLocalOf import cafe.adriel.voyager.navigator.Navigator @@ -11,8 +9,6 @@ import cafe.adriel.voyager.navigator.Navigator */ val LocalBackPress: ProvidableCompositionLocal<(() -> Unit)?> = staticCompositionLocalOf { null } -val LocalNavigatorContentPadding: ProvidableCompositionLocal = compositionLocalOf { PaddingValues() } - interface Tab : cafe.adriel.voyager.navigator.tab.Tab { suspend fun onReselect(navigator: Navigator) {} } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt index 2bc912498e..9adbb36479 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt @@ -5,19 +5,12 @@ import android.content.Intent import android.net.Uri import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.animation.core.tween -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.animation.with import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -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.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalContext @@ -28,21 +21,19 @@ import cafe.adriel.voyager.core.screen.uniqueScreenKey import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.currentOrThrow -import cafe.adriel.voyager.transitions.ScreenTransition import eu.kanade.domain.chapter.model.Chapter import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.hasCustomCover -import eu.kanade.presentation.components.AdaptiveSheet import eu.kanade.presentation.components.ChangeCategoryDialog import eu.kanade.presentation.components.DuplicateMangaDialog import eu.kanade.presentation.components.LoadingScreen +import eu.kanade.presentation.components.NavigatorAdaptiveSheet import eu.kanade.presentation.manga.ChapterSettingsDialog import eu.kanade.presentation.manga.EditCoverAction import eu.kanade.presentation.manga.MangaScreen import eu.kanade.presentation.manga.components.DeleteChaptersDialog import eu.kanade.presentation.manga.components.DownloadCustomAmountDialog import eu.kanade.presentation.manga.components.MangaCoverDialog -import eu.kanade.presentation.util.LocalNavigatorContentPadding import eu.kanade.presentation.util.isTabletUi import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.Source @@ -170,31 +161,15 @@ class MangaScreen( onSetAsDefault = screenModel::setCurrentSettingsAsDefault, ) MangaInfoScreenModel.Dialog.TrackSheet -> { - var enableSwipeDismiss by remember { mutableStateOf(true) } - AdaptiveSheet( - enableSwipeDismiss = enableSwipeDismiss, + NavigatorAdaptiveSheet( + screen = TrackInfoDialogHomeScreen( + mangaId = successState.manga.id, + mangaTitle = successState.manga.title, + sourceId = successState.source.id, + ), + enableSwipeDismiss = { it.lastItem is TrackInfoDialogHomeScreen }, onDismissRequest = onDismissRequest, - ) { contentPadding -> - Navigator( - screen = TrackInfoDialogHomeScreen( - mangaId = successState.manga.id, - mangaTitle = successState.manga.title, - sourceId = successState.source.id, - ), - content = { - enableSwipeDismiss = it.lastItem is TrackInfoDialogHomeScreen - CompositionLocalProvider(LocalNavigatorContentPadding provides contentPadding) { - ScreenTransition( - navigator = it, - transition = { - fadeIn(animationSpec = tween(220, delayMillis = 90)) with - fadeOut(animationSpec = tween(90)) - }, - ) - } - }, - ) - } + ) } MangaInfoScreenModel.Dialog.FullCover -> { val sm = rememberScreenModel { MangaCoverScreenModel(successState.manga.id) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt index 54161d4693..a79f039933 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt @@ -4,8 +4,10 @@ import android.app.Application import android.content.Context import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxWidth -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.filled.Delete import androidx.compose.material3.ButtonDefaults @@ -51,7 +53,6 @@ import eu.kanade.presentation.manga.TrackInfoDialogHome import eu.kanade.presentation.manga.TrackScoreSelector import eu.kanade.presentation.manga.TrackServiceSearch import eu.kanade.presentation.manga.TrackStatusSelector -import eu.kanade.presentation.util.LocalNavigatorContentPadding import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.track.EnhancedTrackService @@ -95,7 +96,6 @@ data class TrackInfoDialogHomeScreen( TrackInfoDialogHome( trackItems = state.trackItems, dateFormat = dateFormat, - contentPadding = LocalNavigatorContentPadding.current, onStatusClick = { navigator.push( TrackStatusSelectorScreen( @@ -153,8 +153,7 @@ data class TrackInfoDialogHomeScreen( } }, onOpenInBrowser = { openTrackerInBrowser(context, it) }, - onRemoved = { sm.unregisterTracking(it.service.id) }, - ) + ) { sm.unregisterTracking(it.service.id) } } /** @@ -257,7 +256,6 @@ private data class TrackStatusSelectorScreen( } val state by sm.state.collectAsState() TrackStatusSelector( - contentPadding = LocalNavigatorContentPadding.current, selection = state.selection, onSelectionChange = sm::setSelection, selections = remember { sm.getSelections() }, @@ -308,7 +306,6 @@ private data class TrackChapterSelectorScreen( val state by sm.state.collectAsState() TrackChapterSelector( - contentPadding = LocalNavigatorContentPadding.current, selection = state.selection, onSelectionChange = sm::setSelection, range = remember { sm.getRange() }, @@ -364,7 +361,6 @@ private data class TrackScoreSelectorScreen( val state by sm.state.collectAsState() TrackScoreSelector( - contentPadding = LocalNavigatorContentPadding.current, selection = state.selection, onSelectionChange = sm::setSelection, selections = remember { sm.getSelections() }, @@ -422,7 +418,6 @@ private data class TrackDateSelectorScreen( track.finished_reading_date > 0 } TrackDateSelector( - contentPadding = LocalNavigatorContentPadding.current, title = if (start) { stringResource(R.string.track_started_reading_date) } else { @@ -497,7 +492,7 @@ private data class TrackDateRemoverScreen( ) } AlertDialogContent( - modifier = Modifier.padding(LocalNavigatorContentPadding.current), + modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars), icon = { Icon( imageVector = Icons.Default.Delete, @@ -585,7 +580,6 @@ data class TrackServiceSearchScreen( var textFieldValue by remember { mutableStateOf(TextFieldValue(initialQuery)) } TrackServiceSearch( - contentPadding = LocalNavigatorContentPadding.current, query = textFieldValue, onQueryChange = { textFieldValue = it }, onDispatchQuery = { sm.trackingSearch(textFieldValue.text) },