Browse Source

Track sheet fixes (#8673)

* Fix Track sheet not being disposed properly

* Change insets handling
Ivan Iskandar 2 years ago
parent
commit
47f079891f

+ 55 - 0
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<Float>(durationMillis = SheetAnimationDur
 private const val ScrimAnimationDuration = 350
 private val ScrimAnimationSpec = tween<Float>(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,

+ 4 - 3
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<TrackItem>,
     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 ->

+ 4 - 11
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<Int, String>,
@@ -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<Int>,
@@ -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<String>,
@@ -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(

+ 4 - 10
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(),
                 ) {

+ 0 - 4
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<PaddingValues> = compositionLocalOf { PaddingValues() }
-
 interface Tab : cafe.adriel.voyager.navigator.tab.Tab {
     suspend fun onReselect(navigator: Navigator) {}
 }

+ 9 - 34
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) }

+ 5 - 11
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) },