From 918ca4ee6166608090cd7c1d597f910b75a2dfce Mon Sep 17 00:00:00 2001 From: Josh Stagg Date: Tue, 19 Nov 2024 14:43:43 -0800 Subject: [PATCH] Revert `AnimatedScreen` work on it later --- .../foundation/NavigableCircuitContent.kt | 16 +- .../foundation/NavigationDecoration.kt | 170 +----------------- gradle/libs.versions.toml | 2 +- .../com/slack/circuit/star/home/HomeScreen.kt | 12 +- .../circuit/star/petdetail/PetDetailScreen.kt | 16 +- .../circuit/star/petlist/PetListScreen.kt | 13 +- 6 files changed, 11 insertions(+), 218 deletions(-) diff --git a/circuit-foundation/src/commonMain/kotlin/com/slack/circuit/foundation/NavigableCircuitContent.kt b/circuit-foundation/src/commonMain/kotlin/com/slack/circuit/foundation/NavigableCircuitContent.kt index 2b9e6ab3c..01c71ea28 100644 --- a/circuit-foundation/src/commonMain/kotlin/com/slack/circuit/foundation/NavigableCircuitContent.kt +++ b/circuit-foundation/src/commonMain/kotlin/com/slack/circuit/foundation/NavigableCircuitContent.kt @@ -348,11 +348,9 @@ public object NavigatorDefaults { // The states are available as `targetState` and `initialState`. val diff = targetState.args.size - initialState.args.size val sameRoot = targetState.args.lastOrNull() == initialState.args.lastOrNull() - val targetScreen = targetState.args.firstOrNull()?.screen - val initialScreen = initialState.args.firstOrNull()?.screen when { - sameRoot && diff > 0 -> contentTransform(targetScreen, initialScreen, forward) - sameRoot && diff < 0 -> contentTransform(initialScreen, targetScreen, backward) + sameRoot && diff > 0 -> forward + sameRoot && diff < 0 -> backward else -> fadeIn() togetherWith fadeOut() }.using( // Disable clipping since the faded slide-in/out should @@ -361,16 +359,6 @@ public object NavigatorDefaults { ) } - private fun contentTransform( - targetScreen: Screen?, - initialScreen: Screen?, - fallback: ContentTransform, - ): ContentTransform { - return ((targetScreen as? AnimatedScreen)?.enterTransition() - ?: fallback.targetContentEnter) togetherWith - ((initialScreen as? AnimatedScreen)?.exitTransition() ?: fallback.initialContentExit) - } - @Composable public override fun AnimatedContentScope.AnimatedNavContent( targetState: DefaultAnimatedState, diff --git a/circuit-foundation/src/commonMain/kotlin/com/slack/circuit/foundation/NavigationDecoration.kt b/circuit-foundation/src/commonMain/kotlin/com/slack/circuit/foundation/NavigationDecoration.kt index 617590a14..a5079b0d3 100644 --- a/circuit-foundation/src/commonMain/kotlin/com/slack/circuit/foundation/NavigationDecoration.kt +++ b/circuit-foundation/src/commonMain/kotlin/com/slack/circuit/foundation/NavigationDecoration.kt @@ -5,27 +5,17 @@ package com.slack.circuit.foundation import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedContentScope import androidx.compose.animation.AnimatedContentTransitionScope -import androidx.compose.animation.AnimatedVisibilityScope import androidx.compose.animation.ContentTransform -import androidx.compose.animation.EnterTransition -import androidx.compose.animation.ExitTransition import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.core.Transition -import androidx.compose.foundation.layout.Box import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable -import androidx.compose.runtime.State import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateMapOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.runtime.snapshots.SnapshotStateMap import androidx.compose.ui.Modifier import com.slack.circuit.backstack.NavArgument import com.slack.circuit.backstack.NavDecoration -import com.slack.circuit.retained.rememberRetained import com.slack.circuit.runtime.screen.Screen import com.slack.circuit.sharedelements.ProvideAnimatedTransitionScope import com.slack.circuit.sharedelements.SharedElementTransitionScope.AnimatedScope.Navigation @@ -49,22 +39,9 @@ public abstract class AnimatedNavDecoration( } with(decorator) { Content(args, backStackDepth, modifier) { modifier -> - val backStackEntryState = sharedElementBackStack() AnimatedContent(modifier = modifier, transitionSpec = transitionSpec()) { targetState -> - val animatedContentScope = this@AnimatedContent - ProvideAnimatedTransitionScope(Navigation, animatedContentScope) { - sharedElementTransitionScope -> - val entry = backStackEntryState.value - Box( - modifier = - Modifier.overrideAnimations( - animatedVisibilityScope = animatedContentScope, - screen = targetState.screen, - sharedElementTransition = sharedElementTransitionScope != null && entry != null, - ) - ) { - AnimatedNavContent(targetState) { content(it) } - } + ProvideAnimatedTransitionScope(Navigation, this) { + AnimatedNavContent(targetState) { content(it) } } } } @@ -110,148 +87,5 @@ public interface AnimatedNavState { public val backStackDepth: Int } -/** A [Screen] that supports custom Enter/Exit transitions. */ -// TODO Remove AnimatedScreen and replace it with a mechanism to support custom transitions between -// any two given screens -public interface AnimatedNavigationOverride { - // todo Should this indicate if it's a backward navigation or forward navigation? - public fun transitionSpec(source: Screen, target: Screen, direction: Direction): ContentTransform? - - // todo If we don't need the sharedBounds we don't need this. It might be nice to have this check - // so we default override any shared transition away from the normal animations? - public fun expectsSharedElementTransition(source: Screen, target: Screen): Boolean - - public enum class Direction { - Forward, - Backward, - } -} - -public interface AnimatedScreen : Screen { - /** - * A [EnterTransition] to use when showing this screen. If null is returned the screen is animated - * by the [NavDecoration]. - * - * @param sharedElementTransition Whether this screen has a shared element transition. - */ - public fun enterTransition(sharedElementTransition: Boolean = false): EnterTransition? = null - - /** - * A [EnterTransition] to use when hiding this screen. If null is returned the screen is animated - * by the [NavDecoration]. - * - * @param sharedElementTransition Whether this screen has a shared element transition. - */ - public fun exitTransition(sharedElementTransition: Boolean = false): ExitTransition? = null - - /** - * A key for use when a shared element transition is expected when navigating to this screen. If - * no shared element transition is expected, return `null`. - */ - public fun sharedElementTransitionKey(): Any? = null -} - -/** Create or retrieve a [SharedElementBackStackEntry] for the current back stack. */ -@Composable -private fun Transition.sharedElementBackStack(): - State { - val current = targetState.asNavArgBackStackState() - val previous = currentState.asNavArgBackStackState() - - val sharedKeys = rememberRetained { - mutableStateMapOf() - } - - var lastCurrent by rememberRetained { mutableStateOf(current) } - var lastPrevious by rememberRetained { mutableStateOf(previous) } - - val lastCurrentValue = lastCurrent - val lastPreviousValue = lastPrevious - - return produceState(null, current, previous) { - // Ignore a settled state - if (current == previous) { - value = null - return@produceState - } - value = - when { - // Forward - lastCurrentValue == previous && - (lastCurrentValue.backStackDepth + 1) == current.backStackDepth -> { - forwardSharedElementBackStackEntry(current.screen, previous.screen)?.also { - sharedKeys[current] = it - } - } - // Backward - lastPreviousValue == current && - lastPreviousValue.backStackDepth == current.backStackDepth -> { - backwardSharedElementBackStackEntry(lastCurrentValue, current.screen, sharedKeys).also { - sharedKeys.remove(lastCurrentValue) - } - } - // Unknown - else -> null - } - lastCurrent = current - lastPrevious = previous - } -} - -/** If navigating forward, with a key, we're expecting a shared element transition. */ -private fun forwardSharedElementBackStackEntry( - current: Screen, - previous: Screen, -): SharedElementBackStackEntry? { - val currentScreen = current as? AnimatedScreen - val key = currentScreen?.sharedElementTransitionKey() ?: return null - return SharedElementBackStackEntry(key, currentScreen, previous) -} - -/** - * If navigating backward, with a shared key, and screens already seen; We are expecting a shared - * element transition. - */ -private fun backwardSharedElementBackStackEntry( - lastCurrentArg: NavArgBackStackState, - current: Screen, - sharedKeys: SnapshotStateMap, -): SharedElementBackStackEntry? { - val previousScreen = lastCurrentArg.screen as? AnimatedScreen - val key = previousScreen?.sharedElementTransitionKey() ?: return null - val entry = sharedKeys[lastCurrentArg] ?: return null - return if ( - key == entry.key && previousScreen == entry.currentScreen && current == entry.previousScreen - ) { - entry - } else { - null - } -} - -private fun Modifier.overrideAnimations( - animatedVisibilityScope: AnimatedVisibilityScope, - screen: Screen, - sharedElementTransition: Boolean, -): Modifier { - val animatedScreen = screen as? AnimatedScreen - val enter = animatedScreen?.enterTransition(sharedElementTransition) - val exit = animatedScreen?.exitTransition(sharedElementTransition) - if (enter == null || exit == null) return this - return with(animatedVisibilityScope) { animateEnterExit(enter = enter, exit = exit) } -} - -private fun AnimatedNavState.asNavArgBackStackState(): NavArgBackStackState { - return NavArgBackStackState(screen, backStackDepth) -} - -private data class NavArgBackStackState(val screen: Screen, val backStackDepth: Int) - -private data class SharedElementBackStackEntry( - val key: Any, - val currentScreen: AnimatedScreen, - val previousScreen: Screen, -) - public class DefaultAnimatedNavDecoration(decoratorFactory: AnimatedNavDecorator.Factory) : AnimatedNavDecoration(decoratorFactory) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ec66ebb0b..070c469f6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -25,7 +25,7 @@ datastore = "1.1.1" detekt = "1.23.7" dokka = "1.9.20" eithernet = "2.0.0-alpha01" -jdk = "22" +jdk = "23" jvmTarget = "11" publishedJvmTarget = "1.8" kct = "0.5.1" diff --git a/samples/star/src/commonMain/kotlin/com/slack/circuit/star/home/HomeScreen.kt b/samples/star/src/commonMain/kotlin/com/slack/circuit/star/home/HomeScreen.kt index 52a9e9d81..b5d7e6b0b 100644 --- a/samples/star/src/commonMain/kotlin/com/slack/circuit/star/home/HomeScreen.kt +++ b/samples/star/src/commonMain/kotlin/com/slack/circuit/star/home/HomeScreen.kt @@ -5,8 +5,6 @@ package com.slack.circuit.star.home import androidx.compose.animation.EnterExitState import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.core.EaseInOutCubic -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -29,7 +27,6 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.Layout import androidx.compose.ui.unit.IntOffset import com.slack.circuit.codegen.annotations.CircuitInject -import com.slack.circuit.foundation.AnimatedScreen import com.slack.circuit.foundation.CircuitContent import com.slack.circuit.foundation.NavEvent import com.slack.circuit.foundation.onNavEvent @@ -37,6 +34,7 @@ import com.slack.circuit.retained.rememberRetained import com.slack.circuit.runtime.CircuitUiEvent import com.slack.circuit.runtime.CircuitUiState import com.slack.circuit.runtime.Navigator +import com.slack.circuit.runtime.screen.Screen import com.slack.circuit.sharedelements.SharedElementTransitionScope import com.slack.circuit.sharedelements.SharedElementTransitionScope.AnimatedScope.Navigation import com.slack.circuit.sharedelements.progress @@ -51,13 +49,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @CommonParcelize -data object HomeScreen : AnimatedScreen { - - override fun enterTransition(sharedElementTransition: Boolean) = - if (sharedElementTransition) fadeIn() else null - - override fun exitTransition(sharedElementTransition: Boolean) = - if (sharedElementTransition) fadeOut() else null +data object HomeScreen : Screen { data class State( val navItems: ImmutableList = diff --git a/samples/star/src/commonMain/kotlin/com/slack/circuit/star/petdetail/PetDetailScreen.kt b/samples/star/src/commonMain/kotlin/com/slack/circuit/star/petdetail/PetDetailScreen.kt index 407f62408..46673a163 100644 --- a/samples/star/src/commonMain/kotlin/com/slack/circuit/star/petdetail/PetDetailScreen.kt +++ b/samples/star/src/commonMain/kotlin/com/slack/circuit/star/petdetail/PetDetailScreen.kt @@ -3,8 +3,6 @@ package com.slack.circuit.star.petdetail import androidx.compose.animation.ExperimentalSharedTransitionApi -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.ExperimentalLayoutApi @@ -38,7 +36,6 @@ import androidx.compose.ui.text.capitalize import androidx.compose.ui.text.intl.LocaleList import androidx.compose.ui.unit.dp import com.slack.circuit.codegen.annotations.CircuitInject -import com.slack.circuit.foundation.AnimatedScreen import com.slack.circuit.foundation.CircuitContent import com.slack.circuit.foundation.thenIf import com.slack.circuit.foundation.thenIfNotNull @@ -46,6 +43,7 @@ import com.slack.circuit.runtime.CircuitUiEvent import com.slack.circuit.runtime.CircuitUiState import com.slack.circuit.runtime.Navigator import com.slack.circuit.runtime.presenter.Presenter +import com.slack.circuit.runtime.screen.Screen import com.slack.circuit.sharedelements.DelicateCircuitSharedElementsApi import com.slack.circuit.sharedelements.SharedElementTransitionScope import com.slack.circuit.sharedelements.SharedElementTransitionScope.AnimatedScope.Navigation @@ -87,17 +85,7 @@ data class PetDetailScreen( val petId: Long, val photoUrlMemoryCacheKey: String? = null, val animal: PartialAnimal? = null, -) : AnimatedScreen { - - override fun enterTransition(sharedElementTransition: Boolean) = - if (sharedElementTransition) fadeIn() else null - - override fun exitTransition(sharedElementTransition: Boolean) = - if (sharedElementTransition) fadeOut() else null - - override fun sharedElementTransitionKey(): Any? { - return if (animal != null && photoUrlMemoryCacheKey != null) petId else null - } +) : Screen { @CommonParcelize data class PartialAnimal( diff --git a/samples/star/src/commonMain/kotlin/com/slack/circuit/star/petlist/PetListScreen.kt b/samples/star/src/commonMain/kotlin/com/slack/circuit/star/petlist/PetListScreen.kt index 372f6ddea..975d4c937 100644 --- a/samples/star/src/commonMain/kotlin/com/slack/circuit/star/petlist/PetListScreen.kt +++ b/samples/star/src/commonMain/kotlin/com/slack/circuit/star/petlist/PetListScreen.kt @@ -2,13 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 package com.slack.circuit.star.petlist -import androidx.compose.animation.EnterTransition -import androidx.compose.animation.ExitTransition import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.SharedTransitionScope.PlaceHolderSize.Companion.animatedSize import androidx.compose.animation.SharedTransitionScope.ResizeMode.Companion.ScaleToBounds import androidx.compose.animation.core.AnimationConstants -import androidx.compose.animation.fadeOut import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.clickable @@ -92,7 +89,6 @@ import coil3.compose.LocalPlatformContext import coil3.request.ImageRequest.Builder import coil3.request.crossfade import com.slack.circuit.codegen.annotations.CircuitInject -import com.slack.circuit.foundation.AnimatedScreen import com.slack.circuit.foundation.rememberAnsweringNavigator import com.slack.circuit.overlay.OverlayEffect import com.slack.circuit.retained.collectAsRetainedState @@ -101,6 +97,7 @@ import com.slack.circuit.runtime.CircuitUiEvent import com.slack.circuit.runtime.CircuitUiState import com.slack.circuit.runtime.Navigator import com.slack.circuit.runtime.presenter.Presenter +import com.slack.circuit.runtime.screen.Screen import com.slack.circuit.sharedelements.SharedElementTransitionScope import com.slack.circuit.sharedelements.SharedElementTransitionScope.AnimatedScope.Navigation import com.slack.circuit.sharedelements.progress @@ -145,13 +142,7 @@ import kotlinx.collections.immutable.toImmutableSet import kotlinx.coroutines.flow.map @CommonParcelize -data object PetListScreen : AnimatedScreen { - - override fun enterTransition(sharedElementTransition: Boolean) = - if (sharedElementTransition) EnterTransition.None else null - - override fun exitTransition(sharedElementTransition: Boolean) = - if (sharedElementTransition) ExitTransition.None else fadeOut() +data object PetListScreen : Screen { sealed interface State : CircuitUiState { val isRefreshing: Boolean