BIT-985: Add initial transitions to the auth flow (#174)

This commit is contained in:
Brian Yencho 2023-10-30 11:07:07 -05:00 committed by Álison Fernandes
parent e2e2c60759
commit 751b7ab2a8
7 changed files with 240 additions and 3 deletions

View file

@ -4,6 +4,7 @@ import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptions
import androidx.navigation.compose.composable
import com.x8bit.bitwarden.ui.platform.theme.TransitionProviders
private const val CREATE_ACCOUNT_ROUTE = "create_account"
@ -21,7 +22,13 @@ fun NavGraphBuilder.createAccountDestinations(
onNavigateBack: () -> Unit,
onNavigateToLogin: (emailAddress: String, captchaToken: String) -> Unit,
) {
composable(route = CREATE_ACCOUNT_ROUTE) {
composable(
route = CREATE_ACCOUNT_ROUTE,
enterTransition = TransitionProviders.Enter.slideUp,
exitTransition = TransitionProviders.Exit.slideDown,
popEnterTransition = TransitionProviders.Enter.slideUp,
popExitTransition = TransitionProviders.Exit.slideDown,
) {
CreateAccountScreen(
onNavigateBack = onNavigateBack,
onNavigateToLogin = onNavigateToLogin,

View file

@ -4,6 +4,7 @@ import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavOptions
import androidx.navigation.compose.composable
import com.x8bit.bitwarden.ui.platform.theme.TransitionProviders
const val LANDING_ROUTE: String = "landing"
@ -21,7 +22,13 @@ fun NavGraphBuilder.landingDestinations(
onNavigateToCreateAccount: () -> Unit,
onNavigateToLogin: (emailAddress: String) -> Unit,
) {
composable(route = LANDING_ROUTE) {
composable(
route = LANDING_ROUTE,
enterTransition = TransitionProviders.Enter.stay,
exitTransition = TransitionProviders.Exit.stay,
popEnterTransition = TransitionProviders.Enter.stay,
popExitTransition = TransitionProviders.Exit.stay,
) {
LandingScreen(
onNavigateToCreateAccount = onNavigateToCreateAccount,
onNavigateToLogin = onNavigateToLogin,

View file

@ -7,6 +7,7 @@ import androidx.navigation.NavOptions
import androidx.navigation.NavType
import androidx.navigation.compose.composable
import androidx.navigation.navArgument
import com.x8bit.bitwarden.ui.platform.theme.TransitionProviders
private const val EMAIL_ADDRESS: String = "email_address"
private const val CAPTCHA_TOKEN = "captcha_token"
@ -51,6 +52,10 @@ fun NavGraphBuilder.loginDestinations(
nullable = true
},
),
enterTransition = TransitionProviders.Enter.slideUp,
exitTransition = TransitionProviders.Exit.slideDown,
popEnterTransition = TransitionProviders.Enter.slideUp,
popExitTransition = TransitionProviders.Exit.slideDown,
) {
LoginScreen(
onNavigateBack = onNavigateBack,

View file

@ -19,6 +19,7 @@ import com.x8bit.bitwarden.ui.platform.feature.splash.splashDestination
import com.x8bit.bitwarden.ui.platform.feature.vaultunlocked.VAULT_UNLOCKED_GRAPH_ROUTE
import com.x8bit.bitwarden.ui.platform.feature.vaultunlocked.navigateToVaultUnlockedGraph
import com.x8bit.bitwarden.ui.platform.feature.vaultunlocked.vaultUnlockedGraph
import com.x8bit.bitwarden.ui.platform.theme.RootTransitionProviders
/**
* Controls root level [NavHost] for the app.
@ -39,6 +40,10 @@ fun RootNavScreen(
NavHost(
navController = navController,
startDestination = SPLASH_ROUTE,
enterTransition = RootTransitionProviders.Enter.fadeIn,
exitTransition = RootTransitionProviders.Exit.fadeOut,
popEnterTransition = RootTransitionProviders.Enter.fadeIn,
popExitTransition = RootTransitionProviders.Exit.fadeOut,
) {
splashDestination()
authGraph(navController)

View file

@ -37,6 +37,7 @@ import com.x8bit.bitwarden.ui.platform.components.PlaceholderComposable
import com.x8bit.bitwarden.ui.platform.feature.settings.SETTINGS_GRAPH_ROUTE
import com.x8bit.bitwarden.ui.platform.feature.settings.navigateToSettingsGraph
import com.x8bit.bitwarden.ui.platform.feature.settings.settingsGraph
import com.x8bit.bitwarden.ui.platform.theme.RootTransitionProviders
import com.x8bit.bitwarden.ui.tools.feature.generator.GENERATOR_ROUTE
import com.x8bit.bitwarden.ui.tools.feature.generator.generatorDestination
import com.x8bit.bitwarden.ui.tools.feature.generator.navigateToGenerator
@ -175,6 +176,10 @@ private fun VaultUnlockedNavBarScaffold(
modifier = Modifier
.consumeWindowInsets(WindowInsets.navigationBars)
.padding(innerPadding),
enterTransition = RootTransitionProviders.Enter.fadeIn,
exitTransition = RootTransitionProviders.Exit.fadeOut,
popEnterTransition = RootTransitionProviders.Enter.fadeIn,
popExitTransition = RootTransitionProviders.Exit.fadeOut,
) {
vaultDestination()
sendDestination()

View file

@ -0,0 +1,208 @@
package com.x8bit.bitwarden.ui.platform.theme
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.navigation.NavBackStackEntry
import androidx.navigation.compose.NavHost
typealias EnterTransitionProvider =
(@JvmSuppressWildcards AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition?)
typealias ExitTransitionProvider =
(@JvmSuppressWildcards AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition?)
typealias NonNullEnterTransitionProvider =
(@JvmSuppressWildcards AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition)
typealias NonNullExitTransitionProvider =
(@JvmSuppressWildcards AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition)
/**
* The default transition time (in milliseconds) for all transitions in the [TransitionProviders].
*/
const val DEFAULT_TRANSITION_TIME_MS: Int = 350
/**
* Checks if the parent of the destination before and after the navigation is the same. This is
* useful to ignore certain enter/exit transitions when navigating between distinct, nested flows.
*/
val AnimatedContentTransitionScope<NavBackStackEntry>.isSameGraphNavigation: Boolean
get() = initialState.destination.parent == targetState.destination.parent
/**
* Contains standard "transition providers" that may be used to specify the [EnterTransition] and
* [ExitTransition] used when building a typical composable destination. These may return `null`
* values in order to allow transitions between nested navigation graphs to be specified by
* components higher up in the graph.
*/
object TransitionProviders {
/**
* The standard set of "enter" transition providers.
*/
object Enter {
/**
* Fades the new screen in.
*
* Note that this represents a `null` transition when navigating between different nested
* navigation graphs.
*/
val fadeIn: EnterTransitionProvider = {
RootTransitionProviders
.Enter
.fadeIn(this)
.takeIf { isSameGraphNavigation }
}
/**
* Slides the new screen in from the bottom of the screen.
*
* Note that this represents a `null` transition when navigating between different nested
* navigation graphs.
*/
val slideUp: EnterTransitionProvider = {
RootTransitionProviders
.Enter
.slideUp(this)
.takeIf { isSameGraphNavigation }
}
/**
* A "no-op" transition: this changes nothing about the screen but "lasts" as long as
* other standard transitions in order to leave the screen in place such that it does not
* immediately appear while the other screen transitions away.
*
* Note that this represents a `null` transition when navigating between different nested
* navigation graphs.
*/
val stay: EnterTransitionProvider = {
RootTransitionProviders
.Enter
.stay(this)
.takeIf { isSameGraphNavigation }
}
}
/**
* The standard set of "exit" transition providers.
*/
object Exit {
/**
* Fades the current screen out.
*
* Note that this represents a `null` transition when navigating between different nested
* navigation graphs.
*/
val fadeOut: ExitTransitionProvider = {
RootTransitionProviders
.Exit
.fadeOut(this)
.takeIf { isSameGraphNavigation }
}
/**
* Slides the current screen down to the bottom of the screen.
*
* Note that this represents a `null` transition when navigating between different nested
* navigation graphs.
*/
val slideDown: ExitTransitionProvider = {
RootTransitionProviders
.Exit
.slideDown(this)
.takeIf { isSameGraphNavigation }
}
/**
* A "no-op" transition: this changes nothing about the screen but "lasts" as long as
* other standard transitions in order to leave the screen in place such that it does not
* immediately disappear while the other screen transitions into place.
*
* Note that this represents a `null` transition when navigating between different nested
* navigation graphs.
*/
val stay: ExitTransitionProvider = {
RootTransitionProviders
.Exit
.stay(this)
.takeIf { isSameGraphNavigation }
}
}
}
/**
* Contains standard "transition providers" that may be used to specify the [EnterTransition] and
* [ExitTransition] used when building a root [NavHost], which requires a non-null value.
*/
object RootTransitionProviders {
/**
* The standard set of "enter" transition providers.
*/
object Enter {
/**
* Fades the new screen in.
*/
val fadeIn: NonNullEnterTransitionProvider = {
fadeIn(tween(DEFAULT_TRANSITION_TIME_MS))
}
/**
* Slides the new screen in from the bottom of the screen.
*/
val slideUp: NonNullEnterTransitionProvider = {
slideIntoContainer(
towards = AnimatedContentTransitionScope.SlideDirection.Up,
animationSpec = tween(DEFAULT_TRANSITION_TIME_MS),
)
}
/**
* A "no-op" transition: this changes nothing about the screen but "lasts" as long as
* other standard transitions in order to leave the screen in place such that it does not
* immediately appear while the other screen transitions away.
*/
val stay: NonNullEnterTransitionProvider = {
fadeIn(
animationSpec = tween(DEFAULT_TRANSITION_TIME_MS),
initialAlpha = 1f,
)
}
}
/**
* The standard set of "exit" transition providers.
*/
object Exit {
/**
* Fades the current screen out.
*/
val fadeOut: NonNullExitTransitionProvider = {
fadeOut(tween(DEFAULT_TRANSITION_TIME_MS))
}
/**
* Slides the current screen down to the bottom of the screen.
*/
val slideDown: NonNullExitTransitionProvider = {
slideOutOfContainer(
towards = AnimatedContentTransitionScope.SlideDirection.Down,
animationSpec = tween(DEFAULT_TRANSITION_TIME_MS),
)
}
/**
* A "no-op" transition: this changes nothing about the screen but "lasts" as long as
* other standard transitions in order to leave the screen in place such that it does not
* immediately disappear while the other screen transitions into place.
*/
val stay: NonNullExitTransitionProvider = {
fadeOut(
animationSpec = tween(DEFAULT_TRANSITION_TIME_MS),
targetAlpha = 0f,
)
}
}
}

View file

@ -6,7 +6,7 @@
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:textCursorDrawable">@null</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowBackground">@null</item>
<item name="android:windowBackground">@color/surface</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowActionModeOverlay">true</item>
<item name="android:windowLayoutInDisplayCutoutMode">default</item>