Skip to content

Commit cc76237

Browse files
committed
feat: updated deposit and withdrawal flows with currency selection and USDC info screens
Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent fa43e3e commit cc76237

23 files changed

Lines changed: 320 additions & 140 deletions

File tree

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

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -167,23 +167,15 @@ sealed interface AppRoute : NavKey, Parcelable {
167167
@Parcelize
168168
sealed interface Transfers : AppRoute {
169169
@Serializable
170-
data class Deposit(val mint: Mint): Transfers, FlowRouteWithResult<DepositResult> {
170+
data class Deposit(val showOtherOptions: Boolean = true): Transfers, FlowRouteWithResult<DepositResult> {
171171
override val initialStack: List<NavKey>
172-
get() = if (mint == Mint.usdf) {
173-
listOf(DepositStep.UsdcInformational)
174-
} else {
175-
listOf(DepositStep.Destination(mint))
176-
}
172+
get() = listOf(DepositStep.UsdcInformational(showOtherOptions))
177173
}
178174

179175
@Serializable
180-
data class Withdrawal(val mint: Mint) : Transfers, FlowRouteWithResult<WithdrawalResult> {
176+
data class Withdrawal(val showOtherOptions: Boolean = true) : Transfers, FlowRouteWithResult<WithdrawalResult> {
181177
override val initialStack: List<NavKey>
182-
get() = if (mint == Mint.usdf) {
183-
listOf(WithdrawalStep.UsdcInformational)
184-
} else {
185-
listOf(WithdrawalStep.Amount(mint))
186-
}
178+
get() = listOf(WithdrawalStep.UsdcInformational(showOtherOptions))
187179
}
188180
}
189181

apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/deposit/DepositStep.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ import kotlinx.serialization.Serializable
1313
@Serializable
1414
sealed interface DepositStep : FlowStep, Parcelable {
1515
@Parcelize
16-
object UsdcInformational : DepositStep
16+
data class UsdcInformational(val showOtherOptions: Boolean) : DepositStep
1717

1818

19+
@Parcelize
20+
@Serializable
21+
data object SelectToken: DepositStep
1922
@Parcelize
2023
@Serializable
2124
data class Destination(val mint: Mint) : DepositStep

apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/tokens/SwapResult.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,8 @@ sealed interface SwapResult : Parcelable {
1313
@Parcelize
1414
@Serializable
1515
data object Canceled : SwapResult
16+
17+
@Parcelize
18+
@Serializable
19+
data object OpenDeposit : SwapResult
1620
}

apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/withdrawal/WithdrawalStep.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ import kotlinx.serialization.Serializable
1414
@Serializable
1515
sealed interface WithdrawalStep : FlowStep, Parcelable {
1616
@Parcelize
17-
object UsdcInformational: WithdrawalStep
17+
data class UsdcInformational(val showOtherOptions: Boolean): WithdrawalStep
18+
19+
@Parcelize
20+
@Serializable
21+
data object SelectToken: WithdrawalStep
1822
@Parcelize
1923
@Serializable
2024
data class Amount(val mint: Mint) : WithdrawalStep

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,4 +669,7 @@
669669
<string name="description_buyWithPhantomConnected">Confirm the transaction in Phantom to continue</string>
670670

671671
<string name="title_depositToken">Deposit %1$s</string>
672+
673+
<string name="action_depositOtherCurrencies">Deposit Other Flipcash Currencies</string>
674+
<string name="action_withdrawOtherCurrencies">Withdraw Other Flipcash Currencies</string>
672675
</resources>

apps/flipcash/features/advanced/src/main/kotlin/com/flipcash/app/advanced/internal/AdvancedFeatureMenuItems.kt

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,6 @@ import com.flipcash.app.core.tokens.TokenPurpose
1414
import com.flipcash.app.menu.FullMenuItem
1515
import com.flipcash.features.advanced.R
1616

17-
internal data object CurrencyCreator : FullMenuItem<AdvancedFeaturesScreenViewModel.Event>() {
18-
override val icon: Painter
19-
@Composable get() = rememberVectorPainter(Icons.Default.AddCircle)
20-
override val name: String
21-
@Composable get() = stringResource(R.string.title_createYourCurrency)
22-
override val action: AdvancedFeaturesScreenViewModel.Event = AdvancedFeaturesScreenViewModel.Event.OpenScreen(
23-
AppRoute.Token.CurrencyCreator
24-
)
25-
}
26-
2717
internal data object BillCustomizer : FullMenuItem<AdvancedFeaturesScreenViewModel.Event>() {
2818
override val icon: Painter
2919
@Composable get() = rememberVectorPainter(Icons.Outlined.Palette)
@@ -32,16 +22,6 @@ internal data object BillCustomizer : FullMenuItem<AdvancedFeaturesScreenViewMod
3222
override val action: AdvancedFeaturesScreenViewModel.Event = AdvancedFeaturesScreenViewModel.Event.OpenBillPlayground
3323
}
3424

35-
internal data object Deposit : FullMenuItem<AdvancedFeaturesScreenViewModel.Event>() {
36-
override val icon: Painter
37-
@Composable get() = painterResource(R.drawable.ic_menu_deposit)
38-
override val name: String
39-
@Composable get() = stringResource(R.string.title_depositFunds)
40-
override val action: AdvancedFeaturesScreenViewModel.Event = AdvancedFeaturesScreenViewModel.Event.OpenScreen(
41-
AppRoute.Sheets.TokenSelection(purpose = TokenPurpose.Deposit)
42-
)
43-
}
44-
4525
internal data object DeviceLogs : FullMenuItem<AdvancedFeaturesScreenViewModel.Event>() {
4626
override val icon: Painter
4727
@Composable get() = rememberVectorPainter(Icons.Outlined.Description)

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ dependencies {
1010
implementation(project(":libs:messaging"))
1111

1212
implementation(project(":apps:flipcash:shared:featureflags"))
13+
implementation(project(":apps:flipcash:shared:tokens"))
1314
implementation(project(":services:flipcash"))
1415

1516
}
Lines changed: 98 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,141 @@
11
package com.flipcash.app.deposit
22

3+
import androidx.compose.foundation.layout.Column
4+
import androidx.compose.foundation.layout.fillMaxSize
35
import androidx.compose.runtime.Composable
46
import androidx.compose.runtime.CompositionLocalProvider
7+
import androidx.compose.runtime.LaunchedEffect
58
import androidx.compose.runtime.getValue
6-
import androidx.compose.runtime.key
7-
import androidx.compose.runtime.remember
9+
import androidx.compose.ui.Alignment
10+
import androidx.compose.ui.Modifier
11+
import androidx.compose.ui.res.stringResource
812
import androidx.compose.ui.tooling.preview.Preview
913
import androidx.compose.ui.tooling.preview.PreviewWrapper
14+
import androidx.hilt.navigation.compose.hiltViewModel
1015
import androidx.lifecycle.compose.collectAsStateWithLifecycle
1116
import androidx.navigation3.runtime.NavEntry
1217
import androidx.navigation3.runtime.NavKey
1318
import androidx.navigation3.runtime.entryProvider
1419
import com.flipcash.app.core.AppRoute
1520
import com.flipcash.app.core.deposit.DepositResult
1621
import com.flipcash.app.core.deposit.DepositStep
17-
import com.flipcash.app.featureflags.FeatureFlag
18-
import com.flipcash.app.featureflags.LocalFeatureFlags
22+
import com.flipcash.app.core.tokens.TokenPurpose
1923
import com.flipcash.app.deposit.internal.DepositViewModel
2024
import com.flipcash.app.deposit.internal.UsdcDepositInformationScreen
2125
import com.flipcash.app.theme.FlipcashThemeWrapper
26+
import com.flipcash.app.tokens.ui.SelectTokenViewModel
27+
import com.flipcash.app.tokens.ui.TokenList
28+
import com.flipcash.core.R
2229
import com.getcode.navigation.annotatedEntry
2330
import com.getcode.navigation.core.LocalCodeNavigator
2431
import com.getcode.navigation.flow.FlowExitReason
2532
import com.getcode.navigation.flow.FlowHost
2633
import com.getcode.navigation.flow.LocalFlowNavigator
2734
import com.getcode.navigation.flow.PreviewFlowNavigator
2835
import com.getcode.navigation.flow.deliverFlowResult
36+
import com.getcode.navigation.flow.rememberFlowNavigator
37+
import com.getcode.navigation.flow.rememberInitialStack
38+
import com.getcode.navigation.flowAnnotatedEntry
2939
import com.getcode.navigation.results.NavResultOrCanceled
3040
import com.getcode.navigation.results.NavResultStateRegistry
31-
import com.getcode.solana.keys.Mint
41+
import com.getcode.ui.components.AppBarWithTitle
42+
import kotlinx.coroutines.flow.filter
43+
import kotlinx.coroutines.flow.filterIsInstance
44+
import kotlinx.coroutines.flow.launchIn
45+
import kotlinx.coroutines.flow.map
46+
import kotlinx.coroutines.flow.onEach
3247

3348
@Composable
3449
fun DepositFlowScreen(
3550
route: AppRoute.Transfers.Deposit,
3651
resultStateRegistry: NavResultStateRegistry,
3752
) {
3853
val outerNavigator = LocalCodeNavigator.current
39-
val featureFlags = LocalFeatureFlags.current
54+
val initialStack = route.rememberInitialStack<DepositStep>()
4055

41-
val directDeposit by featureFlags
42-
.observe(FeatureFlag.DepositUsdc)
43-
.collectAsStateWithLifecycle()
44-
45-
val initialStack = remember(route, directDeposit) {
46-
@Suppress("UNCHECKED_CAST")
47-
val steps = route.initialStack as List<DepositStep>
48-
println("direct deposit = $directDeposit, isUsdf=${route.mint == Mint.usdf}")
49-
if (!directDeposit && route.mint == Mint.usdf) {
50-
listOf(DepositStep.Destination(route.mint))
51-
} else {
52-
steps
53-
}
54-
}
55-
56-
key(directDeposit) {
57-
FlowHost(
58-
initialStack = initialStack,
59-
resultStateRegistry = resultStateRegistry,
60-
onExit = { reason, isSheetRoot ->
61-
val result: DepositResult = when (reason) {
62-
is FlowExitReason.Completed -> reason.result
63-
FlowExitReason.Canceled,
64-
FlowExitReason.BackedOutOfRoot -> DepositResult.Canceled
65-
}
66-
if (isSheetRoot) {
67-
outerNavigator.pop()
68-
} else {
69-
outerNavigator.deliverFlowResult(
70-
route = route,
71-
value = NavResultOrCanceled.ReturnValue(result),
72-
)
73-
when (result) {
74-
DepositResult.Success -> {
75-
outerNavigator.popUntil { it == AppRoute.Sheets.Menu }
76-
}
77-
DepositResult.Canceled -> {
78-
outerNavigator.pop()
79-
}
56+
FlowHost(
57+
initialStack = initialStack,
58+
resultStateRegistry = resultStateRegistry,
59+
onExit = { reason, isSheetRoot ->
60+
val result: DepositResult = when (reason) {
61+
is FlowExitReason.Completed -> reason.result
62+
FlowExitReason.Canceled,
63+
FlowExitReason.BackedOutOfRoot -> DepositResult.Canceled
64+
}
65+
if (isSheetRoot) {
66+
outerNavigator.pop()
67+
} else {
68+
outerNavigator.deliverFlowResult(
69+
route = route,
70+
value = NavResultOrCanceled.ReturnValue(result),
71+
)
72+
when (result) {
73+
DepositResult.Success -> {
74+
outerNavigator.popUntil { it == AppRoute.Sheets.Menu }
75+
}
76+
DepositResult.Canceled -> {
77+
outerNavigator.pop()
8078
}
8179
}
82-
},
83-
entryProvider = depositEntryProvider(route.mint),
84-
)
85-
}
80+
}
81+
},
82+
entryProvider = depositEntryProvider(route.showOtherOptions),
83+
)
8684
}
8785

8886
private fun depositEntryProvider(
89-
mint: Mint,
87+
showOtherOptions: Boolean,
9088
): (NavKey) -> NavEntry<NavKey> = entryProvider {
91-
annotatedEntry<DepositStep.UsdcInformational> {
92-
UsdcDepositInformationScreen()
89+
flowAnnotatedEntry<DepositStep.UsdcInformational> {
90+
UsdcDepositInformationScreen(showOtherOptions)
91+
}
92+
annotatedEntry<DepositStep.SelectToken> {
93+
DepositSelectTokenScreen()
94+
}
95+
annotatedEntry<DepositStep.Destination> { key ->
96+
DepositDestinationScreen(key.mint)
97+
}
98+
}
99+
100+
@Composable
101+
private fun DepositSelectTokenScreen() {
102+
val flowNavigator = rememberFlowNavigator<DepositStep, DepositResult>()
103+
val viewModel = hiltViewModel<SelectTokenViewModel>()
104+
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
105+
106+
Column(
107+
modifier = Modifier.fillMaxSize(),
108+
horizontalAlignment = Alignment.CenterHorizontally,
109+
) {
110+
AppBarWithTitle(
111+
isInModal = true,
112+
title = stringResource(R.string.title_selectCurrency),
113+
backButton = true,
114+
onBackIconClicked = { flowNavigator.back() },
115+
titleAlignment = Alignment.CenterHorizontally,
116+
)
117+
TokenList(
118+
modifier = Modifier.fillMaxSize(),
119+
tokens = state.tokens,
120+
selectedToken = state.selectedToken,
121+
showFlags = true,
122+
includeReserves = true,
123+
onTokenSelected = { viewModel.dispatchEvent(SelectTokenViewModel.Event.OnTokenSelected(it.address)) },
124+
)
93125
}
94-
annotatedEntry<DepositStep.Destination> {
95-
DepositDestinationScreen(mint)
126+
127+
LaunchedEffect(viewModel) {
128+
viewModel.dispatchEvent(SelectTokenViewModel.Event.OnPurposeChanged(TokenPurpose.Deposit))
129+
}
130+
131+
LaunchedEffect(viewModel) {
132+
viewModel.eventFlow
133+
.filterIsInstance<SelectTokenViewModel.Event.OnTokenSelected>()
134+
.filter { it.fromUser }
135+
.map { it.mint }
136+
.onEach { mint ->
137+
flowNavigator.navigateTo(DepositStep.Destination(mint))
138+
}.launchIn(this)
96139
}
97140
}
98141

@@ -112,5 +155,5 @@ private fun DepositFlowPreview(
112155
@PreviewWrapper(FlipcashThemeWrapper::class)
113156
@Composable
114157
private fun Preview_UsdcInformational() {
115-
DepositFlowPreview { UsdcDepositInformationScreen() }
158+
DepositFlowPreview { UsdcDepositInformationScreen(true) }
116159
}

apps/flipcash/features/deposit/src/main/kotlin/com/flipcash/app/deposit/internal/DepositViewModel.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ internal class DepositViewModel @Inject constructor(
6161
.onResult(
6262
onSuccess = { result ->
6363
viewModelScope.launch {
64-
val directDeposit = featureFlags.get(FeatureFlag.DepositUsdc)
65-
val address = if (result.token.address == Mint.usdf && directDeposit) {
64+
val address = if (result.token.address == Mint.usdc) {
6665
userManager.accountCluster?.authorityPublicKey?.base58()
6766
} else {
6867
userManager.accountCluster?.depositAddressFor(result.token)?.base58()
@@ -77,11 +76,11 @@ internal class DepositViewModel @Inject constructor(
7776
}
7877
return@launch
7978
}
80-
val tokenName = when {
81-
directDeposit && result.token.address == Mint.usdf -> {
79+
val tokenName = when (result.token.address) {
80+
Mint.usdc -> {
8281
resources.getString(R.string.displayName_usdc)
8382
}
84-
result.token.address == Mint.usdf -> {
83+
Mint.usdf -> {
8584
resources.getString(R.string.displayName_usdf)
8685
}
8786
else -> result.token.name

apps/flipcash/features/deposit/src/main/kotlin/com/flipcash/app/deposit/internal/UsdcDepositInformationScreen.kt

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,8 @@ import com.getcode.ui.theme.CodeButton
2727
import com.getcode.ui.theme.CodeScaffold
2828

2929
@Composable
30-
internal fun UsdcDepositInformationScreen() {
30+
internal fun UsdcDepositInformationScreen(showOtherOptions: Boolean) {
3131
val flowNavigator = rememberFlowNavigator<DepositStep, DepositResult>()
32-
3332
CodeScaffold(
3433
topBar = {
3534
AppBarWithTitle(
@@ -41,17 +40,33 @@ internal fun UsdcDepositInformationScreen() {
4140
)
4241
},
4342
bottomBar = {
44-
CodeButton(
45-
modifier = Modifier
46-
.fillMaxWidth()
43+
Column(
44+
modifier = Modifier.fillMaxWidth()
4745
.padding(horizontal = CodeTheme.dimens.inset)
4846
.padding(bottom = CodeTheme.dimens.grid.x2)
4947
.navigationBarsPadding(),
50-
buttonState = ButtonState.Filled,
51-
text = stringResource(R.string.action_next),
5248
) {
53-
flowNavigator.navigateTo(DepositStep.Destination(Mint.usdf))
49+
CodeButton(
50+
modifier = Modifier
51+
.fillMaxWidth(),
52+
buttonState = ButtonState.Filled,
53+
text = stringResource(R.string.action_next),
54+
) {
55+
flowNavigator.navigateTo(DepositStep.Destination(Mint.usdc))
56+
}
57+
58+
if (showOtherOptions) {
59+
CodeButton(
60+
modifier = Modifier
61+
.fillMaxWidth(),
62+
buttonState = ButtonState.Subtle,
63+
text = stringResource(R.string.action_depositOtherCurrencies),
64+
) {
65+
flowNavigator.navigateTo(DepositStep.SelectToken)
66+
}
67+
}
5468
}
69+
5570
}
5671
) { padding ->
5772
Box(

0 commit comments

Comments
 (0)