Skip to content

Commit fa9a873

Browse files
authored
Merge pull request #704 from code-payments/feat/consolidated-buy-amount-entry
feat(buy): consolidate amount entry into single screen
2 parents b1a14d7 + 68b99a0 commit fa9a873

43 files changed

Lines changed: 897 additions & 1035 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/build.gradle.kts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,6 @@ dependencies {
176176
implementation(project(":apps:flipcash:features:backupkey"))
177177
implementation(project(":apps:flipcash:features:shareapp"))
178178
implementation(project(":apps:flipcash:features:withdrawal"))
179-
implementation(project(":apps:flipcash:features:onramp"))
180179
implementation(project(":apps:flipcash:features:contact-verification"))
181180
implementation(project(":apps:flipcash:features:tokens"))
182181
implementation(project(":apps:flipcash:features:transactions"))

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ import com.flipcash.app.login.router.LoginRouter
4040
import com.flipcash.app.login.seed.SeedInputScreen
4141
import com.flipcash.app.menu.MenuScreen
4242
import com.flipcash.app.myaccount.MyAccountScreen
43-
import com.flipcash.app.onramp.OnRampCustomAmountScreen
4443
import com.flipcash.app.permissions.NotificationPermissionRationaleScreen
4544
import com.flipcash.app.permissions.NotificationPermissionScreen
4645
import com.flipcash.app.purchase.PurchaseAccountScreen
@@ -113,7 +112,6 @@ fun appEntryProvider(
113112
annotatedEntry<AppRoute.Token.TxProcessing> { key ->
114113
TokenTxProcessingScreen(key.swapId, key.swapPurpose, key.amount, key.awaitExternalWallet, key.isFundingShortfall)
115114
}
116-
annotatedEntry<AppRoute.Token.OnRamp> { key -> OnRampCustomAmountScreen(key.mint) }
117115
annotatedEntry<AppRoute.Token.Discovery> { TokenDiscoveryScreen() }
118116
annotatedEntry<AppRoute.Token.CurrencyCreator> { key ->
119117
CurrencyCreatorFlowScreen(route = key, resultStateRegistry = resultStateRegistry)

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,15 @@ sealed interface AppRoute : NavKey, Parcelable {
137137
val shortfall: Fiat? = null,
138138
) : Token, FlowRouteWithResult<SwapResult> {
139139
override val initialStack: List<NavKey>
140-
get() = listOf(SwapStep.Entry(purpose))
140+
get() = listOf(SwapStep.Entry(purpose, initialAmount = shortfall))
141141
}
142142

143+
@Serializable
144+
data object PhantomConnectInfo: Token
145+
146+
@Serializable
147+
data object PhantomConfirmTransaction: Token
148+
143149
@Serializable
144150
data class TxProcessing(
145151
val swapId: SwapId,
@@ -149,9 +155,6 @@ sealed interface AppRoute : NavKey, Parcelable {
149155
val isFundingShortfall: Boolean = false,
150156
) : Token, NonDismissableRoute, NonDraggableRoute
151157

152-
@Serializable
153-
data class OnRamp(val mint: Mint) : Token
154-
155158
@Serializable
156159
data object Discovery: AppRoute
157160

@@ -209,7 +212,7 @@ private fun buildVerificationInitialStack(
209212
emailVerificationCode: String?,
210213
): List<NavKey> {
211214
if (includePhone && includeEmail) {
212-
return listOf(VerificationStep.Intro(origin is AppRoute.Token.OnRamp))
215+
return listOf(VerificationStep.Intro(origin is AppRoute.Token.Swap))
213216
}
214217
if (includePhone) {
215218
return listOf(VerificationStep.PhoneEntry)

apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/android/extensions/Context.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ fun Context.launchPhotos(searchQuery: String = "Flipcash") {
2121
if (searchIntent.resolveActivity(packageManager) != null) {
2222
startActivity(searchIntent)
2323
} else {
24-
println("No apps handling image search")
2524
// Fall back to just opening the gallery
2625
val photosIntent = IntentUtils.photosApp()
2726
startActivity(photosIntent)

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,27 @@ import com.getcode.navigation.NonDismissableRoute
55
import com.getcode.navigation.NonDraggableRoute
66
import com.getcode.navigation.flow.FlowStep
77
import com.getcode.opencode.internal.solana.model.SwapId
8+
import com.getcode.opencode.model.financial.Fiat
89
import kotlinx.parcelize.Parcelize
910
import kotlinx.serialization.Serializable
1011

1112
@Serializable
1213
sealed interface SwapStep : FlowStep, Parcelable {
1314
@Parcelize
1415
@Serializable
15-
data class Entry(val purpose: SwapPurpose) : SwapStep
16+
data class Entry(val purpose: SwapPurpose, val initialAmount: Fiat? = null) : SwapStep
1617

1718
@Parcelize
1819
@Serializable
1920
data object SellReceipt : SwapStep
2021

22+
@Parcelize
23+
@Serializable
24+
data object PhantomConnect: SwapStep
25+
26+
@Parcelize
27+
@Serializable
28+
data object PhantomConfirmTransaction: SwapStep
2129
@Parcelize
2230
@Serializable
2331
data class Processing(

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,22 @@ import kotlinx.serialization.Serializable
1414
* @see Buy Represents a purchase intent (e.g., swapping base currency for a specific token).
1515
* @see Sell Represents a liquidation intent (e.g., swapping a specific token back to base currency).
1616
*/
17+
@Serializable
18+
enum class FundingSource {
19+
Flexible,
20+
Phantom,
21+
Coinbase,
22+
}
23+
1724
@Serializable
1825
@Parcelize
1926
sealed interface SwapPurpose : Parcelable {
27+
val mint: Mint
2028
sealed interface BalanceIncrease
2129
sealed interface BalanceDecrease
22-
@Serializable data class Buy(val mint: Mint) : SwapPurpose, BalanceIncrease
23-
@Serializable data class FundWithWallet(val mint: Mint): SwapPurpose, BalanceIncrease
24-
@Serializable data class Sell(val mint: Mint) : SwapPurpose, BalanceDecrease
30+
@Serializable data class Buy(
31+
override val mint: Mint,
32+
val fundingSource: FundingSource = FundingSource.Flexible,
33+
) : SwapPurpose, BalanceIncrease
34+
@Serializable data class Sell(override val mint: Mint) : SwapPurpose, BalanceDecrease
2535
}

apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/verification/email/EmailDeeplinkOrigin.kt

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
package com.flipcash.app.core.verification.email
22

33
import com.flipcash.app.core.AppRoute
4-
import com.getcode.ed25519.Ed25519
4+
import com.flipcash.app.core.tokens.SwapPurpose
55
import com.getcode.opencode.model.financial.Fiat
66
import com.getcode.opencode.utils.base64
77
import com.getcode.solana.keys.Mint
88
import com.getcode.solana.keys.base58
9-
import com.getcode.utils.base58
10-
import com.getcode.utils.decodeBase58
119
import com.getcode.utils.decodeBase64
1210
import kotlinx.serialization.Serializable
1311
import kotlinx.serialization.json.Json
@@ -24,7 +22,10 @@ sealed class EmailDeeplinkOrigin {
2422
is OnRamp -> {
2523
val amountString = amount?.let { Json.encodeToString(Fiat.Companion.serializer(), it) }
2624
when (source) {
27-
is AppRoute.Token.OnRamp -> "onramp|amountentry|${source.mint.base58()}"
25+
is AppRoute.Token.Swap -> {
26+
val mint = (source.purpose as? SwapPurpose.Buy)?.mint
27+
"onramp|amountentry|${mint?.base58()}"
28+
}
2829
else -> "onramp|null|$amountString"
2930
}
3031
}
@@ -36,12 +37,8 @@ sealed class EmailDeeplinkOrigin {
3637
companion object {
3738
fun fromRoute(route: AppRoute?): EmailDeeplinkOrigin? {
3839
return when (route) {
39-
is AppRoute.Token.OnRamp -> {
40-
OnRamp(route)
41-
}
42-
40+
is AppRoute.Token.Swap -> OnRamp(route)
4341
is AppRoute.Menu.MyAccount -> MyAccount
44-
4542
else -> null
4643
}
4744
}
@@ -53,18 +50,10 @@ sealed class EmailDeeplinkOrigin {
5350
val source = when (splits[1]) {
5451
"menu" -> AppRoute.Sheets.Menu
5552
"amountentry" -> {
56-
println("deeplink origin amountentry")
57-
val mint = splits.getOrNull(2)?.let {
58-
println("deeplink mint = $it")
59-
Mint(it)
60-
}
61-
62-
if (mint == null) {
63-
println("deeplink mint is null")
64-
return null
65-
}
53+
val mint = splits.getOrNull(2)?.let { Mint(it) }
54+
?: return null
6655

67-
AppRoute.Token.OnRamp(mint)
56+
AppRoute.Token.Swap(SwapPurpose.Buy(mint))
6857
}
6958
else -> null
7059
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<!--
2+
~ Copyright (C) 2026 The Android Open Source Project
3+
~
4+
~ Licensed under the Apache License, Version 2.0 (the "License");
5+
~ you may not use this file except in compliance with the License.
6+
~ You may obtain a copy of the License at
7+
~
8+
~ http://www.apache.org/licenses/LICENSE-2.0
9+
~
10+
~ Unless required by applicable law or agreed to in writing, software
11+
~ distributed under the License is distributed on an "AS IS" BASIS,
12+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
~ See the License for the specific language governing permissions and
14+
~ limitations under the License.
15+
-->
16+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
17+
android:width="278dp"
18+
android:height="124dp"
19+
android:viewportWidth="278"
20+
android:viewportHeight="124">
21+
<path
22+
android:pathData="M61,12L61,12A50,50 0,0 1,111 62L111,62A50,50 0,0 1,61 112L61,112A50,50 0,0 1,11 62L11,62A50,50 0,0 1,61 12z"
23+
android:fillColor="#AB9FF1"/>
24+
<path
25+
android:pathData="M37.77,87.76C45.63,87.76 51.53,80.93 55.06,75.53C54.63,76.72 54.39,77.92 54.39,79.07C54.39,82.22 56.2,84.47 59.77,84.47C64.67,84.47 69.91,80.17 72.63,75.53C72.44,76.2 72.34,76.82 72.34,77.39C72.34,79.59 73.58,80.98 76.1,80.98C84.05,80.98 92.05,66.88 92.05,54.55C92.05,44.95 87.2,36.49 75.01,36.49C53.58,36.49 30.49,62.68 30.49,79.59C30.49,86.23 34.06,87.76 37.77,87.76ZM67.63,53.5C67.63,51.11 68.96,49.44 70.91,49.44C72.82,49.44 74.15,51.11 74.15,53.5C74.15,55.89 72.82,57.61 70.91,57.61C68.96,57.61 67.63,55.89 67.63,53.5ZM77.82,53.5C77.82,51.11 79.15,49.44 81.1,49.44C83.01,49.44 84.34,51.11 84.34,53.5C84.34,55.89 83.01,57.61 81.1,57.61C79.15,57.61 77.82,55.89 77.82,53.5Z"
26+
android:fillColor="#ffffff"/>
27+
<path
28+
android:pathData="M139,54.42V62M139,62V69.58M139,62H131.42M139,62H146.58"
29+
android:strokeAlpha="0.33"
30+
android:strokeWidth="2.33333"
31+
android:fillColor="#00000000"
32+
android:strokeColor="#ffffff"
33+
android:strokeLineCap="square"/>
34+
<path
35+
android:pathData="M216.99,112C244.7,112 266.99,89.7 266.99,62C266.99,34.29 244.7,12 216.99,12C189.29,12 166.99,34.29 166.99,62C166.99,89.7 189.29,112 216.99,112Z"
36+
android:fillColor="#2775CA"/>
37+
<path
38+
android:pathData="M226.58,27.62C226.79,26.79 227.63,26.37 228.46,26.79C239.92,30.54 249.08,39.5 252.83,51.37C259.08,71.16 248.25,92.2 228.46,98.45C228.25,98.66 227.83,98.66 227.63,98.66C226.79,98.45 226.38,97.83 226.38,96.99V94.08C226.38,93.03 226.79,92.41 227.63,91.99C236.17,88.87 243.04,82.2 246.17,73.45C252.21,57.41 243.88,39.29 227.63,33.46C227,33.04 226.38,32.21 226.38,31.37V28.46C226.38,28.04 226.38,27.83 226.58,27.62ZM206.38,26.58C207.21,26.79 207.63,27.41 207.63,28.25V31.16C207.63,32.21 207.21,32.83 206.38,33.25C197.83,36.37 190.96,43.04 187.83,51.79C181.79,67.83 190.13,85.95 206.38,91.78C207,92.2 207.63,93.03 207.63,93.66V96.58C207.63,96.99 207.63,97.2 207.42,97.41C207.21,98.24 206.38,98.66 205.54,98.24C193.88,94.49 184.92,85.53 181.17,73.87C174.92,54.08 185.75,33.04 205.54,26.79C205.75,26.58 206.17,26.58 206.38,26.58ZM218.46,37.42C219.5,37.62 220.12,38.25 220.12,39.08V44.08C224.91,44.5 228.66,48.04 229.5,52.62V52.83C229.5,53.66 228.88,54.29 228.04,54.29H224.71C224.08,54.29 223.46,53.87 223.25,53.25C222.21,50.12 220.12,48.87 216.37,48.87C212.21,48.87 210.12,50.75 210.12,53.66C210.12,56.58 211.38,58.25 217.62,59.08C226.37,60.12 230.75,62.62 230.75,69.91C230.75,75.54 226.58,79.91 220.33,80.95V85.95C220.13,86.99 219.5,87.62 218.67,87.62H215.54C214.5,87.41 213.87,86.79 213.87,85.95V80.95C207,79.91 203.67,76.16 202.83,70.95V70.75C202.83,69.91 203.46,69.29 204.29,69.29H207.83C208.46,69.29 209.08,69.7 209.29,70.54C209.92,73.66 211.79,75.95 217.21,75.95C221.17,75.95 224.08,73.66 224.08,70.33C224.08,67 222.21,65.75 216.37,64.71C207.62,63.66 203.46,60.96 203.46,54.08C203.46,48.87 207.42,44.71 213.67,43.87V39.08C213.88,38.04 214.5,37.42 215.33,37.42H218.46Z"
39+
android:fillColor="#ffffff"
40+
android:fillType="evenOdd"/>
41+
<path
42+
android:pathData="M258.73,104.76m-16.98,0a16.98,16.98 0,1 1,33.96 0a16.98,16.98 0,1 1,-33.96 0"
43+
android:strokeWidth="3.95626"
44+
android:fillColor="#ffffff"
45+
android:strokeColor="#19191A"/>
46+
<path
47+
android:pathData="M267.23,108.22C267.49,108.22 267.62,108.53 267.44,108.71L264.67,111.48C264.56,111.59 264.41,111.65 264.26,111.65H250.23C250.18,111.65 250.12,111.63 250.07,111.6C250.02,111.57 249.99,111.52 249.97,111.47C249.95,111.42 249.94,111.36 249.95,111.3C249.96,111.25 249.99,111.2 250.03,111.16L252.8,108.39C252.91,108.28 253.05,108.22 253.21,108.22H267.23Z"
48+
android:fillColor="#19191A"/>
49+
<path
50+
android:pathData="M264.26,103.01C264.41,103.01 264.56,103.07 264.67,103.18L267.44,105.95C267.62,106.13 267.49,106.44 267.23,106.44H253.21C253.05,106.44 252.91,106.38 252.8,106.27L250.03,103.5C249.99,103.46 249.96,103.41 249.95,103.35C249.94,103.3 249.95,103.24 249.97,103.19C249.99,103.14 250.02,103.09 250.07,103.06C250.12,103.03 250.18,103.01 250.23,103.01H264.26Z"
51+
android:fillColor="#19191A"/>
52+
<path
53+
android:pathData="M267.23,97.87C267.49,97.87 267.62,98.18 267.44,98.36L264.67,101.13C264.56,101.24 264.41,101.3 264.26,101.3H250.23C250.18,101.3 250.12,101.28 250.07,101.25C250.02,101.22 249.99,101.18 249.97,101.12C249.95,101.07 249.94,101.01 249.95,100.96C249.96,100.9 249.99,100.85 250.03,100.81L252.8,98.04L252.8,98.04C252.91,97.93 253.06,97.87 253.21,97.87H267.23Z"
54+
android:fillColor="#19191A"/>
55+
</vector>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<!--
2+
~ Copyright (C) 2026 The Android Open Source Project
3+
~
4+
~ Licensed under the Apache License, Version 2.0 (the "License");
5+
~ you may not use this file except in compliance with the License.
6+
~ You may obtain a copy of the License at
7+
~
8+
~ http://www.apache.org/licenses/LICENSE-2.0
9+
~
10+
~ Unless required by applicable law or agreed to in writing, software
11+
~ distributed under the License is distributed on an "AS IS" BASIS,
12+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
~ See the License for the specific language governing permissions and
14+
~ limitations under the License.
15+
-->
16+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
17+
android:width="122dp"
18+
android:height="124dp"
19+
android:viewportWidth="122"
20+
android:viewportHeight="124">
21+
<path
22+
android:pathData="M61,112C88.71,112 111,89.71 111,62C111,34.29 88.71,12 61,12C33.29,12 11,34.29 11,62C11,89.71 33.29,112 61,112Z"
23+
android:fillColor="#AB9FF1"/>
24+
<path
25+
android:pathData="M37.77,87.76C45.63,87.76 51.53,80.93 55.06,75.53C54.63,76.72 54.39,77.92 54.39,79.07C54.39,82.22 56.2,84.47 59.77,84.47C64.67,84.47 69.91,80.17 72.63,75.53C72.44,76.2 72.34,76.82 72.34,77.39C72.34,79.59 73.58,80.98 76.1,80.98C84.05,80.98 92.05,66.88 92.05,54.55C92.05,44.95 87.2,36.49 75.01,36.49C53.58,36.49 30.49,62.68 30.49,79.59C30.49,86.23 34.06,87.76 37.77,87.76ZM67.63,53.5C67.63,51.11 68.96,49.44 70.91,49.44C72.82,49.44 74.15,51.11 74.15,53.5C74.15,55.89 72.82,57.61 70.91,57.61C68.96,57.61 67.63,55.89 67.63,53.5ZM77.82,53.5C77.82,51.11 79.15,49.44 81.1,49.44C83.01,49.44 84.34,51.11 84.34,53.5C84.34,55.89 83.01,57.61 81.1,57.61C79.15,57.61 77.82,55.89 77.82,53.5Z"
26+
android:fillColor="#ffffff"/>
27+
<path
28+
android:pathData="M102.73,104.76m-16.98,0a16.98,16.98 0,1 1,33.96 0a16.98,16.98 0,1 1,-33.96 0"
29+
android:strokeWidth="3.95626"
30+
android:fillColor="#ffffff"
31+
android:strokeColor="#19191A"/>
32+
<path
33+
android:pathData="M110.3,100.29L100.72,111.05L95.69,106.11L97.15,104.63L100.62,108.03L108.75,98.9L110.3,100.29Z"
34+
android:fillColor="#19191A"/>
35+
</vector>

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,12 +332,14 @@
332332
<string name="title_onrampProviderBackpack">Backpack Wallet</string>
333333
<string name="title_onrampProviderPhantom">Phantom Wallet</string>
334334
<string name="title_onrampProviderSolflare">Solflare Wallet</string>
335+
<string name="title_onrampProviderOtherWallet">Other Wallet</string>
335336

336337
<string name="action_addCashWithGooglePay">Add Cash with Google Pay</string>
337338
<string name="action_addCashWithDebitCard">Add Cash with Debit Card</string>
338339
<string name="action_addCashWithCreditCard">Add Cash with Credit Card</string>
339340
<string name="label_confirmIn">Confirm In</string>
340341
<string name="label_phantom">Phantom</string>
342+
341343
<string name="label_solflare">Solflare</string>
342344
<string name="label_backpack">Backpack</string>
343345

@@ -439,6 +441,7 @@
439441
<string name="title_amountToBuy">Amount to Buy</string>
440442
<string name="title_amountToSell">Amount to Sell</string>
441443
<string name="subtitle_buySellCashHint">Enter up to %1$s</string>
444+
<string name="subtitle_buyHintBelowMinimum">You must buy at least %1$s</string>
442445
<string name="subtitle_buyHintLimitExceeded">You can only buy up to %1$s</string>
443446
<string name="subtitle_sellHintLimitExceeded">You can only sell up to %1$s</string>
444447
<string name="action_buy">Buy</string>
@@ -518,6 +521,9 @@
518521
<string name="title_settingsSectionAccount">Account</string>
519522
<string name="title_userFlags">User Flags</string>
520523

524+
<string name="title_purchase">Purchase</string>
525+
<string name="action_connectYourPhantomWallet">Connect Your Phantom Wallet</string>
526+
521527
<string name="error_title_onrampAmountTooLow">$5 Minimum Purchase</string>
522528
<string name="error_description_onrampAmountTooLow">Please enter an amount of $5 or higher</string>
523529

@@ -644,4 +650,13 @@
644650
<string name="title_leaderboard">Leaderboard</string>
645651

646652
<string name="action_discover">Discover</string>
653+
654+
<string name="title_buyWithPhantom">Buy With Phantom</string>
655+
<string name="description_buyWithPhantom">Purchase using Solana USDC in Phantom. Simply connect your wallet and confirm the transaction</string>
656+
657+
<string name="title_confirmation">Confirmation</string>
658+
<string name="title_buyWithPhantomConnected">Connected</string>
659+
<string name="description_buyWithPhantomConnected">Confirm the transaction in Phantom to continue</string>
660+
661+
<string name="title_depositToken">Deposit %1$s</string>
647662
</resources>

0 commit comments

Comments
 (0)