Skip to content

Commit aaee930

Browse files
committed
feat(deposit): deposit-first user experience
Prioritize depositing funds as the primary entry point for new and empty-wallet users. Balance, menu, send, messenger, and cash screens now surface deposit options contextually, routing through the unified Swap flow. Coinbase on-ramp gains phone-region resolution for better availability detection. Phantom deposits swap USDC→USDF directly via CoinbaseStableSwapper into the VM deposit PDA, eliminating the server-side sweep. Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent 10f2c5f commit aaee930

50 files changed

Lines changed: 1126 additions & 216 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/flipcash/app/src/main/kotlin/com/flipcash/app/internal/ui/navigation/AppScreenContent.kt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import com.flipcash.app.shareapp.ShareAppScreen
4646
import com.flipcash.app.tokens.SwapFlowScreen
4747
import com.flipcash.app.tokens.TokenInfoScreen
4848
import com.flipcash.app.tokens.TokenSelectScreen
49-
import com.flipcash.app.tokens.TokenTxProcessingScreen
49+
5050
import com.flipcash.app.transactions.TransactionHistoryScreen
5151
import com.flipcash.app.userflags.UserFlagsScreen
5252
import com.flipcash.app.withdrawal.WithdrawalFlowScreen
@@ -110,10 +110,6 @@ fun appEntryProvider(
110110
annotatedEntry<AppRoute.Token.Swap> { key ->
111111
SwapFlowScreen(route = key, resultStateRegistry = resultStateRegistry)
112112
}
113-
// TODO: fold this into above entry
114-
annotatedEntry<AppRoute.Token.TxProcessing> { key ->
115-
TokenTxProcessingScreen(key.swapId, key.swapPurpose, key.amount, key.isFundingShortfall)
116-
}
117113
annotatedEntry<AppRoute.Token.Discovery> { TokenDiscoveryScreen() }
118114
annotatedEntry<AppRoute.Token.CurrencyCreator> { key ->
119115
CurrencyCreatorFlowScreen(route = key, resultStateRegistry = resultStateRegistry)

apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/AppRoute.kt

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,10 @@ import com.flipcash.app.core.verification.VerificationStep
1515
import com.flipcash.app.core.withdrawal.WithdrawalResult
1616
import com.flipcash.app.core.withdrawal.WithdrawalStep
1717
import com.flipcash.app.core.onboarding.OnboardingStep
18-
import com.getcode.navigation.NonDismissableRoute
19-
import com.getcode.navigation.NonDraggableRoute
2018
import com.getcode.navigation.flow.FlowRoute
2119
import com.getcode.navigation.flow.FlowRouteWithResult
22-
import com.getcode.opencode.exchange.VerifiedFiat
23-
import com.getcode.opencode.internal.solana.model.SwapId
2420
import com.getcode.opencode.model.financial.Fiat
2521
import com.getcode.solana.keys.Mint
26-
import com.getcode.solana.keys.PublicKey
2722
import com.getcode.ui.core.RestrictionType
2823
import kotlinx.parcelize.Parcelize
2924
import kotlinx.serialization.Serializable
@@ -189,13 +184,6 @@ sealed interface AppRoute : NavKey, Parcelable {
189184
@Serializable
190185
data object PhantomConfirmTransaction: Token
191186

192-
@Serializable
193-
data class TxProcessing(
194-
val swapId: SwapId,
195-
val swapPurpose: SwapPurpose? = null,
196-
val amount: VerifiedFiat? = null,
197-
val isFundingShortfall: Boolean = false,
198-
) : Token, NonDismissableRoute, NonDraggableRoute
199187

200188
@Serializable
201189
data object Discovery: AppRoute

apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/send/SendStep.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,8 @@ sealed interface SendResult : Parcelable {
2929
@Parcelize
3030
@Serializable
3131
data class Sent(val amount: com.getcode.opencode.model.financial.Fiat) : SendResult
32+
33+
@Parcelize
34+
@Serializable
35+
data class OpenRoute(val route: com.flipcash.app.core.AppRoute) : SendResult
3236
}

apps/flipcash/core/src/main/res/values/strings.xml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,11 @@
6666
<string name="subtitle_balanceIsHeldInUsdStablecoins">Your balance is held in US dollar stablecoins</string>
6767
<string name="subtitle_currentValueOfAllCurrencies">The current value of your currencies</string>
6868
<string name="subtitle_ofUsdStablecoins">of US dollar stablecoins</string>
69-
<string name="action_depositFunds">Deposit</string>
69+
<string name="action_deposit">Deposit</string>
7070
<string name="title_deposit">Deposit</string>
7171
<string name="title_depositFunds">Deposit Funds</string>
72+
<string name="action_depositFunds">Deposit Funds</string>
73+
7274

7375
<string name="title_withdraw">Withdraw</string>
7476
<string name="title_withdrawFunds">Withdraw Funds</string>
@@ -235,7 +237,7 @@
235237
<string name="title_tapAboveToAddCashToWallet">Tap above to Add Cash to your wallet</string>
236238
<string name="title_tapBelowToAddCashWallet">You don\'t have any cash yet.\nTap below to add cash to your wallet</string>
237239
<string name="title_noBalanceYet">No Balance Yet</string>
238-
<string name="description_noBalanceYet">Buy a currency to get started, or get another Flipcash user to give you some cash</string>
240+
<string name="description_noBalanceYet">Deposit funds to get started</string>
239241
<string name="description_noBalanceYetDiscover">Buy your first currency to get started</string>
240242
<string name="action_dismiss">Dismiss</string>
241243
<string name="title_success">Success</string>

apps/flipcash/features/balance/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ dependencies {
1313

1414
implementation(project(":apps:flipcash:shared:analytics"))
1515
implementation(project(":apps:flipcash:shared:featureflags"))
16+
implementation(project(":apps:flipcash:shared:payments"))
1617
implementation(project(":apps:flipcash:shared:tokens"))
1718
implementation(project(":apps:flipcash:shared:userflags"))
1819
implementation(project(":libs:datetime"))

apps/flipcash/features/balance/src/main/kotlin/com/flipcash/app/balance/internal/BalanceScreenContent.kt

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
1010
import androidx.compose.foundation.layout.navigationBarsPadding
1111
import androidx.compose.foundation.layout.padding
1212
import androidx.compose.foundation.shape.CircleShape
13-
import androidx.compose.material.Text
13+
import androidx.compose.material3.Text
1414
import androidx.compose.runtime.Composable
1515
import androidx.compose.runtime.CompositionLocalProvider
1616
import androidx.compose.runtime.getValue
@@ -97,29 +97,23 @@ private fun BalanceScreenContent(
9797

9898
Text(
9999
modifier = Modifier.fillMaxWidth(0.6f),
100-
text = if (tokenState.discoveryEnabled) {
101-
stringResource(R.string.description_noBalanceYetDiscover)
102-
} else {
103-
stringResource(R.string.description_noBalanceYet)
104-
},
100+
text = stringResource(R.string.description_noBalanceYet),
105101
style = CodeTheme.typography.textSmall,
106102
color = CodeTheme.colors.textSecondary,
107103
textAlign = TextAlign.Center,
108104
)
109105

110-
if (tokenState.discoveryEnabled) {
111-
CodeButton(
112-
onClick = {
113-
dispatchEvent(
114-
BalanceViewModel.Event.OpenScreen(AppRoute.Token.Discovery)
115-
)
116-
},
117-
modifier = Modifier.align(Alignment.CenterHorizontally),
118-
contentPadding = PaddingValues(),
119-
text = stringResource(R.string.action_discoverCurrencies),
120-
shape = CircleShape,
121-
)
122-
}
106+
CodeButton(
107+
onClick = {
108+
dispatchEvent(BalanceViewModel.Event.PresentDepositOptions)
109+
},
110+
modifier = Modifier
111+
.padding(top = CodeTheme.dimens.grid.x2)
112+
.align(Alignment.CenterHorizontally),
113+
contentPadding = PaddingValues(),
114+
text = stringResource(R.string.action_depositFunds),
115+
shape = CircleShape,
116+
)
123117
}
124118
}
125119
},

apps/flipcash/features/balance/src/main/kotlin/com/flipcash/app/balance/internal/BalanceViewModel.kt

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.flipcash.app.balance.internal
22

33
import androidx.lifecycle.viewModelScope
44
import com.flipcash.app.core.AppRoute
5+
import com.flipcash.app.payments.PurchaseMethodController
56
import com.flipcash.app.userflags.UserFlagsCoordinator
67
import com.flipcash.services.internal.model.thirdparty.OnRampProvider
78
import com.flipcash.services.user.AuthState
@@ -23,6 +24,7 @@ internal class BalanceViewModel @Inject constructor(
2324
userManager: UserManager,
2425
userFlags: UserFlagsCoordinator,
2526
dispatchers: DispatcherProvider,
27+
purchaseMethodController: PurchaseMethodController,
2628
) : BaseViewModel<BalanceViewModel.State, BalanceViewModel.Event>(
2729
initialState = State(),
2830
updateStateForEvent = updateStateForEvent,
@@ -38,6 +40,7 @@ internal class BalanceViewModel @Inject constructor(
3840
data object OpenCurrencySelection : Event
3941

4042
data class OpenScreen(val screen: AppRoute) : Event
43+
data object PresentDepositOptions: Event
4144
}
4245

4346
init {
@@ -46,9 +49,13 @@ internal class BalanceViewModel @Inject constructor(
4649
.flatMapLatest { userFlags.resolvedFlags }
4750
.mapNotNull { it.preferredOnRampProvider.effectiveValue }
4851
.filterIsInstance<OnRampProvider.Defined>()
49-
.onEach { provider ->
50-
dispatchEvent(Event.OnPreferredOnRampProviderChanged(provider))
51-
}
52+
.onEach { provider -> dispatchEvent(Event.OnPreferredOnRampProviderChanged(provider)) }
53+
.launchIn(viewModelScope)
54+
55+
eventFlow
56+
.filterIsInstance<Event.PresentDepositOptions>()
57+
.mapNotNull { purchaseMethodController.presentDepositOptions() }
58+
.onEach { route -> dispatchEvent(Event.OpenScreen(route)) }
5259
.launchIn(viewModelScope)
5360
}
5461

@@ -59,6 +66,7 @@ internal class BalanceViewModel @Inject constructor(
5966
is Event.OnPreferredOnRampProviderChanged -> { state ->
6067
state.copy(preferredOnRampProvider = event.provider)
6168
}
69+
Event.PresentDepositOptions -> { state -> state }
6270
is Event.OpenScreen -> { state -> state }
6371
}
6472
}

apps/flipcash/features/cash/src/main/kotlin/com/flipcash/app/cash/internal/CashScreenViewModel.kt

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ internal class CashScreenViewModel @Inject constructor(
100100
data class OnCurrencyChanged(val model: com.getcode.opencode.model.financial.Currency) : Event
101101
data class OnLimitsChanged(val limits: Limits?) : Event
102102
data object OnGive : Event
103-
data class PresentBill(val bill: Bill.Cash) : Event
103+
data class PresentBill(val bill: Bill) : Event
104104

105105
data class AddCashToWallet(val amount: Fiat) : Event
106106
data class UpdateLoadingState(val loading: Boolean = false, val success: Boolean = false) :
@@ -255,11 +255,20 @@ internal class CashScreenViewModel @Inject constructor(
255255
return@onEach
256256
}
257257

258-
val bill = Bill.Cash(
259-
token = stateFlow.value.token!!.token,
260-
amount = result.localFiat,
261-
verifiedState = result.verifiedState,
262-
)
258+
val selectedToken = stateFlow.value.token!!.token
259+
val bill = if (selectedToken.address == Mint.usdf) {
260+
Bill.GoldBar(
261+
token = selectedToken,
262+
amount = result.localFiat,
263+
verifiedState = result.verifiedState,
264+
)
265+
} else {
266+
Bill.Cash(
267+
token = selectedToken,
268+
amount = result.localFiat,
269+
verifiedState = result.verifiedState,
270+
)
271+
}
263272

264273
dispatchEvent(Event.UpdateLoadingState(loading = false, success = true))
265274
dispatchEvent(Event.PresentBill(bill))

apps/flipcash/features/currency-creator/src/main/kotlin/com/flipcash/app/currencycreator/internal/CurrencyCreatorViewModel.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import com.flipcash.app.currencycreator.CurrencyCreatorCoordinator
1414
import com.flipcash.app.currencycreator.internal.components.CurrencyCreatorTopBarController
1515
import com.flipcash.app.onramp.DeeplinkError
1616
import com.flipcash.app.onramp.DeeplinkOnRampError
17+
import com.flipcash.app.onramp.PhantomSwapResult
1718
import com.flipcash.app.onramp.PhantomWalletController
1819
import com.flipcash.app.onramp.isAlert
1920
import com.flipcash.app.onramp.isNetworkCause
@@ -542,7 +543,8 @@ internal class CurrencyCreatorViewModel @Inject constructor(
542543
amount = totalAmount,
543544
fee = feeAmount,
544545
token = token,
545-
).onSuccess { swapId ->
546+
).onSuccess { result ->
547+
val swapId = (result as PhantomSwapResult.WithSwapId).swapId
546548
dispatchEvent(Event.PurchaseSubmitted(swapId, token.address))
547549
}.onFailure { error ->
548550
handlePhantomError(error)

apps/flipcash/features/direct-send/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ dependencies {
1919
implementation(project(":libs:messaging"))
2020
implementation(project(":libs:permissions:bindings"))
2121
implementation(project(":apps:flipcash:shared:featureflags"))
22+
implementation(project(":apps:flipcash:shared:payments"))
2223
implementation(project(":apps:flipcash:shared:permissions"))
2324
implementation(project(":apps:flipcash:shared:contacts"))
2425
implementation(project(":apps:flipcash:shared:tokens"))

0 commit comments

Comments
 (0)