diff --git a/app/src/main/java/eu/kanade/core/navigation/Screen.kt b/app/src/main/java/eu/kanade/core/navigation/Screen.kt deleted file mode 100644 index 0f0cac0da6..0000000000 --- a/app/src/main/java/eu/kanade/core/navigation/Screen.kt +++ /dev/null @@ -1,12 +0,0 @@ -package eu.kanade.core.navigation - -import cafe.adriel.voyager.core.screen.ScreenKey -import cafe.adriel.voyager.core.screen.uniqueScreenKey -import cafe.adriel.voyager.core.screen.Screen as VoyagerScreen - -// TODO: this prevents crashes in nested navigators with transitions not being disposed -// properly. Go back to using vanilla Voyager Screens once fixed upstream. -abstract class Screen : VoyagerScreen { - - override val key: ScreenKey = uniqueScreenKey -} diff --git a/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt index e9d4fe295e..1c9bdacddd 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt @@ -21,8 +21,6 @@ import eu.kanade.presentation.browse.components.BrowseSourceComfortableGrid import eu.kanade.presentation.browse.components.BrowseSourceCompactGrid import eu.kanade.presentation.browse.components.BrowseSourceList import eu.kanade.presentation.components.AppBar -import eu.kanade.presentation.components.EmptyScreen -import eu.kanade.presentation.components.EmptyScreenAction import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.Source @@ -30,8 +28,10 @@ import eu.kanade.tachiyomi.source.SourceManager import kotlinx.coroutines.flow.StateFlow import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.manga.model.Manga -import tachiyomi.presentation.core.components.LoadingScreen import tachiyomi.presentation.core.components.material.Scaffold +import tachiyomi.presentation.core.screens.EmptyScreen +import tachiyomi.presentation.core.screens.EmptyScreenAction +import tachiyomi.presentation.core.screens.LoadingScreen @Composable fun BrowseSourceContent( diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt index bb9f0021ee..530c31af38 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt @@ -47,7 +47,6 @@ import eu.kanade.domain.extension.interactor.ExtensionSourceItem import eu.kanade.presentation.browse.components.ExtensionIcon import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBarActions -import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.components.WarningBanner import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget import eu.kanade.presentation.more.settings.widget.TrailingWidgetBuffer @@ -61,6 +60,7 @@ import tachiyomi.presentation.core.components.material.DIVIDER_ALPHA import tachiyomi.presentation.core.components.material.Divider import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.padding +import tachiyomi.presentation.core.screens.EmptyScreen @Composable fun ExtensionDetailsScreen( diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionFilterScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionFilterScreen.kt index 3c81a28677..30fb439430 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionFilterScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionFilterScreen.kt @@ -8,13 +8,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import eu.kanade.presentation.components.AppBar -import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.browse.extension.ExtensionFilterState import eu.kanade.tachiyomi.util.system.LocaleHelper import tachiyomi.presentation.core.components.FastScrollLazyColumn import tachiyomi.presentation.core.components.material.Scaffold +import tachiyomi.presentation.core.screens.EmptyScreen @Composable fun ExtensionFilterScreen( diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt index 60bd96ca61..b894960c57 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt @@ -37,7 +37,6 @@ import androidx.compose.ui.unit.dp import com.google.accompanist.flowlayout.FlowRow import eu.kanade.presentation.browse.components.BaseBrowseItem import eu.kanade.presentation.browse.components.ExtensionIcon -import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.manga.components.DotSeparatorNoSpaceText import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.extension.model.Extension @@ -46,10 +45,11 @@ import eu.kanade.tachiyomi.ui.browse.extension.ExtensionUiModel import eu.kanade.tachiyomi.ui.browse.extension.ExtensionsState import eu.kanade.tachiyomi.util.system.LocaleHelper import tachiyomi.presentation.core.components.FastScrollLazyColumn -import tachiyomi.presentation.core.components.LoadingScreen import tachiyomi.presentation.core.components.material.PullRefresh import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.topSmallPaddingValues +import tachiyomi.presentation.core.screens.EmptyScreen +import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.theme.header import tachiyomi.presentation.core.util.plus import tachiyomi.presentation.core.util.secondaryItemAlpha diff --git a/app/src/main/java/eu/kanade/presentation/browse/MigrateMangaScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/MigrateMangaScreen.kt index efaef476e0..f3d72cb793 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/MigrateMangaScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/MigrateMangaScreen.kt @@ -6,13 +6,13 @@ import androidx.compose.foundation.lazy.items import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import eu.kanade.presentation.components.AppBar -import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.manga.components.BaseMangaListItem import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.browse.migration.manga.MigrateMangaState import tachiyomi.domain.manga.model.Manga import tachiyomi.presentation.core.components.FastScrollLazyColumn import tachiyomi.presentation.core.components.material.Scaffold +import tachiyomi.presentation.core.screens.EmptyScreen @Composable fun MigrateMangaScreen( diff --git a/app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt index d19a66470b..bae685e2a8 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt @@ -25,18 +25,18 @@ import androidx.compose.ui.text.style.TextOverflow import eu.kanade.domain.source.interactor.SetMigrateSorting import eu.kanade.presentation.browse.components.BaseSourceItem import eu.kanade.presentation.browse.components.SourceIcon -import eu.kanade.presentation.components.EmptyScreen import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrateSourceState import eu.kanade.tachiyomi.util.system.copyToClipboard import tachiyomi.domain.source.model.Source import tachiyomi.presentation.core.components.Badge import tachiyomi.presentation.core.components.BadgeGroup -import tachiyomi.presentation.core.components.LoadingScreen import tachiyomi.presentation.core.components.ScrollbarLazyColumn import tachiyomi.presentation.core.components.Scroller.STICKY_HEADER_KEY_PREFIX import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.topSmallPaddingValues +import tachiyomi.presentation.core.screens.EmptyScreen +import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.theme.header import tachiyomi.presentation.core.util.plus import tachiyomi.presentation.core.util.secondaryItemAlpha diff --git a/app/src/main/java/eu/kanade/presentation/browse/SourcesFilterScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/SourcesFilterScreen.kt index 0f7f224073..d4ad910348 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/SourcesFilterScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/SourcesFilterScreen.kt @@ -10,7 +10,6 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import eu.kanade.presentation.browse.components.BaseSourceItem import eu.kanade.presentation.components.AppBar -import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.browse.source.SourcesFilterState @@ -18,6 +17,7 @@ import eu.kanade.tachiyomi.util.system.LocaleHelper import tachiyomi.domain.source.model.Source import tachiyomi.presentation.core.components.FastScrollLazyColumn import tachiyomi.presentation.core.components.material.Scaffold +import tachiyomi.presentation.core.screens.EmptyScreen @Composable fun SourcesFilterScreen( diff --git a/app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt index e3381b494e..e89d506202 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt @@ -22,7 +22,6 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import eu.kanade.presentation.browse.components.BaseSourceItem -import eu.kanade.presentation.components.EmptyScreen import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.ui.browse.source.SourcesState @@ -30,10 +29,11 @@ import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel.Listi import eu.kanade.tachiyomi.util.system.LocaleHelper import tachiyomi.domain.source.model.Pin import tachiyomi.domain.source.model.Source -import tachiyomi.presentation.core.components.LoadingScreen import tachiyomi.presentation.core.components.ScrollbarLazyColumn import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.topSmallPaddingValues +import tachiyomi.presentation.core.screens.EmptyScreen +import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.theme.header import tachiyomi.presentation.core.util.plus diff --git a/app/src/main/java/eu/kanade/presentation/category/CategoryScreen.kt b/app/src/main/java/eu/kanade/presentation/category/CategoryScreen.kt index 64e9fce643..2d712ef790 100644 --- a/app/src/main/java/eu/kanade/presentation/category/CategoryScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/category/CategoryScreen.kt @@ -10,13 +10,13 @@ import androidx.compose.ui.res.stringResource import eu.kanade.presentation.category.components.CategoryContent import eu.kanade.presentation.category.components.CategoryFloatingActionButton import eu.kanade.presentation.components.AppBar -import eu.kanade.presentation.components.EmptyScreen import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.category.CategoryScreenState import tachiyomi.domain.category.model.Category import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.topSmallPaddingValues +import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.util.plus @Composable 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 cb2a06102f..2e92b63e18 100644 --- a/app/src/main/java/eu/kanade/presentation/components/AdaptiveSheet.kt +++ b/app/src/main/java/eu/kanade/presentation/components/AdaptiveSheet.kt @@ -1,74 +1,25 @@ 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 -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxWithConstraints 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.consumeWindowInsets -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.navigationBars -import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.only -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.requiredWidthIn -import androidx.compose.foundation.layout.systemBars -import androidx.compose.foundation.layout.systemBarsPadding -import androidx.compose.foundation.layout.widthIn -import androidx.compose.foundation.layout.windowInsetsPadding -import androidx.compose.foundation.shape.ZeroCornerSize -import androidx.compose.material.SwipeableState -import androidx.compose.material.rememberSwipeableState -import androidx.compose.material.swipeable -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -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.runtime.snapshotFlow -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.input.nestedscroll.NestedScrollConnection -import androidx.compose.ui.input.nestedscroll.NestedScrollSource -import androidx.compose.ui.input.nestedscroll.nestedScroll 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.core.navigation.Screen import eu.kanade.presentation.util.isTabletUi -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.drop -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.launch -import kotlin.math.roundToInt -import kotlin.time.Duration.Companion.milliseconds - -private const val SheetAnimationDuration = 500 -private val SheetAnimationSpec = tween(durationMillis = SheetAnimationDuration) -private const val ScrimAnimationDuration = 350 -private val ScrimAnimationSpec = tween(durationMillis = ScrimAnimationDuration) +import tachiyomi.presentation.core.components.AdaptiveSheet as AdaptiveSheetImpl @Composable fun NavigatorAdaptiveSheet( @@ -141,204 +92,3 @@ fun AdaptiveSheet( content(contentPadding) } } - -@Composable -fun AdaptiveSheetImpl( - isTabletUi: Boolean, - tonalElevation: Dp, - enableSwipeDismiss: Boolean, - onDismissRequest: () -> Unit, - content: @Composable () -> Unit, -) { - val scope = rememberCoroutineScope() - if (isTabletUi) { - var targetAlpha by remember { mutableStateOf(0f) } - val alpha by animateFloatAsState( - targetValue = targetAlpha, - animationSpec = ScrimAnimationSpec, - ) - val internalOnDismissRequest: () -> Unit = { - scope.launch { - targetAlpha = 0f - delay(ScrimAnimationSpec.durationMillis.milliseconds) - onDismissRequest() - } - } - BoxWithConstraints( - modifier = Modifier - .clickable( - enabled = true, - interactionSource = remember { MutableInteractionSource() }, - indication = null, - onClick = internalOnDismissRequest, - ) - .fillMaxSize() - .alpha(alpha), - contentAlignment = Alignment.Center, - ) { - Box( - modifier = Modifier - .matchParentSize() - .background(MaterialTheme.colorScheme.scrim.copy(alpha = 0.32f)), - ) - Surface( - modifier = Modifier - .requiredWidthIn(max = 460.dp) - .clickable( - interactionSource = remember { MutableInteractionSource() }, - indication = null, - onClick = {}, - ) - .systemBarsPadding() - .padding(vertical = 16.dp), - shape = MaterialTheme.shapes.extraLarge, - tonalElevation = tonalElevation, - content = { - BackHandler(enabled = alpha > 0f, onBack = internalOnDismissRequest) - content() - }, - ) - - LaunchedEffect(Unit) { - targetAlpha = 1f - } - } - } else { - val swipeState = rememberSwipeableState( - initialValue = 1, - animationSpec = SheetAnimationSpec, - ) - val internalOnDismissRequest: () -> Unit = { if (swipeState.currentValue == 0) scope.launch { swipeState.animateTo(1) } } - BoxWithConstraints( - modifier = Modifier - .clickable( - interactionSource = remember { MutableInteractionSource() }, - indication = null, - onClick = internalOnDismissRequest, - ) - .fillMaxSize(), - contentAlignment = Alignment.BottomCenter, - ) { - val fullHeight = constraints.maxHeight.toFloat() - val anchors = mapOf(0f to 0, fullHeight to 1) - val scrimAlpha by animateFloatAsState( - targetValue = if (swipeState.targetValue == 1) 0f else 1f, - animationSpec = ScrimAnimationSpec, - ) - Box( - modifier = Modifier - .matchParentSize() - .alpha(scrimAlpha) - .background(MaterialTheme.colorScheme.scrim.copy(alpha = 0.32f)), - ) - Surface( - modifier = Modifier - .widthIn(max = 460.dp) - .clickable( - interactionSource = remember { MutableInteractionSource() }, - indication = null, - onClick = {}, - ) - .nestedScroll( - remember(enableSwipeDismiss, anchors) { - swipeState.preUpPostDownNestedScrollConnection( - enabled = enableSwipeDismiss, - anchor = anchors, - ) - }, - ) - .offset { - IntOffset( - 0, - swipeState.offset.value.roundToInt(), - ) - } - .swipeable( - enabled = enableSwipeDismiss, - state = swipeState, - anchors = anchors, - orientation = Orientation.Vertical, - resistance = null, - ) - .windowInsetsPadding( - WindowInsets.systemBars - .only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), - ) - .consumeWindowInsets( - WindowInsets.systemBars - .only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), - ), - shape = MaterialTheme.shapes.extraLarge.copy(bottomStart = ZeroCornerSize, bottomEnd = ZeroCornerSize), - tonalElevation = tonalElevation, - content = { - BackHandler(enabled = swipeState.targetValue == 0, onBack = internalOnDismissRequest) - content() - }, - ) - - LaunchedEffect(swipeState) { - scope.launch { swipeState.animateTo(0) } - snapshotFlow { swipeState.currentValue } - .drop(1) - .filter { it == 1 } - .collectLatest { - delay(ScrimAnimationSpec.durationMillis.milliseconds) - onDismissRequest() - } - } - } - } -} - -/** - * Yoinked from Swipeable.kt with modifications to disable - */ -private fun SwipeableState.preUpPostDownNestedScrollConnection( - enabled: Boolean = true, - anchor: Map, -) = object : NestedScrollConnection { - override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { - val delta = available.toFloat() - return if (enabled && delta < 0 && source == NestedScrollSource.Drag) { - performDrag(delta).toOffset() - } else { - Offset.Zero - } - } - - override fun onPostScroll( - consumed: Offset, - available: Offset, - source: NestedScrollSource, - ): Offset { - return if (enabled && source == NestedScrollSource.Drag) { - performDrag(available.toFloat()).toOffset() - } else { - Offset.Zero - } - } - - override suspend fun onPreFling(available: Velocity): Velocity { - val toFling = Offset(available.x, available.y).toFloat() - return if (enabled && toFling < 0 && offset.value > anchor.keys.minOrNull()!!) { - performFling(velocity = toFling) - // since we go to the anchor with tween settling, consume all for the best UX - available - } else { - Velocity.Zero - } - } - - override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { - return if (enabled) { - performFling(velocity = Offset(available.x, available.y).toFloat()) - available - } else { - Velocity.Zero - } - } - - private fun Float.toOffset(): Offset = Offset(0f, this) - - private fun Offset.toFloat(): Float = this.y -} diff --git a/app/src/main/java/eu/kanade/presentation/components/EmptyScreen.kt b/app/src/main/java/eu/kanade/presentation/components/EmptyScreen.kt index faf6bd0e93..fad8fd6503 100644 --- a/app/src/main/java/eu/kanade/presentation/components/EmptyScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/components/EmptyScreen.kt @@ -1,124 +1,15 @@ package eu.kanade.presentation.components -import androidx.annotation.StringRes -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.paddingFromBaseline import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.HelpOutline import androidx.compose.material.icons.outlined.Refresh -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp import eu.kanade.presentation.theme.TachiyomiTheme import eu.kanade.tachiyomi.R -import tachiyomi.presentation.core.components.material.padding +import tachiyomi.presentation.core.screens.EmptyScreen +import tachiyomi.presentation.core.screens.EmptyScreenAction import tachiyomi.presentation.core.util.ThemePreviews -import tachiyomi.presentation.core.util.secondaryItemAlpha -import kotlin.random.Random - -@Composable -fun EmptyScreen( - @StringRes textResource: Int, - modifier: Modifier = Modifier, - actions: List? = null, -) { - EmptyScreen( - message = stringResource(textResource), - modifier = modifier, - actions = actions, - ) -} - -@Composable -fun EmptyScreen( - message: String, - modifier: Modifier = Modifier, - actions: List? = null, -) { - val face = remember { getRandomErrorFace() } - Column( - modifier = modifier - .fillMaxSize() - .padding(horizontal = 24.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, - ) { - Text( - text = face, - modifier = Modifier.secondaryItemAlpha(), - style = MaterialTheme.typography.displayMedium, - ) - - Text( - text = message, - modifier = Modifier.paddingFromBaseline(top = 24.dp).secondaryItemAlpha(), - style = MaterialTheme.typography.bodyMedium, - textAlign = TextAlign.Center, - ) - - if (!actions.isNullOrEmpty()) { - Row( - modifier = Modifier - .padding( - top = 24.dp, - start = 24.dp, - end = 24.dp, - ), - horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small), - ) { - actions.forEach { - ActionButton( - modifier = Modifier.weight(1f), - title = stringResource(it.stringResId), - icon = it.icon, - onClick = it.onClick, - ) - } - } - } - } -} - -@Composable -private fun ActionButton( - modifier: Modifier = Modifier, - title: String, - icon: ImageVector, - onClick: () -> Unit, -) { - TextButton( - modifier = modifier, - onClick = onClick, - ) { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - Icon( - imageVector = icon, - contentDescription = null, - ) - Spacer(Modifier.height(4.dp)) - Text( - text = title, - textAlign = TextAlign.Center, - ) - } - } -} @ThemePreviews @Composable @@ -155,22 +46,3 @@ private fun WithActionPreview() { } } } - -data class EmptyScreenAction( - @StringRes val stringResId: Int, - val icon: ImageVector, - val onClick: () -> Unit, -) - -private val ERROR_FACES = listOf( - "(・o・;)", - "Σ(ಠ_ಠ)", - "ಥ_ಥ", - "(˘・_・˘)", - "(; ̄Д ̄)", - "(・Д・。", -) - -private fun getRandomErrorFace(): String { - return ERROR_FACES[Random.nextInt(ERROR_FACES.size)] -} diff --git a/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt b/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt index bb08db6b20..3ed6d15fcd 100644 --- a/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt @@ -14,12 +14,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource -import eu.kanade.presentation.components.InfoScaffold import eu.kanade.presentation.theme.TachiyomiTheme import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.util.CrashLogUtil import kotlinx.coroutines.launch import tachiyomi.presentation.core.components.material.padding +import tachiyomi.presentation.core.screens.InfoScreen import tachiyomi.presentation.core.util.ThemePreviews @Composable @@ -30,7 +30,7 @@ fun CrashScreen( val scope = rememberCoroutineScope() val context = LocalContext.current - InfoScaffold( + InfoScreen( icon = Icons.Outlined.BugReport, headingText = stringResource(R.string.crash_screen_title), subtitleText = stringResource(R.string.crash_screen_description, stringResource(R.string.app_name)), 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 0bf4d20526..a6062543ed 100644 --- a/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt @@ -11,15 +11,15 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import eu.kanade.presentation.components.AppBarTitle -import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.components.SearchToolbar import eu.kanade.presentation.history.components.HistoryContent import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.history.HistoryScreenModel import eu.kanade.tachiyomi.ui.history.HistoryState import tachiyomi.domain.history.model.HistoryWithRelations -import tachiyomi.presentation.core.components.LoadingScreen import tachiyomi.presentation.core.components.material.Scaffold +import tachiyomi.presentation.core.screens.EmptyScreen +import tachiyomi.presentation.core.screens.LoadingScreen import java.util.Date @Composable diff --git a/app/src/main/java/eu/kanade/presentation/library/components/LibraryPager.kt b/app/src/main/java/eu/kanade/presentation/library/components/LibraryPager.kt index b286fbde5b..6c9fc418bd 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/LibraryPager.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/LibraryPager.kt @@ -17,13 +17,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.unit.dp import eu.kanade.core.prefs.PreferenceMutableState -import eu.kanade.presentation.components.EmptyScreen import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.library.LibraryItem import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.model.LibraryManga import tachiyomi.presentation.core.components.HorizontalPager import tachiyomi.presentation.core.components.PagerState +import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.util.plus @Composable diff --git a/app/src/main/java/eu/kanade/presentation/more/NewUpdateScreen.kt b/app/src/main/java/eu/kanade/presentation/more/NewUpdateScreen.kt index 4e24da3abb..5e1c4d7ced 100644 --- a/app/src/main/java/eu/kanade/presentation/more/NewUpdateScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/NewUpdateScreen.kt @@ -19,10 +19,10 @@ import com.halilibo.richtext.markdown.Markdown import com.halilibo.richtext.ui.RichTextStyle import com.halilibo.richtext.ui.material3.Material3RichText import com.halilibo.richtext.ui.string.RichTextStringStyle -import eu.kanade.presentation.components.InfoScaffold import eu.kanade.presentation.theme.TachiyomiTheme import eu.kanade.tachiyomi.R import tachiyomi.presentation.core.components.material.padding +import tachiyomi.presentation.core.screens.InfoScreen import tachiyomi.presentation.core.util.ThemePreviews @Composable @@ -33,7 +33,7 @@ fun NewUpdateScreen( onRejectUpdate: () -> Unit, onAcceptUpdate: () -> Unit, ) { - InfoScaffold( + InfoScreen( icon = Icons.Outlined.NewReleases, headingText = stringResource(R.string.update_check_notification_update_available), subtitleText = versionName, diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/AboutScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/AboutScreen.kt index be0e9ed87c..edc61a180d 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/AboutScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/AboutScreen.kt @@ -18,12 +18,12 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.core.navigation.Screen import eu.kanade.domain.ui.UiPreferences import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.more.LogoHeader import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget import eu.kanade.presentation.util.LocalBackPress +import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.updater.AppUpdateChecker diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/ClearDatabaseScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/ClearDatabaseScreen.kt index 988b56aef9..2d8bd1defd 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/ClearDatabaseScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/ClearDatabaseScreen.kt @@ -32,12 +32,11 @@ import cafe.adriel.voyager.core.model.coroutineScope import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.core.navigation.Screen import eu.kanade.domain.source.interactor.GetSourcesWithNonLibraryManga import eu.kanade.presentation.browse.components.SourceIcon import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBarActions -import eu.kanade.presentation.components.EmptyScreen +import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.util.system.toast import kotlinx.coroutines.flow.collectLatest @@ -49,9 +48,10 @@ import tachiyomi.data.Database import tachiyomi.domain.source.model.Source import tachiyomi.domain.source.model.SourceWithCount import tachiyomi.presentation.core.components.FastScrollLazyColumn -import tachiyomi.presentation.core.components.LoadingScreen import tachiyomi.presentation.core.components.material.Divider import tachiyomi.presentation.core.components.material.Scaffold +import tachiyomi.presentation.core.screens.EmptyScreen +import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.util.selectedBackground import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/LicensesScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/LicensesScreen.kt index 7ae37b3ea5..f6fb430fe2 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/LicensesScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/LicensesScreen.kt @@ -9,8 +9,8 @@ import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow import com.mikepenz.aboutlibraries.ui.compose.LibrariesContainer import com.mikepenz.aboutlibraries.ui.compose.LibraryDefaults -import eu.kanade.core.navigation.Screen import eu.kanade.presentation.components.AppBar +import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.R import tachiyomi.presentation.core.components.material.Scaffold diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt index 8b3611d2be..1055a9a6d8 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt @@ -44,11 +44,11 @@ import androidx.core.graphics.ColorUtils import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.core.navigation.Screen import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBarActions import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget import eu.kanade.presentation.util.LocalBackPress +import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.R import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.material.Scaffold diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSearchScreen.kt index 5cde66936d..c5fdc1abf7 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSearchScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSearchScreen.kt @@ -49,13 +49,13 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.core.navigation.Screen -import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.more.settings.Preference +import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.util.system.isLTR import tachiyomi.presentation.core.components.material.Divider import tachiyomi.presentation.core.components.material.Scaffold +import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.util.runOnEnterKeyPressed import cafe.adriel.voyager.core.screen.Screen as VoyagerScreen diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/WorkerInfoScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/WorkerInfoScreen.kt index edd1e71322..b09ee8d072 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/WorkerInfoScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/WorkerInfoScreen.kt @@ -36,7 +36,7 @@ import cafe.adriel.voyager.core.model.coroutineScope import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.core.navigation.Screen +import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.R import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.map diff --git a/app/src/main/java/eu/kanade/presentation/track/TrackServiceSearch.kt b/app/src/main/java/eu/kanade/presentation/track/TrackServiceSearch.kt index ec87f8d3b6..8088168c46 100644 --- a/app/src/main/java/eu/kanade/presentation/track/TrackServiceSearch.kt +++ b/app/src/main/java/eu/kanade/presentation/track/TrackServiceSearch.kt @@ -56,15 +56,15 @@ import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.toLowerCase import androidx.compose.ui.unit.dp -import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.manga.components.MangaCover import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.track.model.TrackSearch -import tachiyomi.presentation.core.components.LoadingScreen import tachiyomi.presentation.core.components.ScrollbarLazyColumn import tachiyomi.presentation.core.components.material.Divider import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.padding +import tachiyomi.presentation.core.screens.EmptyScreen +import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.presentation.core.util.plus import tachiyomi.presentation.core.util.runOnEnterKeyPressed import tachiyomi.presentation.core.util.secondaryItemAlpha 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 77fc475d2f..f8fa342ea4 100644 --- a/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt @@ -24,7 +24,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.util.fastAll import androidx.compose.ui.util.fastAny import eu.kanade.presentation.components.AppBar -import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.manga.components.ChapterDownloadAction import eu.kanade.presentation.manga.components.MangaBottomActionMenu import eu.kanade.tachiyomi.R @@ -34,9 +33,10 @@ import eu.kanade.tachiyomi.ui.updates.UpdatesState import kotlinx.coroutines.delay import kotlinx.coroutines.launch import tachiyomi.presentation.core.components.FastScrollLazyColumn -import tachiyomi.presentation.core.components.LoadingScreen import tachiyomi.presentation.core.components.material.PullRefresh import tachiyomi.presentation.core.components.material.Scaffold +import tachiyomi.presentation.core.screens.EmptyScreen +import tachiyomi.presentation.core.screens.LoadingScreen import kotlin.time.Duration.Companion.seconds @Composable 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 6a7d281c63..fe89516d64 100644 --- a/app/src/main/java/eu/kanade/presentation/util/Navigator.kt +++ b/app/src/main/java/eu/kanade/presentation/util/Navigator.kt @@ -3,6 +3,9 @@ package eu.kanade.presentation.util import androidx.compose.runtime.Composable import androidx.compose.runtime.ProvidableCompositionLocal import androidx.compose.runtime.staticCompositionLocalOf +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.core.screen.ScreenKey +import cafe.adriel.voyager.core.screen.uniqueScreenKey import cafe.adriel.voyager.core.stack.StackEvent import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.transitions.ScreenTransition @@ -18,6 +21,13 @@ interface Tab : cafe.adriel.voyager.navigator.tab.Tab { suspend fun onReselect(navigator: Navigator) {} } +// TODO: this prevents crashes in nested navigators with transitions not being disposed +// properly. Go back to using vanilla Voyager Screens once fixed upstream. +abstract class Screen : Screen { + + override val key: ScreenKey = uniqueScreenKey +} + interface AssistContentScreen { fun onProvideAssistUrl(): String? } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionFilterScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionFilterScreen.kt index 1ec16ba28b..5ef6120927 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionFilterScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionFilterScreen.kt @@ -8,12 +8,12 @@ import androidx.compose.ui.platform.LocalContext import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.core.navigation.Screen import eu.kanade.presentation.browse.ExtensionFilterScreen +import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.util.system.toast import kotlinx.coroutines.flow.collectLatest -import tachiyomi.presentation.core.components.LoadingScreen +import tachiyomi.presentation.core.screens.LoadingScreen class ExtensionFilterScreen : Screen() { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreen.kt index 2191d218d2..7d071e90fe 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreen.kt @@ -9,10 +9,10 @@ import androidx.compose.ui.platform.LocalUriHandler import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.core.navigation.Screen import eu.kanade.presentation.browse.ExtensionDetailsScreen +import eu.kanade.presentation.util.Screen import kotlinx.coroutines.flow.collectLatest -import tachiyomi.presentation.core.components.LoadingScreen +import tachiyomi.presentation.core.screens.LoadingScreen data class ExtensionDetailsScreen( private val pkgName: String, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/SourcePreferencesScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/SourcePreferencesScreen.kt index e28d800304..f93fce78d8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/SourcePreferencesScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/SourcePreferencesScreen.kt @@ -37,7 +37,7 @@ import androidx.preference.forEach import androidx.preference.getOnBindEditTextListener import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.core.navigation.Screen +import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.SharedPreferencesDataStore import eu.kanade.tachiyomi.source.ConfigurableSource diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrationMangaScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrationMangaScreen.kt index c6d634c17a..938845096d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrationMangaScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrationMangaScreen.kt @@ -8,14 +8,14 @@ import androidx.compose.ui.platform.LocalContext import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.core.navigation.Screen import eu.kanade.presentation.browse.MigrateMangaScreen +import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateSearchScreen import eu.kanade.tachiyomi.ui.manga.MangaScreen import eu.kanade.tachiyomi.util.system.toast import kotlinx.coroutines.flow.collectLatest -import tachiyomi.presentation.core.components.LoadingScreen +import tachiyomi.presentation.core.screens.LoadingScreen data class MigrationMangaScreen( private val sourceId: Long, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt index 923c2e744b..469ce2df63 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt @@ -51,7 +51,7 @@ import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.MangaUpdate import tachiyomi.domain.track.interactor.GetTracks import tachiyomi.domain.track.interactor.InsertTrack -import tachiyomi.presentation.core.components.LoadingScreen +import tachiyomi.presentation.core.screens.LoadingScreen import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.util.Date diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreen.kt index 604a6e7fb8..639c1eb74b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreen.kt @@ -6,8 +6,8 @@ import androidx.compose.runtime.getValue import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.core.navigation.Screen import eu.kanade.presentation.browse.MigrateSearchScreen +import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.ui.manga.MangaScreen class MigrateSearchScreen(private val mangaId: Long) : Screen() { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt index 8189512e1a..24d32aba5d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt @@ -21,9 +21,9 @@ import androidx.paging.compose.collectAsLazyPagingItems import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.core.navigation.Screen import eu.kanade.presentation.browse.BrowseSourceContent import eu.kanade.presentation.components.SearchToolbar +import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.online.HttpSource diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesFilterScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesFilterScreen.kt index 5bc5f7ce8b..69a278d3b6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesFilterScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesFilterScreen.kt @@ -8,11 +8,11 @@ import androidx.compose.ui.platform.LocalContext import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.core.navigation.Screen import eu.kanade.presentation.browse.SourcesFilterScreen +import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.util.system.toast -import tachiyomi.presentation.core.components.LoadingScreen +import tachiyomi.presentation.core.screens.LoadingScreen class SourcesFilterScreen : Screen() { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt index b6da64329c..4705e89f2e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt @@ -36,7 +36,6 @@ import androidx.paging.compose.collectAsLazyPagingItems import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.core.navigation.Screen import eu.kanade.presentation.browse.BrowseSourceContent import eu.kanade.presentation.browse.MissingSourceScreen import eu.kanade.presentation.browse.components.BrowseSourceToolbar @@ -44,6 +43,7 @@ import eu.kanade.presentation.browse.components.RemoveMangaDialog import eu.kanade.presentation.category.ChangeCategoryDialog import eu.kanade.presentation.manga.DuplicateMangaDialog import eu.kanade.presentation.util.AssistContentScreen +import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.LocalSource diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt index ec5aabbee5..698d46b777 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt @@ -6,8 +6,8 @@ import androidx.compose.runtime.getValue import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.core.navigation.Screen import eu.kanade.presentation.browse.GlobalSearchScreen +import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen import eu.kanade.tachiyomi.ui.manga.MangaScreen diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryScreen.kt index 6b4437ebf5..77628a4156 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryScreen.kt @@ -8,14 +8,14 @@ import androidx.compose.ui.platform.LocalContext import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.core.navigation.Screen import eu.kanade.presentation.category.CategoryScreen import eu.kanade.presentation.category.components.CategoryCreateDialog import eu.kanade.presentation.category.components.CategoryDeleteDialog import eu.kanade.presentation.category.components.CategoryRenameDialog +import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.util.system.toast import kotlinx.coroutines.flow.collectLatest -import tachiyomi.presentation.core.components.LoadingScreen +import tachiyomi.presentation.core.screens.LoadingScreen class CategoryScreen : Screen() { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt index 6a2836122e..5fbf8e17ec 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt @@ -47,10 +47,9 @@ import androidx.recyclerview.widget.LinearLayoutManager import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.core.navigation.Screen import eu.kanade.presentation.components.AppBar -import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.components.OverflowMenu +import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.download.DownloadService import eu.kanade.tachiyomi.databinding.DownloadListBinding @@ -58,6 +57,7 @@ import tachiyomi.core.util.lang.launchUI import tachiyomi.presentation.core.components.Pill import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton import tachiyomi.presentation.core.components.material.Scaffold +import tachiyomi.presentation.core.screens.EmptyScreen import kotlin.math.roundToInt object DownloadQueueScreen : Screen() { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt index e8b0591324..60dd21e0ff 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/home/HomeScreen.kt @@ -34,9 +34,9 @@ import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.tab.LocalTabNavigator import cafe.adriel.voyager.navigator.tab.TabNavigator -import eu.kanade.core.navigation.Screen import eu.kanade.domain.library.service.LibraryPreferences import eu.kanade.domain.source.service.SourcePreferences +import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.isTabletUi import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.browse.BrowseTab diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt index 7f4749fa4a..d579de51b0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt @@ -30,8 +30,6 @@ import cafe.adriel.voyager.navigator.tab.LocalTabNavigator import cafe.adriel.voyager.navigator.tab.TabOptions import eu.kanade.domain.manga.model.isLocal import eu.kanade.presentation.category.ChangeCategoryDialog -import eu.kanade.presentation.components.EmptyScreen -import eu.kanade.presentation.components.EmptyScreenAction import eu.kanade.presentation.library.DeleteLibraryMangaDialog import eu.kanade.presentation.library.LibrarySettingsDialog import eu.kanade.presentation.library.components.LibraryContent @@ -55,8 +53,10 @@ import tachiyomi.domain.category.model.Category import tachiyomi.domain.library.model.LibraryManga import tachiyomi.domain.library.model.display import tachiyomi.domain.manga.model.Manga -import tachiyomi.presentation.core.components.LoadingScreen import tachiyomi.presentation.core.components.material.Scaffold +import tachiyomi.presentation.core.screens.EmptyScreen +import tachiyomi.presentation.core.screens.EmptyScreenAction +import tachiyomi.presentation.core.screens.LoadingScreen object LibraryTab : Tab { 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 be9107dd5c..c73bdfea4f 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 @@ -20,7 +20,6 @@ import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.core.navigation.Screen import eu.kanade.domain.manga.model.hasCustomCover import eu.kanade.domain.manga.model.toSManga import eu.kanade.presentation.category.ChangeCategoryDialog @@ -32,6 +31,7 @@ import eu.kanade.presentation.manga.MangaScreen import eu.kanade.presentation.manga.components.DeleteChaptersDialog import eu.kanade.presentation.manga.components.MangaCoverDialog import eu.kanade.presentation.util.AssistContentScreen +import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.isTabletUi import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.Source @@ -54,7 +54,7 @@ import tachiyomi.core.util.lang.withIOContext import tachiyomi.core.util.system.logcat import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.manga.model.Manga -import tachiyomi.presentation.core.components.LoadingScreen +import tachiyomi.presentation.core.screens.LoadingScreen class MangaScreen( private val mangaId: Long, 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 5b3311d335..9ed5bc594c 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 @@ -35,7 +35,6 @@ import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.core.navigation.Screen import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay import eu.kanade.domain.track.model.toDbTrack import eu.kanade.domain.track.model.toDomainTrack @@ -46,6 +45,7 @@ import eu.kanade.presentation.track.TrackInfoDialogHome import eu.kanade.presentation.track.TrackScoreSelector import eu.kanade.presentation.track.TrackServiceSearch import eu.kanade.presentation.track.TrackStatusSelector +import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.track.EnhancedTrackService diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/more/NewUpdateScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/more/NewUpdateScreen.kt index 2ee4339fe4..f6ebdab643 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/more/NewUpdateScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/more/NewUpdateScreen.kt @@ -5,8 +5,8 @@ import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.core.navigation.Screen import eu.kanade.presentation.more.NewUpdateScreen +import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.data.updater.AppUpdateService import eu.kanade.tachiyomi.util.system.openInBrowser diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsScreen.kt index 4be9b01faf..e53427a468 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsScreen.kt @@ -12,13 +12,13 @@ import androidx.compose.ui.Modifier import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.core.navigation.Screen import eu.kanade.presentation.more.settings.screen.AboutScreen import eu.kanade.presentation.more.settings.screen.SettingsBackupScreen import eu.kanade.presentation.more.settings.screen.SettingsGeneralScreen import eu.kanade.presentation.more.settings.screen.SettingsMainScreen import eu.kanade.presentation.util.DefaultNavigatorScreenTransition import eu.kanade.presentation.util.LocalBackPress +import eu.kanade.presentation.util.Screen import eu.kanade.presentation.util.isTabletUi import tachiyomi.presentation.core.components.TwoPanelBox diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/track/BaseOAuthLoginActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/track/BaseOAuthLoginActivity.kt index fb948240ca..a1a91a003b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/track/BaseOAuthLoginActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/track/BaseOAuthLoginActivity.kt @@ -7,7 +7,7 @@ import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.ui.base.activity.BaseActivity import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.util.view.setComposeContent -import tachiyomi.presentation.core.components.LoadingScreen +import tachiyomi.presentation.core.screens.LoadingScreen import uy.kohesive.injekt.injectLazy abstract class BaseOAuthLoginActivity : BaseActivity() { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreen.kt index 4d738745fd..a76f837837 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreen.kt @@ -7,13 +7,13 @@ import androidx.compose.ui.res.stringResource import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.core.navigation.Screen import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.more.stats.StatsScreenContent import eu.kanade.presentation.more.stats.StatsScreenState +import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.R -import tachiyomi.presentation.core.components.LoadingScreen import tachiyomi.presentation.core.components.material.Scaffold +import tachiyomi.presentation.core.screens.LoadingScreen class StatsScreen : Screen() { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewScreen.kt index 8d4485ece2..dbb5147947 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewScreen.kt @@ -5,8 +5,8 @@ import androidx.compose.ui.platform.LocalContext import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.core.navigation.Screen import eu.kanade.presentation.util.AssistContentScreen +import eu.kanade.presentation.util.Screen import eu.kanade.presentation.webview.WebViewScreenContent class WebViewScreen( diff --git a/presentation-core/build.gradle.kts b/presentation-core/build.gradle.kts index 8c2218464f..cacfc5d594 100644 --- a/presentation-core/build.gradle.kts +++ b/presentation-core/build.gradle.kts @@ -23,6 +23,7 @@ android { dependencies { // Compose implementation(platform(compose.bom)) + implementation(compose.activity) implementation(compose.foundation) implementation(compose.material3.core) implementation(compose.material.core) diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt new file mode 100644 index 0000000000..d52362b19d --- /dev/null +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt @@ -0,0 +1,261 @@ +package tachiyomi.presentation.core.components + +import androidx.activity.compose.BackHandler +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.tween +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides +import androidx.compose.foundation.layout.consumeWindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.only +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.requiredWidthIn +import androidx.compose.foundation.layout.systemBars +import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.foundation.shape.ZeroCornerSize +import androidx.compose.material.SwipeableState +import androidx.compose.material.rememberSwipeableState +import androidx.compose.material.swipeable +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +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.runtime.snapshotFlow +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.NestedScrollSource +import androidx.compose.ui.input.nestedscroll.nestedScroll +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 kotlinx.coroutines.delay +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.drop +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.launch +import kotlin.math.roundToInt +import kotlin.time.Duration.Companion.milliseconds + +private const val SheetAnimationDuration = 500 +private val SheetAnimationSpec = tween(durationMillis = SheetAnimationDuration) +private const val ScrimAnimationDuration = 350 +private val ScrimAnimationSpec = tween(durationMillis = ScrimAnimationDuration) + +@Composable +fun AdaptiveSheet( + isTabletUi: Boolean, + tonalElevation: Dp, + enableSwipeDismiss: Boolean, + onDismissRequest: () -> Unit, + content: @Composable () -> Unit, +) { + val scope = rememberCoroutineScope() + if (isTabletUi) { + var targetAlpha by remember { mutableStateOf(0f) } + val alpha by animateFloatAsState( + targetValue = targetAlpha, + animationSpec = ScrimAnimationSpec, + ) + val internalOnDismissRequest: () -> Unit = { + scope.launch { + targetAlpha = 0f + delay(ScrimAnimationSpec.durationMillis.milliseconds) + onDismissRequest() + } + } + BoxWithConstraints( + modifier = Modifier + .clickable( + enabled = true, + interactionSource = remember { MutableInteractionSource() }, + indication = null, + onClick = internalOnDismissRequest, + ) + .fillMaxSize() + .alpha(alpha), + contentAlignment = Alignment.Center, + ) { + Box( + modifier = Modifier + .matchParentSize() + .background(MaterialTheme.colorScheme.scrim.copy(alpha = 0.32f)), + ) + Surface( + modifier = Modifier + .requiredWidthIn(max = 460.dp) + .clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = null, + onClick = {}, + ) + .systemBarsPadding() + .padding(vertical = 16.dp), + shape = MaterialTheme.shapes.extraLarge, + tonalElevation = tonalElevation, + content = { + BackHandler(enabled = alpha > 0f, onBack = internalOnDismissRequest) + content() + }, + ) + + LaunchedEffect(Unit) { + targetAlpha = 1f + } + } + } else { + val swipeState = rememberSwipeableState( + initialValue = 1, + animationSpec = SheetAnimationSpec, + ) + val internalOnDismissRequest: () -> Unit = { if (swipeState.currentValue == 0) scope.launch { swipeState.animateTo(1) } } + BoxWithConstraints( + modifier = Modifier + .clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = null, + onClick = internalOnDismissRequest, + ) + .fillMaxSize(), + contentAlignment = Alignment.BottomCenter, + ) { + val fullHeight = constraints.maxHeight.toFloat() + val anchors = mapOf(0f to 0, fullHeight to 1) + val scrimAlpha by animateFloatAsState( + targetValue = if (swipeState.targetValue == 1) 0f else 1f, + animationSpec = ScrimAnimationSpec, + ) + Box( + modifier = Modifier + .matchParentSize() + .alpha(scrimAlpha) + .background(MaterialTheme.colorScheme.scrim.copy(alpha = 0.32f)), + ) + Surface( + modifier = Modifier + .widthIn(max = 460.dp) + .clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = null, + onClick = {}, + ) + .nestedScroll( + remember(enableSwipeDismiss, anchors) { + swipeState.preUpPostDownNestedScrollConnection( + enabled = enableSwipeDismiss, + anchor = anchors, + ) + }, + ) + .offset { + IntOffset( + 0, + swipeState.offset.value.roundToInt(), + ) + } + .swipeable( + enabled = enableSwipeDismiss, + state = swipeState, + anchors = anchors, + orientation = Orientation.Vertical, + resistance = null, + ) + .windowInsetsPadding( + WindowInsets.systemBars + .only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), + ) + .consumeWindowInsets( + WindowInsets.systemBars + .only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), + ), + shape = MaterialTheme.shapes.extraLarge.copy(bottomStart = ZeroCornerSize, bottomEnd = ZeroCornerSize), + tonalElevation = tonalElevation, + content = { + BackHandler(enabled = swipeState.targetValue == 0, onBack = internalOnDismissRequest) + content() + }, + ) + + LaunchedEffect(swipeState) { + scope.launch { swipeState.animateTo(0) } + snapshotFlow { swipeState.currentValue } + .drop(1) + .filter { it == 1 } + .collectLatest { + delay(ScrimAnimationSpec.durationMillis.milliseconds) + onDismissRequest() + } + } + } + } +} + +/** + * Yoinked from Swipeable.kt with modifications to disable + */ +private fun SwipeableState.preUpPostDownNestedScrollConnection( + enabled: Boolean = true, + anchor: Map, +) = object : NestedScrollConnection { + override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { + val delta = available.toFloat() + return if (enabled && delta < 0 && source == NestedScrollSource.Drag) { + performDrag(delta).toOffset() + } else { + Offset.Zero + } + } + + override fun onPostScroll( + consumed: Offset, + available: Offset, + source: NestedScrollSource, + ): Offset { + return if (enabled && source == NestedScrollSource.Drag) { + performDrag(available.toFloat()).toOffset() + } else { + Offset.Zero + } + } + + override suspend fun onPreFling(available: Velocity): Velocity { + val toFling = Offset(available.x, available.y).toFloat() + return if (enabled && toFling < 0 && offset.value > anchor.keys.minOrNull()!!) { + performFling(velocity = toFling) + // since we go to the anchor with tween settling, consume all for the best UX + available + } else { + Velocity.Zero + } + } + + override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { + return if (enabled) { + performFling(velocity = Offset(available.x, available.y).toFloat()) + available + } else { + Velocity.Zero + } + } + + private fun Float.toOffset(): Offset = Offset(0f, this) + + private fun Offset.toFloat(): Float = this.y +} diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/screens/EmptyScreen.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/screens/EmptyScreen.kt new file mode 100644 index 0000000000..80265cd815 --- /dev/null +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/screens/EmptyScreen.kt @@ -0,0 +1,133 @@ +package tachiyomi.presentation.core.screens + +import androidx.annotation.StringRes +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.paddingFromBaseline +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import tachiyomi.presentation.core.components.material.padding +import tachiyomi.presentation.core.util.secondaryItemAlpha +import kotlin.random.Random + +data class EmptyScreenAction( + @StringRes val stringResId: Int, + val icon: ImageVector, + val onClick: () -> Unit, +) + +@Composable +fun EmptyScreen( + @StringRes textResource: Int, + modifier: Modifier = Modifier, + actions: List? = null, +) { + EmptyScreen( + message = stringResource(textResource), + modifier = modifier, + actions = actions, + ) +} + +@Composable +fun EmptyScreen( + message: String, + modifier: Modifier = Modifier, + actions: List? = null, +) { + val face = remember { getRandomErrorFace() } + Column( + modifier = modifier + .fillMaxSize() + .padding(horizontal = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + Text( + text = face, + modifier = Modifier.secondaryItemAlpha(), + style = MaterialTheme.typography.displayMedium, + ) + + Text( + text = message, + modifier = Modifier.paddingFromBaseline(top = 24.dp).secondaryItemAlpha(), + style = MaterialTheme.typography.bodyMedium, + textAlign = TextAlign.Center, + ) + + if (!actions.isNullOrEmpty()) { + Row( + modifier = Modifier + .padding( + top = 24.dp, + start = 24.dp, + end = 24.dp, + ), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small), + ) { + actions.forEach { + ActionButton( + modifier = Modifier.weight(1f), + title = stringResource(it.stringResId), + icon = it.icon, + onClick = it.onClick, + ) + } + } + } + } +} + +@Composable +private fun ActionButton( + modifier: Modifier = Modifier, + title: String, + icon: ImageVector, + onClick: () -> Unit, +) { + TextButton( + modifier = modifier, + onClick = onClick, + ) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Icon( + imageVector = icon, + contentDescription = null, + ) + Spacer(Modifier.height(4.dp)) + Text( + text = title, + textAlign = TextAlign.Center, + ) + } + } +} + +private val ERROR_FACES = listOf( + "(・o・;)", + "Σ(ಠ_ಠ)", + "ಥ_ಥ", + "(˘・_・˘)", + "(; ̄Д ̄)", + "(・Д・。", +) + +private fun getRandomErrorFace(): String { + return ERROR_FACES[Random.nextInt(ERROR_FACES.size)] +} diff --git a/app/src/main/java/eu/kanade/presentation/components/InfoScaffold.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/screens/InfoScreen.kt similarity index 90% rename from app/src/main/java/eu/kanade/presentation/components/InfoScaffold.kt rename to presentation-core/src/main/java/tachiyomi/presentation/core/screens/InfoScreen.kt index 542e3ef7f4..74f8211d91 100644 --- a/app/src/main/java/eu/kanade/presentation/components/InfoScaffold.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/screens/InfoScreen.kt @@ -1,4 +1,4 @@ -package eu.kanade.presentation.components +package tachiyomi.presentation.core.screens import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -26,14 +26,13 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex -import eu.kanade.presentation.theme.TachiyomiTheme import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.util.ThemePreviews import tachiyomi.presentation.core.util.secondaryItemAlpha @Composable -fun InfoScaffold( +fun InfoScreen( icon: ImageVector, headingText: String, subtitleText: String, @@ -125,17 +124,15 @@ fun InfoScaffold( @ThemePreviews @Composable private fun InfoScaffoldPreview() { - TachiyomiTheme { - InfoScaffold( - icon = Icons.Outlined.Newspaper, - headingText = "Heading", - subtitleText = "Subtitle", - acceptText = "Accept", - onAcceptClick = {}, - rejectText = "Reject", - onRejectClick = {}, - ) { - Text("Hello world") - } + InfoScreen( + icon = Icons.Outlined.Newspaper, + headingText = "Heading", + subtitleText = "Subtitle", + acceptText = "Accept", + onAcceptClick = {}, + rejectText = "Reject", + onRejectClick = {}, + ) { + Text("Hello world") } } diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/LoadingScreen.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/screens/LoadingScreen.kt similarity index 91% rename from presentation-core/src/main/java/tachiyomi/presentation/core/components/LoadingScreen.kt rename to presentation-core/src/main/java/tachiyomi/presentation/core/screens/LoadingScreen.kt index 1b05814a75..c9ef088ccb 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/LoadingScreen.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/screens/LoadingScreen.kt @@ -1,4 +1,4 @@ -package tachiyomi.presentation.core.components +package tachiyomi.presentation.core.screens import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize