From 7fd9339fa195ec13b46e79e461dec4757e23f57e Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Wed, 19 Aug 2026 18:40:20 -0400 Subject: [PATCH] feat(withdraw): picker-first withdrawal refresh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ports iOS #600. The withdraw flow no longer assumes Dollars: the route now takes a `preselectedMint` that decides where the flow opens. - `null` -> the currency picker (the v2 "Withdraw Money" wallet tile) - Dollars/USDF -> the "Withdraw as USDC" intro, as before - anything else -> straight to the amount screen Under the new UI, picking Dollars in the picker detours through the intro, the intro drops its "Withdraw Other Flipcash Currencies" escape hatch (every currency is one step back now) and its app-bar title, and each step names itself: "Amount to Withdraw", "Enter Address". v1 is untouched — the route still defaults to Dollars, so the settings entry keeps the intro-first flow, the escape hatch, and the flow-wide "Withdraw" title. Also fixes the v2 currency-info Withdraw tile, which started the Dollars flow no matter which currency was on screen. The v2 intro art is composed rather than flattened: the gold Dollars coin (new) -> arrow (new) -> the existing USDC-on-Solana mark. --- .../kotlin/com/flipcash/app/core/AppRoute.kt | 22 +++++- .../res/drawable-xxhdpi/ic_coin_dollars.webp | Bin 0 -> 18782 bytes .../src/main/res/drawable/ic_arrow_right.xml | 13 ++++ .../core/src/main/res/values/strings.xml | 4 + .../balance/internal/WalletScreenContent.kt | 8 +- .../components/info/CurrencyInfoContentV2.kt | 7 +- .../features/withdrawal/build.gradle.kts | 1 + .../app/withdrawal/WithdrawalFlowScreen.kt | 17 +++- .../UsdcWithdrawalInformationScreen.kt | 73 ++++++++++++++---- .../screens/WithdrawalDestinationScreen.kt | 11 ++- .../internal/screens/WithdrawalEntryScreen.kt | 11 ++- 11 files changed, 146 insertions(+), 21 deletions(-) create mode 100644 apps/flipcash/core/src/main/res/drawable-xxhdpi/ic_coin_dollars.webp create mode 100644 apps/flipcash/core/src/main/res/drawable/ic_arrow_right.xml diff --git a/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/AppRoute.kt b/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/AppRoute.kt index 6416f35189..83a3b9a93e 100644 --- a/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/AppRoute.kt +++ b/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/AppRoute.kt @@ -265,10 +265,28 @@ sealed interface AppRoute : NavKey, Parcelable { get() = listOf(DepositStep.UsdcInformational(showOtherOptions)) } + /** + * The withdraw flow. + * + * [preselectedMint] picks the entry step: `null` opens the currency picker (the v2 + * "Withdraw Money" tile and the v2 settings entry), Dollars/USDF detours through the + * "Withdraw as USDC" intro, and any other currency lands straight on the amount screen. + * + * [showOtherOptions] is the intro's legacy "Withdraw Other Flipcash Currencies" escape + * hatch — v1 only, since v2 reaches every currency through the picker. + */ @Serializable - data class Withdrawal(val showOtherOptions: Boolean = true) : Transfers, FlowRouteWithResult { + data class Withdrawal( + val showOtherOptions: Boolean = true, + val preselectedMint: Mint? = Mint.usdf, + ) : Transfers, FlowRouteWithResult { override val initialStack: List - get() = listOf(WithdrawalStep.UsdcInformational(showOtherOptions)) + get() = when (preselectedMint) { + null -> listOf(WithdrawalStep.SelectToken) + // The flow models USDF→USDC as a USDC withdrawal, so both mints mean the reserve. + Mint.usdf, Mint.usdc -> listOf(WithdrawalStep.UsdcInformational(showOtherOptions)) + else -> listOf(WithdrawalStep.Amount(preselectedMint)) + } } } diff --git a/apps/flipcash/core/src/main/res/drawable-xxhdpi/ic_coin_dollars.webp b/apps/flipcash/core/src/main/res/drawable-xxhdpi/ic_coin_dollars.webp new file mode 100644 index 0000000000000000000000000000000000000000..a6c603a9e818523f4cbbd140fed3db08c2379cf7 GIT binary patch literal 18782 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
  • D%FwVcYTkJ97R4a#+Hs3^S@i9}I5Mc&0mBc(<;k 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 ZQmQA;=3pQ>6hr&~m`tFk2mk;8001R^`?>%C literal 0 HcmV?d00001 diff --git a/apps/flipcash/core/src/main/res/drawable/ic_arrow_right.xml b/apps/flipcash/core/src/main/res/drawable/ic_arrow_right.xml new file mode 100644 index 0000000000..cd8ece0038 --- /dev/null +++ b/apps/flipcash/core/src/main/res/drawable/ic_arrow_right.xml @@ -0,0 +1,13 @@ + + + + diff --git a/apps/flipcash/core/src/main/res/values/strings.xml b/apps/flipcash/core/src/main/res/values/strings.xml index 141425cd1e..d251530f62 100644 --- a/apps/flipcash/core/src/main/res/values/strings.xml +++ b/apps/flipcash/core/src/main/res/values/strings.xml @@ -185,6 +185,8 @@ You should download Flipcash, a new way to send cash\n\n%1$s Enter Solana address + + Enter Address Where would you like to withdraw your %1$s to? Enter up to %1$s You can only give up to %1$s @@ -773,6 +775,8 @@ Withdraw as USDC Your USDF will be converted 1:1 to Solana USDC on withdrawal + + Your Dollars will be converted 1:1 to USDC on Solana Solana USDC USDC USDF diff --git a/apps/flipcash/features/balance/src/main/kotlin/com/flipcash/app/balance/internal/WalletScreenContent.kt b/apps/flipcash/features/balance/src/main/kotlin/com/flipcash/app/balance/internal/WalletScreenContent.kt index 9b120b233e..9fb8ccc309 100644 --- a/apps/flipcash/features/balance/src/main/kotlin/com/flipcash/app/balance/internal/WalletScreenContent.kt +++ b/apps/flipcash/features/balance/src/main/kotlin/com/flipcash/app/balance/internal/WalletScreenContent.kt @@ -224,7 +224,13 @@ internal fun WalletScreenContent( text = stringResource(R.string.action_withdrawMoney), icon = painterResource(R.drawable.ic_menu_withdraw), ) { - dispatchEvent(WalletViewModel.Event.OpenScreen(AppRoute.Transfers.Withdrawal())) + // No preselected mint: the flow opens on the currency picker, which lists + // every balance (Dollars included). + dispatchEvent( + WalletViewModel.Event.OpenScreen( + AppRoute.Transfers.Withdrawal(preselectedMint = null) + ) + ) } } diff --git a/apps/flipcash/features/tokens/src/main/kotlin/com/flipcash/app/tokens/internal/components/info/CurrencyInfoContentV2.kt b/apps/flipcash/features/tokens/src/main/kotlin/com/flipcash/app/tokens/internal/components/info/CurrencyInfoContentV2.kt index 81e0a438c0..2bc03e946d 100644 --- a/apps/flipcash/features/tokens/src/main/kotlin/com/flipcash/app/tokens/internal/components/info/CurrencyInfoContentV2.kt +++ b/apps/flipcash/features/tokens/src/main/kotlin/com/flipcash/app/tokens/internal/components/info/CurrencyInfoContentV2.kt @@ -400,9 +400,14 @@ private fun CurrencyActionTiles( ) }, onClick = { + // Preselect the currency being viewed: Dollars detours through the + // "Withdraw as USDC" intro, anything else opens straight on the amount screen. dispatch( TokenInfoViewModel.Event.OpenScreen( - AppRoute.Transfers.Withdrawal(showOtherOptions = false) + AppRoute.Transfers.Withdrawal( + showOtherOptions = false, + preselectedMint = tokenMint, + ) ) ) }, diff --git a/apps/flipcash/features/withdrawal/build.gradle.kts b/apps/flipcash/features/withdrawal/build.gradle.kts index bb01cdd496..a2a6ac1bfc 100644 --- a/apps/flipcash/features/withdrawal/build.gradle.kts +++ b/apps/flipcash/features/withdrawal/build.gradle.kts @@ -13,6 +13,7 @@ dependencies { testImplementation(libs.bundles.compose.ui.testing) implementation(project(":apps:flipcash:shared:amount-entry")) + implementation(project(":apps:flipcash:shared:featureflags")) implementation(project(":apps:flipcash:shared:transaction-history")) implementation(project(":apps:flipcash:shared:analytics")) implementation(project(":apps:flipcash:shared:tokens")) diff --git a/apps/flipcash/features/withdrawal/src/main/kotlin/com/flipcash/app/withdrawal/WithdrawalFlowScreen.kt b/apps/flipcash/features/withdrawal/src/main/kotlin/com/flipcash/app/withdrawal/WithdrawalFlowScreen.kt index e021550a1f..d757e0565b 100644 --- a/apps/flipcash/features/withdrawal/src/main/kotlin/com/flipcash/app/withdrawal/WithdrawalFlowScreen.kt +++ b/apps/flipcash/features/withdrawal/src/main/kotlin/com/flipcash/app/withdrawal/WithdrawalFlowScreen.kt @@ -20,6 +20,8 @@ import com.flipcash.app.core.AppRoute import com.flipcash.app.core.tokens.TokenPurpose import com.flipcash.app.core.withdrawal.WithdrawalResult import com.flipcash.app.core.withdrawal.WithdrawalStep +import com.flipcash.app.featureflags.FeatureFlag +import com.flipcash.app.featureflags.LocalFeatureFlags import com.flipcash.app.theme.FlipcashThemeWrapper import com.flipcash.app.tokens.ui.SelectTokenViewModel import com.flipcash.app.tokens.ui.TokenList @@ -39,6 +41,7 @@ import com.getcode.navigation.flow.rememberFlowNavigator import com.getcode.navigation.flow.rememberInitialStack import com.getcode.navigation.results.NavResultOrCanceled import com.getcode.navigation.results.NavResultStateRegistry +import com.getcode.solana.keys.Mint import com.getcode.ui.components.AppBarWithTitle import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterIsInstance @@ -108,6 +111,8 @@ private fun WithdrawalSelectTokenScreen() { val flowNavigator = rememberFlowNavigator() val viewModel = hiltViewModel() val state by viewModel.stateFlow.collectAsStateWithLifecycle() + val isNewUi by LocalFeatureFlags.current.observe(FeatureFlag.NewUi) + .collectAsStateWithLifecycle() Column( modifier = Modifier.fillMaxSize(), @@ -130,13 +135,21 @@ private fun WithdrawalSelectTokenScreen() { viewModel.dispatchEvent(SelectTokenViewModel.Event.OnPurposeChanged(TokenPurpose.Withdraw)) } - LaunchedEffect(viewModel) { + LaunchedEffect(viewModel, isNewUi) { viewModel.eventFlow .filterIsInstance() .filter { it.fromUser } .map { it.mint } .onEach { mint -> - flowNavigator.navigateTo(WithdrawalStep.Amount(mint)) + // v2 makes the picker the flow's entry, so Dollars has to detour through the + // "Withdraw as USDC" intro here — v1 reaches that intro first and only lands on the + // picker via its escape hatch, so a pick there always goes straight to the amount. + val isReserve = mint == Mint.usdf || mint == Mint.usdc + if (isNewUi && isReserve) { + flowNavigator.navigateTo(WithdrawalStep.UsdcInformational(showOtherOptions = false)) + } else { + flowNavigator.navigateTo(WithdrawalStep.Amount(mint)) + } }.launchIn(this) } } diff --git a/apps/flipcash/features/withdrawal/src/main/kotlin/com/flipcash/app/withdrawal/internal/screens/UsdcWithdrawalInformationScreen.kt b/apps/flipcash/features/withdrawal/src/main/kotlin/com/flipcash/app/withdrawal/internal/screens/UsdcWithdrawalInformationScreen.kt index 016bf18200..4d1dd4b1a6 100644 --- a/apps/flipcash/features/withdrawal/src/main/kotlin/com/flipcash/app/withdrawal/internal/screens/UsdcWithdrawalInformationScreen.kt +++ b/apps/flipcash/features/withdrawal/src/main/kotlin/com/flipcash/app/withdrawal/internal/screens/UsdcWithdrawalInformationScreen.kt @@ -10,23 +10,24 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ArrowForward -import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.flipcash.app.core.withdrawal.WithdrawalResult import com.flipcash.app.core.withdrawal.WithdrawalStep +import com.flipcash.app.featureflags.FeatureFlag +import com.flipcash.app.featureflags.LocalFeatureFlags import com.flipcash.core.R import com.getcode.navigation.flow.rememberFlowNavigator import com.getcode.solana.keys.Mint import com.getcode.theme.CodeTheme -import com.getcode.theme.White20 import com.getcode.ui.components.AppBarWithTitle import com.getcode.ui.theme.ButtonState import com.getcode.ui.theme.CodeButton @@ -35,11 +36,15 @@ import com.getcode.ui.theme.CodeScaffold @Composable internal fun UsdcWithdrawalInformationScreen(showOtherOptions: Boolean) { val flowNavigator = rememberFlowNavigator() + val isNewUi by LocalFeatureFlags.current.observe(FeatureFlag.NewUi) + .collectAsStateWithLifecycle() CodeScaffold( topBar = { AppBarWithTitle( - title = stringResource(R.string.title_withdraw), + // v2 reaches this screen mid-flow (picker → Dollars), where the screen's own heading + // already names it; v1 enters here, so it keeps the "Withdraw" title. + title = if (isNewUi) "" else stringResource(R.string.title_withdraw), onBackIconClicked = { flowNavigator.back() }, titleAlignment = Alignment.CenterHorizontally, ) @@ -60,7 +65,9 @@ internal fun UsdcWithdrawalInformationScreen(showOtherOptions: Boolean) { flowNavigator.navigateTo(WithdrawalStep.Amount(Mint.usdc)) } - if (showOtherOptions) { + // The escape hatch is a v1 affordance: v2 enters the flow on the picker, so every + // other currency is already one step back. + if (showOtherOptions && !isNewUi) { CodeButton( modifier = Modifier .fillMaxWidth(), @@ -88,14 +95,18 @@ internal fun UsdcWithdrawalInformationScreen(showOtherOptions: Boolean) { modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center, ) { - Image( - painter = painterResource(R.drawable.ic_withdraw_usdf_as_usdc), - contentDescription = null, - ) + if (isNewUi) { + ConversionGraphic() + } else { + Image( + painter = painterResource(R.drawable.ic_withdraw_usdf_as_usdc), + contentDescription = null, + ) + } } Column( - modifier = Modifier.fillMaxWidth(0.60f), + modifier = Modifier.fillMaxWidth(if (isNewUi) 0.80f else 0.60f), verticalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x3), horizontalAlignment = Alignment.CenterHorizontally, ) { @@ -105,7 +116,13 @@ internal fun UsdcWithdrawalInformationScreen(showOtherOptions: Boolean) { color = CodeTheme.colors.textMain, ) Text( - text = stringResource(R.string.description_withdrawUsdfAsUsdc), + text = stringResource( + if (isNewUi) { + R.string.description_withdrawDollarsAsUsdc + } else { + R.string.description_withdrawUsdfAsUsdc + } + ), style = CodeTheme.typography.textSmall, color = CodeTheme.colors.textSecondary, textAlign = TextAlign.Center, @@ -114,4 +131,34 @@ internal fun UsdcWithdrawalInformationScreen(showOtherOptions: Boolean) { } } } -} \ No newline at end of file +} + +/** + * v2 "Dollars → USDC" graphic (Figma node 9216:19798). The v1 art carried the Flipcash "F" mark on + * the left; v2 swaps in the gold Dollars coin, so the two halves are composed here rather than + * shipped as one flattened asset. + */ +@Composable +private fun ConversionGraphic() { + Row( + verticalAlignment = Alignment.CenterVertically, + // 14 dp between each pair, matching the 278x124 Figma frame's coin/arrow gaps. + horizontalArrangement = Arrangement.spacedBy(14.dp), + ) { + Image( + modifier = Modifier.size(100.dp), + painter = painterResource(R.drawable.ic_coin_dollars), + contentDescription = null, + ) + Image( + modifier = Modifier.size(28.dp), + painter = painterResource(R.drawable.ic_arrow_right), + contentDescription = null, + ) + // Natural size (111x112): the 100 dp coin plus the Solana badge overhanging its corner. + Image( + painter = painterResource(R.drawable.ic_usdc_on_solana), + contentDescription = null, + ) + } +} diff --git a/apps/flipcash/features/withdrawal/src/main/kotlin/com/flipcash/app/withdrawal/internal/screens/WithdrawalDestinationScreen.kt b/apps/flipcash/features/withdrawal/src/main/kotlin/com/flipcash/app/withdrawal/internal/screens/WithdrawalDestinationScreen.kt index 45709489ef..a426673a4d 100644 --- a/apps/flipcash/features/withdrawal/src/main/kotlin/com/flipcash/app/withdrawal/internal/screens/WithdrawalDestinationScreen.kt +++ b/apps/flipcash/features/withdrawal/src/main/kotlin/com/flipcash/app/withdrawal/internal/screens/WithdrawalDestinationScreen.kt @@ -4,11 +4,15 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.flipcash.app.core.withdrawal.WithdrawalResult import com.flipcash.app.core.withdrawal.WithdrawalStep +import com.flipcash.app.featureflags.FeatureFlag +import com.flipcash.app.featureflags.LocalFeatureFlags import com.flipcash.app.withdrawal.WithdrawalViewModel import com.flipcash.app.withdrawal.internal.destination.WithdrawalDestinationScreen import com.flipcash.core.R @@ -23,12 +27,17 @@ import kotlinx.coroutines.flow.onEach internal fun WithdrawalDestinationScreen() { val flowNavigator = rememberFlowNavigator() val viewModel = flowSharedViewModel() + val isNewUi by LocalFeatureFlags.current.observe(FeatureFlag.NewUi) + .collectAsStateWithLifecycle() Column( modifier = Modifier.fillMaxSize(), ) { AppBarWithTitle( - title = stringResource(R.string.title_withdraw), + // v2 names each step; v1 keeps the flow-wide "Withdraw" title on every screen. + title = stringResource( + if (isNewUi) R.string.title_addressEntry else R.string.title_withdraw + ), titleAlignment = Alignment.CenterHorizontally, onBackIconClicked = { flowNavigator.back() }, ) diff --git a/apps/flipcash/features/withdrawal/src/main/kotlin/com/flipcash/app/withdrawal/internal/screens/WithdrawalEntryScreen.kt b/apps/flipcash/features/withdrawal/src/main/kotlin/com/flipcash/app/withdrawal/internal/screens/WithdrawalEntryScreen.kt index 4b1c20fa36..7e003587ae 100644 --- a/apps/flipcash/features/withdrawal/src/main/kotlin/com/flipcash/app/withdrawal/internal/screens/WithdrawalEntryScreen.kt +++ b/apps/flipcash/features/withdrawal/src/main/kotlin/com/flipcash/app/withdrawal/internal/screens/WithdrawalEntryScreen.kt @@ -4,12 +4,16 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.flipcash.app.core.AppRoute import com.flipcash.app.core.withdrawal.WithdrawalResult import com.flipcash.app.core.withdrawal.WithdrawalStep +import com.flipcash.app.featureflags.FeatureFlag +import com.flipcash.app.featureflags.LocalFeatureFlags import com.flipcash.app.withdrawal.WithdrawalViewModel import com.flipcash.app.withdrawal.internal.entry.WithdrawalEntryScreen import com.flipcash.core.R @@ -29,12 +33,17 @@ internal fun WithdrawalEntryScreen( val codeNavigator = LocalCodeNavigator.current val flowNavigator = rememberFlowNavigator() val viewModel = flowSharedViewModel() + val isNewUi by LocalFeatureFlags.current.observe(FeatureFlag.NewUi) + .collectAsStateWithLifecycle() Column( modifier = Modifier.fillMaxSize(), ) { AppBarWithTitle( - title = stringResource(R.string.title_withdraw), + // v2 names each step; v1 keeps the flow-wide "Withdraw" title on every screen. + title = stringResource( + if (isNewUi) R.string.title_amountToWithdraw else R.string.title_withdraw + ), onBackIconClicked = { flowNavigator.back() }, titleAlignment = Alignment.CenterHorizontally, )