diff --git a/app/src/main/java/to/bitkit/data/backup/VssBackupClient.kt b/app/src/main/java/to/bitkit/data/backup/VssBackupClient.kt index 6f0a84a28..bcb981092 100644 --- a/app/src/main/java/to/bitkit/data/backup/VssBackupClient.kt +++ b/app/src/main/java/to/bitkit/data/backup/VssBackupClient.kt @@ -29,6 +29,7 @@ class VssBackupClient @Inject constructor( suspend fun setup() = withContext(bgDispatcher) { try { withTimeout(30.seconds) { + val vssServerUrl = Env.vssServerUrl Logger.verbose("VSS client setting up…", context = TAG) if (Env.lnurlAuthSeverUrl.isNotEmpty()) { val mnemonic = keychain.loadString(Keychain.Key.BIP39_MNEMONIC.name) @@ -36,7 +37,7 @@ class VssBackupClient @Inject constructor( val passphrase = keychain.loadString(Keychain.Key.BIP39_PASSPHRASE.name) vssNewClientWithLnurlAuth( - baseUrl = Env.vssServerUrl, + baseUrl = vssServerUrl, storeId = vssStoreIdProvider.getVssStoreId(), mnemonic = mnemonic, passphrase = passphrase, @@ -44,12 +45,12 @@ class VssBackupClient @Inject constructor( ) } else { vssNewClient( - baseUrl = Env.vssServerUrl, + baseUrl = vssServerUrl, storeId = vssStoreIdProvider.getVssStoreId(), ) } isSetup.complete(Unit) - Logger.info("VSS client setup ok", context = TAG) + Logger.info("VSS client setup with server: '$vssServerUrl'", context = TAG) } } catch (e: Throwable) { isSetup.completeExceptionally(e) diff --git a/app/src/main/java/to/bitkit/ui/utils/WeViewExtensions.kt b/app/src/main/java/to/bitkit/ext/WebView.kt similarity index 96% rename from app/src/main/java/to/bitkit/ui/utils/WeViewExtensions.kt rename to app/src/main/java/to/bitkit/ext/WebView.kt index 6cf9731c9..806c637dc 100644 --- a/app/src/main/java/to/bitkit/ui/utils/WeViewExtensions.kt +++ b/app/src/main/java/to/bitkit/ext/WebView.kt @@ -1,4 +1,4 @@ -package to.bitkit.ui.utils +package to.bitkit.ext import android.annotation.SuppressLint import android.webkit.WebSettings diff --git a/app/src/main/java/to/bitkit/ui/ContentView.kt b/app/src/main/java/to/bitkit/ui/ContentView.kt index 433940c34..b623b0dba 100644 --- a/app/src/main/java/to/bitkit/ui/ContentView.kt +++ b/app/src/main/java/to/bitkit/ui/ContentView.kt @@ -24,7 +24,6 @@ import androidx.navigation.NavHostController import androidx.navigation.NavOptions import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable -import androidx.navigation.compose.navigation import androidx.navigation.compose.rememberNavController import androidx.navigation.toRoute import kotlinx.coroutines.delay @@ -139,9 +138,9 @@ import to.bitkit.ui.sheets.PinSheet import to.bitkit.ui.sheets.SendSheet import to.bitkit.ui.theme.TRANSITION_SHEET_MS import to.bitkit.ui.utils.AutoReadClipboardHandler +import to.bitkit.ui.utils.Transitions import to.bitkit.ui.utils.composableWithDefaultTransitions -import to.bitkit.ui.utils.screenSlideIn -import to.bitkit.ui.utils.screenSlideOut +import to.bitkit.ui.utils.navigationWithDefaultTransitions import to.bitkit.utils.Logger import to.bitkit.viewmodels.ActivityListViewModel import to.bitkit.viewmodels.AppViewModel @@ -411,7 +410,7 @@ private fun RootNavHost( widgets(navController, settingsViewModel, currencyViewModel) // TODO extract transferNavigation - navigation( + navigationWithDefaultTransitions( startDestination = Routes.TransferIntro, ) { composableWithDefaultTransitions { @@ -550,7 +549,7 @@ private fun RootNavHost( onCloseClick = { navController.navigateToHome() }, ) } - navigation( + navigationWithDefaultTransitions( startDestination = Routes.ExternalConnection(), ) { composableWithDefaultTransitions { @@ -902,7 +901,7 @@ private fun NavGraphBuilder.cjitDetailSettings( private fun NavGraphBuilder.lightningConnections( navController: NavHostController, ) { - navigation( + navigationWithDefaultTransitions( startDestination = Routes.LightningConnections, ) { composableWithDefaultTransitions { @@ -956,9 +955,9 @@ private fun NavGraphBuilder.qrScanner( appViewModel: AppViewModel, navController: NavHostController, ) { - composable( - enterTransition = { screenSlideIn }, - exitTransition = { screenSlideOut }, + composableWithDefaultTransitions( + enterTransition = { Transitions.slideInVertically }, + popExitTransition = { Transitions.slideOutVertically }, ) { QrScanningScreen(navController = navController) { qrCode -> appViewModel.onScanResult( @@ -999,10 +998,7 @@ private fun NavGraphBuilder.logs( private fun NavGraphBuilder.suggestions( navController: NavHostController, ) { - composable( - enterTransition = { screenSlideIn }, - exitTransition = { screenSlideOut }, - ) { + composableWithDefaultTransitions { BuyIntroScreen( onBackClick = { navController.popBackStack() } ) @@ -1088,7 +1084,7 @@ private fun NavGraphBuilder.widgets( currencyViewModel = currencyViewModel ) } - navigation( + navigationWithDefaultTransitions( startDestination = Routes.HeadlinesPreview ) { composableWithDefaultTransitions { @@ -1116,7 +1112,7 @@ private fun NavGraphBuilder.widgets( ) } } - navigation( + navigationWithDefaultTransitions( startDestination = Routes.FactsPreview ) { composableWithDefaultTransitions { @@ -1142,7 +1138,7 @@ private fun NavGraphBuilder.widgets( ) } } - navigation( + navigationWithDefaultTransitions( startDestination = Routes.BlocksPreview ) { composableWithDefaultTransitions { @@ -1168,7 +1164,7 @@ private fun NavGraphBuilder.widgets( ) } } - navigation( + navigationWithDefaultTransitions( startDestination = Routes.WeatherPreview ) { composableWithDefaultTransitions { @@ -1194,7 +1190,7 @@ private fun NavGraphBuilder.widgets( ) } } - navigation( + navigationWithDefaultTransitions( startDestination = Routes.PricePreview ) { composableWithDefaultTransitions { diff --git a/app/src/main/java/to/bitkit/ui/MainActivity.kt b/app/src/main/java/to/bitkit/ui/MainActivity.kt index f122779e1..d40f4b9b8 100644 --- a/app/src/main/java/to/bitkit/ui/MainActivity.kt +++ b/app/src/main/java/to/bitkit/ui/MainActivity.kt @@ -7,6 +7,7 @@ import androidx.activity.viewModels import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut +import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope @@ -16,11 +17,13 @@ import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.fragment.app.FragmentActivity import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.navigation.toRoute import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import kotlinx.serialization.Serializable import to.bitkit.androidServices.LightningNodeService @@ -39,11 +42,8 @@ import to.bitkit.ui.screens.SplashScreen import to.bitkit.ui.sheets.ForgotPinSheet import to.bitkit.ui.sheets.NewTransactionSheet import to.bitkit.ui.theme.AppThemeSurface +import to.bitkit.ui.utils.composableWithDefaultTransitions import to.bitkit.ui.utils.enableAppEdgeToEdge -import to.bitkit.ui.utils.screenScaleIn -import to.bitkit.ui.utils.screenScaleOut -import to.bitkit.ui.utils.screenSlideIn -import to.bitkit.ui.utils.screenSlideOut import to.bitkit.viewmodels.ActivityListViewModel import to.bitkit.viewmodels.AppViewModel import to.bitkit.viewmodels.BackupsViewModel @@ -86,137 +86,27 @@ class MainActivity : FragmentActivity() { ) { val scope = rememberCoroutineScope() if (!walletViewModel.walletExists) { - val startupNavController = rememberNavController() - NavHost( - navController = startupNavController, - startDestination = StartupRoutes.Terms, - ) { - composable { - TermsOfUseScreen( - onNavigateToIntro = { - startupNavController.navigate(StartupRoutes.Intro) - } - ) - } - composable( - enterTransition = { screenSlideIn }, - exitTransition = { screenScaleOut }, - popEnterTransition = { screenScaleIn }, - popExitTransition = { screenSlideOut }, - ) { - IntroScreen( - onStartClick = { - startupNavController.navigate(StartupRoutes.Slides()) - }, - onSkipClick = { - startupNavController.navigate(StartupRoutes.Slides(4)) - }, - ) - } - composable( - enterTransition = { screenSlideIn }, - exitTransition = { screenScaleOut }, - popEnterTransition = { screenScaleIn }, - popExitTransition = { screenSlideOut }, - ) { navBackEntry -> - val route = navBackEntry.toRoute() - OnboardingSlidesScreen( - currentTab = route.tab, - isGeoBlocked = appViewModel.isGeoBlocked == true, - onAdvancedSetupClick = { startupNavController.navigate(StartupRoutes.Advanced) }, - onCreateClick = { - scope.launch { - try { - appViewModel.resetIsAuthenticatedState() - walletViewModel.setInitNodeLifecycleState() - walletViewModel.createWallet(bip39Passphrase = null) - } catch (e: Throwable) { - appViewModel.toast(e) - } - } - }, - onRestoreClick = { - startupNavController.navigate( - StartupRoutes.WarningMultipleDevices - ) - }, - ) - } - composable( - enterTransition = { screenSlideIn }, - exitTransition = { screenScaleOut }, - popEnterTransition = { screenScaleIn }, - popExitTransition = { screenSlideOut }, - ) { - WarningMultipleDevicesScreen( - onBackClick = { - startupNavController.popBackStack() - }, - onConfirmClick = { - startupNavController.navigate(StartupRoutes.Restore) - } - ) - } - composable( - enterTransition = { screenSlideIn }, - exitTransition = { screenScaleOut }, - popEnterTransition = { screenScaleIn }, - popExitTransition = { screenSlideOut }, - ) { - RestoreWalletView( - onBackClick = { startupNavController.popBackStack() }, - onRestoreClick = { mnemonic, passphrase -> - scope.launch { - try { - appViewModel.resetIsAuthenticatedState() - walletViewModel.setInitNodeLifecycleState() - walletViewModel.setRestoringWalletState() - walletViewModel.restoreWallet(mnemonic, passphrase) - } catch (e: Throwable) { - appViewModel.toast(e) - } - } - } - ) - } - composable( - enterTransition = { screenSlideIn }, - exitTransition = { screenScaleOut }, - popEnterTransition = { screenScaleIn }, - popExitTransition = { screenSlideOut }, - ) { - CreateWalletWithPassphraseScreen( - onBackClick = { startupNavController.popBackStack() }, - onCreateClick = { passphrase -> - scope.launch { - try { - appViewModel.resetIsAuthenticatedState() - walletViewModel.setInitNodeLifecycleState() - walletViewModel.createWallet(bip39Passphrase = passphrase) - } catch (e: Throwable) { - appViewModel.toast(e) - } - } - }, - ) - } - } + OnboardingNav( + startupNavController = rememberNavController(), + scope = scope, + appViewModel = appViewModel, + walletViewModel = walletViewModel, + ) } else { val isAuthenticated by appViewModel.isAuthenticated.collectAsStateWithLifecycle() - IsOnlineTracker(appViewModel) { - InactivityTracker(appViewModel, settingsViewModel) { - ContentView( - appViewModel = appViewModel, - walletViewModel = walletViewModel, - blocktankViewModel = blocktankViewModel, - currencyViewModel = currencyViewModel, - activityListViewModel = activityListViewModel, - transferViewModel = transferViewModel, - settingsViewModel = settingsViewModel, - backupsViewModel = backupsViewModel, - ) - } + IsOnlineTracker(appViewModel) + InactivityTracker(appViewModel, settingsViewModel) { + ContentView( + appViewModel = appViewModel, + walletViewModel = walletViewModel, + blocktankViewModel = blocktankViewModel, + currencyViewModel = currencyViewModel, + activityListViewModel = activityListViewModel, + transferViewModel = transferViewModel, + settingsViewModel = settingsViewModel, + backupsViewModel = backupsViewModel, + ) } AnimatedVisibility( @@ -271,7 +161,107 @@ class MainActivity : FragmentActivity() { } } +@Composable +private fun OnboardingNav( + startupNavController: NavHostController, + scope: CoroutineScope, + appViewModel: AppViewModel, + walletViewModel: WalletViewModel, +) { + NavHost( + navController = startupNavController, + startDestination = StartupRoutes.Terms, + ) { + composable { + TermsOfUseScreen( + onNavigateToIntro = { + startupNavController.navigate(StartupRoutes.Intro) + } + ) + } + composableWithDefaultTransitions { + IntroScreen( + onStartClick = { + startupNavController.navigate(StartupRoutes.Slides()) + }, + onSkipClick = { + startupNavController.navigate(StartupRoutes.Slides(StartupRoutes.LAST_SLIDE_INDEX)) + }, + ) + } + composableWithDefaultTransitions { navBackEntry -> + val route = navBackEntry.toRoute() + OnboardingSlidesScreen( + currentTab = route.tab, + isGeoBlocked = appViewModel.isGeoBlocked == true, + onAdvancedSetupClick = { startupNavController.navigate(StartupRoutes.Advanced) }, + onCreateClick = { + scope.launch { + runCatching { + appViewModel.resetIsAuthenticatedState() + walletViewModel.setInitNodeLifecycleState() + walletViewModel.createWallet(bip39Passphrase = null) + }.onFailure { + appViewModel.toast(it) + } + } + }, + onRestoreClick = { + startupNavController.navigate( + StartupRoutes.WarningMultipleDevices + ) + }, + ) + } + composableWithDefaultTransitions { + WarningMultipleDevicesScreen( + onBackClick = { + startupNavController.popBackStack() + }, + onConfirmClick = { + startupNavController.navigate(StartupRoutes.Restore) + } + ) + } + composableWithDefaultTransitions { + RestoreWalletView( + onBackClick = { startupNavController.popBackStack() }, + onRestoreClick = { mnemonic, passphrase -> + scope.launch { + runCatching { + appViewModel.resetIsAuthenticatedState() + walletViewModel.setInitNodeLifecycleState() + walletViewModel.setRestoringWalletState() + walletViewModel.restoreWallet(mnemonic, passphrase) + }.onFailure { + appViewModel.toast(it) + } + } + } + ) + } + composableWithDefaultTransitions { + CreateWalletWithPassphraseScreen( + onBackClick = { startupNavController.popBackStack() }, + onCreateClick = { passphrase -> + scope.launch { + runCatching { + appViewModel.resetIsAuthenticatedState() + walletViewModel.setInitNodeLifecycleState() + walletViewModel.createWallet(bip39Passphrase = passphrase) + }.onFailure { + appViewModel.toast(it) + } + } + }, + ) + } + } +} + private object StartupRoutes { + const val LAST_SLIDE_INDEX = 4 + @Serializable data object Terms diff --git a/app/src/main/java/to/bitkit/ui/components/IsOnlineTracker.kt b/app/src/main/java/to/bitkit/ui/components/IsOnlineTracker.kt index 9477c8e9b..1371b15d4 100644 --- a/app/src/main/java/to/bitkit/ui/components/IsOnlineTracker.kt +++ b/app/src/main/java/to/bitkit/ui/components/IsOnlineTracker.kt @@ -3,9 +3,6 @@ package to.bitkit.ui.components 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.setValue import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.compose.collectAsStateWithLifecycle import to.bitkit.R @@ -16,20 +13,11 @@ import to.bitkit.viewmodels.AppViewModel @Composable fun IsOnlineTracker( app: AppViewModel, - content: @Composable () -> Unit, ) { val context = LocalContext.current val connectivityState by app.isOnline.collectAsStateWithLifecycle(initialValue = ConnectivityState.CONNECTED) - var isInitialized by remember { mutableStateOf(false) } - LaunchedEffect(connectivityState) { - // Skip the first emission to prevent toast on startup - if (!isInitialized) { - isInitialized = true - return@LaunchedEffect - } - when (connectivityState) { ConnectivityState.CONNECTED -> { app.toast( @@ -50,6 +38,4 @@ fun IsOnlineTracker( else -> Unit } } - - content() } diff --git a/app/src/main/java/to/bitkit/ui/components/SuggestionCard.kt b/app/src/main/java/to/bitkit/ui/components/SuggestionCard.kt index 75907978f..92aea1ddc 100644 --- a/app/src/main/java/to/bitkit/ui/components/SuggestionCard.kt +++ b/app/src/main/java/to/bitkit/ui/components/SuggestionCard.kt @@ -81,28 +81,27 @@ fun SuggestionCard( if (isDismissible || disableGlow) { Modifier.gradientBackground(gradientColor) } else { - val (shadowColor, borderColor, gradientSelectedColor) = when (gradientColor) { - Colors.Purple24 -> Triple( - Color(130, 65, 175), - Color(185, 92, 232), - Color(65, 32, 80) - ) + val (borderColor, centerColor) = + @Suppress("MagicNumber") + when (gradientColor) { + Colors.Purple24 -> Pair( + Color(185, 92, 232), + Color(65, 32, 80) + ) - Colors.Red24 -> Triple( - Color(200, 48, 0), - Color(255, 68, 0), - Color(100, 24, 0) - ) + Colors.Red24 -> Pair( + Color(255, 68, 0), + Color(100, 24, 0) + ) - else -> Triple( - gradientColor, - gradientColor, - gradientColor.copy(alpha = MIN_ALPHA_GRADIENT) - ) - } + else -> Pair( + gradientColor, + gradientColor.copy(alpha = MIN_ALPHA_GRADIENT) + ) + } Modifier - .gradientRadialBackground(gradientSelectedColor, glowAlpha) + .gradientRadialBackground(centerColor, glowAlpha) .border(width = 1.dp, color = borderColor, shape = ShapeDefaults.Large) } ) diff --git a/app/src/main/java/to/bitkit/ui/screens/shop/shopDiscover/ShopDiscoverScreen.kt b/app/src/main/java/to/bitkit/ui/screens/shop/shopDiscover/ShopDiscoverScreen.kt index 1b138bdcc..0050bad33 100644 --- a/app/src/main/java/to/bitkit/ui/screens/shop/shopDiscover/ShopDiscoverScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/shop/shopDiscover/ShopDiscoverScreen.kt @@ -39,6 +39,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import to.bitkit.R import to.bitkit.env.Env +import to.bitkit.ext.configureForBasicWebContent import to.bitkit.models.BitrefillCategory import to.bitkit.ui.components.BodyM import to.bitkit.ui.components.CaptionB @@ -53,7 +54,6 @@ import to.bitkit.ui.shared.util.gradientBackground import to.bitkit.ui.theme.AppThemeSurface import to.bitkit.ui.theme.Colors import to.bitkit.ui.theme.Shapes -import to.bitkit.ui.utils.configureForBasicWebContent private const val SHOP_CARD_SIZE = 164 diff --git a/app/src/main/java/to/bitkit/ui/screens/shop/shopWebView/ShopWebViewScreen.kt b/app/src/main/java/to/bitkit/ui/screens/shop/shopWebView/ShopWebViewScreen.kt index 9c483f7db..00e19df4f 100644 --- a/app/src/main/java/to/bitkit/ui/screens/shop/shopWebView/ShopWebViewScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/shop/shopWebView/ShopWebViewScreen.kt @@ -19,11 +19,11 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.viewinterop.AndroidView import to.bitkit.R import to.bitkit.env.Env +import to.bitkit.ext.configureForBasicWebContent import to.bitkit.ui.scaffold.AppTopBar import to.bitkit.ui.scaffold.CloseNavIcon import to.bitkit.ui.scaffold.ScreenColumn import to.bitkit.ui.theme.AppThemeSurface -import to.bitkit.ui.utils.configureForBasicWebContent @SuppressLint("SetJavaScriptEnabled", "JavascriptInterface") @Composable diff --git a/app/src/main/java/to/bitkit/ui/screens/transfer/TransferIntroScreen.kt b/app/src/main/java/to/bitkit/ui/screens/transfer/TransferIntroScreen.kt index b79aebbe9..39a837151 100644 --- a/app/src/main/java/to/bitkit/ui/screens/transfer/TransferIntroScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/transfer/TransferIntroScreen.kt @@ -1,6 +1,7 @@ package to.bitkit.ui.screens.transfer import androidx.compose.foundation.Image +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -9,6 +10,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -37,6 +39,7 @@ fun TransferIntroScreen( contentAlignment = Alignment.TopCenter, modifier = Modifier .fillMaxSize() + .background(MaterialTheme.colorScheme.background) .systemBarsPadding() ) { Image( @@ -73,9 +76,9 @@ fun TransferIntroScreen( } } -@Preview(showSystemUi = true, showBackground = true) +@Preview(showSystemUi = true) @Composable -private fun TransferIntroScreenPreview() { +private fun Preview() { AppThemeSurface { TransferIntroScreen() } diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/HomeNav.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/HomeNav.kt index 69e6085c7..60031f292 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/HomeNav.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/HomeNav.kt @@ -29,8 +29,7 @@ import to.bitkit.ui.navigateToTransferSpendingAmount import to.bitkit.ui.navigateToTransferSpendingIntro import to.bitkit.ui.screens.wallets.activity.AllActivityScreen import to.bitkit.ui.utils.RequestNotificationPermissions -import to.bitkit.ui.utils.screenSlideIn -import to.bitkit.ui.utils.screenSlideOut +import to.bitkit.ui.utils.Transitions import to.bitkit.viewmodels.ActivityListViewModel import to.bitkit.viewmodels.AppViewModel import to.bitkit.viewmodels.MainUiState @@ -122,8 +121,8 @@ private fun NavContent( ) } composable( - enterTransition = { screenSlideIn }, - exitTransition = { screenSlideOut }, + enterTransition = { Transitions.slideInHorizontally }, + exitTransition = { Transitions.slideOutHorizontally }, ) { val hasSeenSpendingIntro by settingsViewModel.hasSeenSpendingIntro.collectAsStateWithLifecycle() SavingsWalletScreen( @@ -141,8 +140,8 @@ private fun NavContent( ) } composable( - enterTransition = { screenSlideIn }, - exitTransition = { screenSlideOut }, + enterTransition = { Transitions.slideInHorizontally }, + exitTransition = { Transitions.slideOutHorizontally }, ) { val hasSeenSavingsIntro by settingsViewModel.hasSeenSavingsIntro.collectAsStateWithLifecycle() SpendingWalletScreen( @@ -161,8 +160,8 @@ private fun NavContent( ) } composable( - enterTransition = { screenSlideIn }, - exitTransition = { screenSlideOut }, + enterTransition = { Transitions.slideInHorizontally }, + exitTransition = { Transitions.slideOutHorizontally }, ) { AllActivityScreen( viewModel = activityListViewModel, diff --git a/app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt b/app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt index c35e9aa28..9f8d3f16c 100644 --- a/app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt +++ b/app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt @@ -12,7 +12,6 @@ import androidx.compose.ui.platform.testTag import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.compose.NavHost -import androidx.navigation.compose.navigation import androidx.navigation.compose.rememberNavController import androidx.navigation.toRoute import kotlinx.serialization.Serializable @@ -36,6 +35,7 @@ import to.bitkit.ui.screens.wallets.withdraw.WithdrawErrorScreen import to.bitkit.ui.settings.support.SupportScreen import to.bitkit.ui.shared.modifiers.sheetHeight import to.bitkit.ui.utils.composableWithDefaultTransitions +import to.bitkit.ui.utils.navigationWithDefaultTransitions import to.bitkit.viewmodels.AppViewModel import to.bitkit.viewmodels.SendEffect import to.bitkit.viewmodels.SendEvent @@ -127,7 +127,7 @@ fun SendSheet( onContinue = { utxos -> appViewModel.setSendEvent(SendEvent.CoinSelectionContinue(utxos)) }, ) } - navigation( + navigationWithDefaultTransitions( startDestination = SendRoute.FeeRate, ) { composableWithDefaultTransitions { diff --git a/app/src/main/java/to/bitkit/ui/utils/Nav.kt b/app/src/main/java/to/bitkit/ui/utils/Nav.kt deleted file mode 100644 index 162038ac7..000000000 --- a/app/src/main/java/to/bitkit/ui/utils/Nav.kt +++ /dev/null @@ -1,70 +0,0 @@ -package to.bitkit.ui.utils - -import androidx.compose.animation.AnimatedContentScope -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.compose.animation.scaleIn -import androidx.compose.animation.scaleOut -import androidx.compose.animation.slideInHorizontally -import androidx.compose.animation.slideOutHorizontally -import androidx.compose.runtime.Composable -import androidx.navigation.NavBackStackEntry -import androidx.navigation.NavDeepLink -import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavOptionsBuilder -import androidx.navigation.NavType -import androidx.navigation.compose.composable -import kotlin.reflect.KType - -fun NavOptionsBuilder.clearBackStack() = popUpTo(id = 0) - -// region transitions - -/** enterTransition */ -val screenSlideIn = slideInHorizontally(animationSpec = tween(), initialOffsetX = { it }) - -/** exitTransition */ -val screenSlideOut = slideOutHorizontally(animationSpec = tween(), targetOffsetX = { it }) - -/** popEnterTransition */ -val screenScaleIn = scaleIn(animationSpec = tween(), initialScale = 0.95f) + fadeIn() - -/** popExitTransition */ -val screenScaleOut = scaleOut(animationSpec = tween(), targetScale = 0.95f) + fadeOut() - -// endregion - -/** - * Adds the [androidx.compose.runtime.Composable] to the [androidx.navigation.NavGraphBuilder] with the default screen transitions. - */ -inline fun NavGraphBuilder.composableWithDefaultTransitions( - typeMap: Map> = emptyMap(), - deepLinks: List = emptyList(), - noinline enterTransition: (AnimatedContentTransitionScope.() -> EnterTransition?)? = { - screenSlideIn - }, - noinline exitTransition: (AnimatedContentTransitionScope.() -> ExitTransition?)? = { - screenScaleOut - }, - noinline popEnterTransition: (AnimatedContentTransitionScope.() -> EnterTransition?)? = { - screenScaleIn - }, - noinline popExitTransition: (AnimatedContentTransitionScope.() -> ExitTransition?)? = { - screenSlideOut - }, - noinline content: @Composable AnimatedContentScope.(NavBackStackEntry) -> Unit, -) { - composable( - typeMap = typeMap, - deepLinks = deepLinks, - enterTransition = enterTransition, - exitTransition = exitTransition, - popEnterTransition = popEnterTransition, - popExitTransition = popExitTransition, - content = content, - ) -} diff --git a/app/src/main/java/to/bitkit/ui/utils/Transitions.kt b/app/src/main/java/to/bitkit/ui/utils/Transitions.kt new file mode 100644 index 000000000..6c1fefb2e --- /dev/null +++ b/app/src/main/java/to/bitkit/ui/utils/Transitions.kt @@ -0,0 +1,99 @@ +package to.bitkit.ui.utils + +import androidx.compose.animation.AnimatedContentScope +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.compose.animation.scaleIn +import androidx.compose.animation.scaleOut +import androidx.compose.animation.slideInHorizontally +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutHorizontally +import androidx.compose.animation.slideOutVertically +import androidx.compose.runtime.Composable +import androidx.navigation.NavBackStackEntry +import androidx.navigation.NavDeepLink +import androidx.navigation.NavGraph +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavType +import androidx.navigation.compose.composable +import androidx.navigation.compose.navigation +import kotlin.reflect.KType + +object Transitions { + val slideInHorizontally = slideInHorizontally(animationSpec = tween(), initialOffsetX = { it }) + val slideOutHorizontally = slideOutHorizontally(animationSpec = tween(), targetOffsetX = { it }) + val scaleIn = scaleIn(animationSpec = tween(), initialScale = 0.95f) + fadeIn() + val scaleOut = scaleOut(animationSpec = tween(), targetScale = 0.95f) + fadeOut() + val slideInVertically = slideInVertically(animationSpec = tween(), initialOffsetY = { it }) + val slideOutVertically = slideOutVertically(animationSpec = tween(), targetOffsetY = { it }) +} + +/** + * Adds the [Composable] to the [NavGraphBuilder] with the default screen transitions. + */ +@Suppress("LongParameterList") +inline fun NavGraphBuilder.composableWithDefaultTransitions( + typeMap: Map> = emptyMap(), + deepLinks: List = emptyList(), + noinline enterTransition: (AnimatedContentTransitionScope.() -> EnterTransition?)? = { + Transitions.slideInHorizontally + }, + noinline exitTransition: (AnimatedContentTransitionScope.() -> ExitTransition?)? = { + Transitions.scaleOut + }, + noinline popEnterTransition: (AnimatedContentTransitionScope.() -> EnterTransition?)? = { + Transitions.scaleIn + }, + noinline popExitTransition: (AnimatedContentTransitionScope.() -> ExitTransition?)? = { + Transitions.slideOutHorizontally + }, + noinline content: @Composable AnimatedContentScope.(NavBackStackEntry) -> Unit, +) { + composable( + typeMap = typeMap, + deepLinks = deepLinks, + enterTransition = enterTransition, + exitTransition = exitTransition, + popEnterTransition = popEnterTransition, + popExitTransition = popExitTransition, + content = content, + ) +} + +/** + * Construct a nested [NavGraph] with the default screen transitions. + */ +@Suppress("LongParameterList") +inline fun NavGraphBuilder.navigationWithDefaultTransitions( + startDestination: Any, + typeMap: Map> = emptyMap(), + deepLinks: List = emptyList(), + noinline enterTransition: (AnimatedContentTransitionScope.() -> EnterTransition?)? = { + Transitions.slideInHorizontally + }, + noinline exitTransition: (AnimatedContentTransitionScope.() -> ExitTransition?)? = { + Transitions.scaleOut + }, + noinline popEnterTransition: (AnimatedContentTransitionScope.() -> EnterTransition?)? = { + Transitions.scaleIn + }, + noinline popExitTransition: (AnimatedContentTransitionScope.() -> ExitTransition?)? = { + Transitions.slideOutHorizontally + }, + noinline builder: NavGraphBuilder.() -> Unit, +) { + navigation( + startDestination = startDestination, + typeMap = typeMap, + deepLinks = deepLinks, + enterTransition = enterTransition, + exitTransition = exitTransition, + popEnterTransition = popEnterTransition, + popExitTransition = popExitTransition, + builder = builder, + ) +} diff --git a/app/src/main/java/to/bitkit/viewmodels/TransferViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/TransferViewModel.kt index 6ad4a665b..6b261a0e0 100644 --- a/app/src/main/java/to/bitkit/viewmodels/TransferViewModel.kt +++ b/app/src/main/java/to/bitkit/viewmodels/TransferViewModel.kt @@ -537,7 +537,6 @@ class TransferViewModel @Inject constructor( companion object { private const val TAG = "TransferViewModel" - private const val RETRY_LIMIT = 5 private const val QUARTER = 0.25 } }