Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import com.flipcash.app.core.withdrawal.WithdrawalResult
import com.flipcash.app.core.withdrawal.WithdrawalStep
import com.flipcash.app.core.chat.ChatStep
import com.flipcash.app.core.onboarding.OnboardingStep
import com.flipcash.app.core.tokens.FundingSource
import com.getcode.navigation.flow.FlowRoute
import com.getcode.navigation.flow.FlowRouteWithResult
import com.getcode.opencode.model.financial.Fiat
Expand Down Expand Up @@ -181,7 +182,20 @@ sealed interface AppRoute : NavKey, Parcelable {
val popToRoot: Boolean = false,
) : Token, FlowRouteWithResult<SwapResult> {
override val initialStack: List<NavKey>
get() = listOf(SwapStep.Entry(purpose, initialAmount = shortfall))
get() = when (purpose) {
is SwapPurpose.Buy -> {
if (purpose.fundingSource == FundingSource.Phantom) {
// adding money (deposit) via phantom
listOf(SwapStep.PhantomConnect)
} else {
listOf(SwapStep.Entry(purpose, initialAmount = shortfall))
}
}
is SwapPurpose.Sell -> listOf(SwapStep.Entry(purpose, initialAmount = shortfall))
}



}

@Serializable
Expand Down
27 changes: 27 additions & 0 deletions apps/flipcash/core/src/main/res/drawable/ic_google_pay.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="62dp"
android:height="24dp"
android:viewportWidth="62"
android:viewportHeight="24">
<path
android:pathData="M28.743,11.673V18.749H26.482V1.275H32.476C33.994,1.275 35.29,1.778 36.35,2.784C37.433,3.79 37.974,5.018 37.974,6.468C37.974,7.953 37.433,9.181 36.35,10.175C35.302,11.17 34.006,11.661 32.476,11.661H28.743V11.673ZM28.743,3.427V9.521H32.523C33.418,9.521 34.171,9.216 34.76,8.62C35.361,8.023 35.667,7.298 35.667,6.48C35.667,5.673 35.361,4.959 34.76,4.363C34.171,3.743 33.429,3.439 32.523,3.439H28.743V3.427Z"
android:fillColor="#5F6368"/>
<path
android:pathData="M43.886,6.398C45.558,6.398 46.876,6.842 47.842,7.731C48.808,8.62 49.29,9.836 49.29,11.38V18.749H47.136V17.088H47.041C46.111,18.456 44.863,19.135 43.309,19.135C41.978,19.135 40.871,18.749 39.976,17.965C39.081,17.181 38.634,16.211 38.634,15.041C38.634,13.801 39.105,12.819 40.047,12.094C40.989,11.357 42.249,10.994 43.815,10.994C45.157,10.994 46.264,11.24 47.124,11.731V11.216C47.124,10.433 46.818,9.778 46.194,9.228C45.569,8.678 44.839,8.409 44.003,8.409C42.743,8.409 41.743,8.936 41.013,10L39.022,8.76C40.118,7.181 41.743,6.398 43.886,6.398ZM40.965,15.076C40.965,15.661 41.213,16.152 41.719,16.538C42.214,16.924 42.802,17.123 43.473,17.123C44.427,17.123 45.275,16.772 46.017,16.07C46.759,15.368 47.136,14.55 47.136,13.602C46.429,13.053 45.452,12.772 44.192,12.772C43.273,12.772 42.508,12.994 41.896,13.427C41.272,13.883 40.965,14.433 40.965,15.076Z"
android:fillColor="#5F6368"/>
<path
android:pathData="M61.584,6.784L54.048,24H51.716L54.519,17.977L49.549,6.784H52.011L55.59,15.368H55.637L59.123,6.784H61.584Z"
android:fillColor="#5F6368"/>
<path
android:pathData="M19.75,10.246C19.75,9.513 19.684,8.813 19.562,8.139H10.085V11.999L15.543,12C15.322,13.284 14.609,14.379 13.518,15.109V17.613H16.767C18.663,15.869 19.75,13.291 19.75,10.246Z"
android:fillColor="#4285F4"/>
<path
android:pathData="M13.519,15.109C12.615,15.715 11.45,16.069 10.088,16.069C7.456,16.069 5.223,14.308 4.424,11.933H1.073V14.516C2.733,17.788 6.145,20.034 10.088,20.034C12.813,20.034 15.102,19.144 16.768,17.612L13.519,15.109Z"
android:fillColor="#34A853"/>
<path
android:pathData="M4.108,10.018C4.108,9.351 4.22,8.706 4.424,8.101V5.518H1.073C0.386,6.871 0,8.399 0,10.018C0,11.636 0.387,13.164 1.073,14.517L4.424,11.935C4.22,11.329 4.108,10.684 4.108,10.018Z"
android:fillColor="#FABB05"/>
<path
android:pathData="M10.088,3.965C11.575,3.965 12.907,4.474 13.958,5.468L16.837,2.611C15.089,0.993 12.809,0 10.088,0C6.147,0 2.733,2.246 1.073,5.518L4.424,8.101C5.223,5.726 7.456,3.965 10.088,3.965Z"
android:fillColor="#E94235"/>
</vector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="M160,320h640v-80L160,240v80ZM80,240q0,-33 23.5,-56.5T160,160h640q33,0 56.5,23.5T880,240v240L160,480v240h164v80L160,800q-33,0 -56.5,-23.5T80,720v-480ZM598,880 L428,710l56,-56 114,112 226,-226 56,58L598,880ZM160,240v480,-180 113,-413Z"
android:fillColor="#e8eaed"/>
</vector>
28 changes: 22 additions & 6 deletions apps/flipcash/core/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,8 @@
<string name="action_deposit">Deposit</string>
<string name="title_deposit">Deposit</string>
<string name="title_depositFunds">Deposit Funds</string>
<string name="action_depositFunds">Deposit Funds</string>


<string name="action_addMoney">Add Money</string>
<string name="action_addMoreMoney">Add More Money</string>
<string name="title_withdraw">Withdraw</string>
<string name="title_withdrawFunds">Withdraw Funds</string>

Expand Down Expand Up @@ -187,6 +186,7 @@
<string name="error_title_invalidAddress">Invalid address</string>

<string name="action_withdraw">Withdraw</string>
<string name="action_withdrawMoney">Withdraw Money</string>
<string name="action_confirmWithdraw">Yes, Withdraw</string>
<string name="prompt_title_confirmWithdrawal">Are You Sure?</string>

Expand Down Expand Up @@ -241,6 +241,7 @@

<string name="action_addCash">Add Cash</string>
<string name="title_selectMethod">Select Method</string>
<string name="title_amountToAdd">Amount to Add</string>
<string name="title_amountToDeposit">Amount to Deposit</string>
<string name="title_addCashWithDebitCard">Add Cash with Debit Card</string>

Expand All @@ -254,7 +255,14 @@
<string name="title_tapAboveToAddCashToWallet">Tap above to Add Cash to your wallet</string>
<string name="title_tapBelowToAddCashWallet">You don\'t have any cash yet.\nTap below to add cash to your wallet</string>
<string name="title_noBalanceYet">No Balance Yet</string>
<string name="description_noBalanceYet">Deposit funds to get started</string>
<string name="title_insufficientBalance">Insufficient Balance</string>
<string name="description_insufficientBalanceToCreate">Add more money to create a currency</string>
<string name="description_insufficientBalanceToUse">Add more money, or enter a smaller amount</string>
<string name="description_noBalanceYetToGive">Add money to give cash</string>
<string name="description_noBalanceYetToBuy">Add money to buy currencies</string>
<string name="description_noBalanceYetToSend">Add money to send cash</string>
<string name="description_noBalanceYetForBalance">Add money to get started</string>
<string name="description_noBalanceYetToCreate">Add money to create a currency</string>
<string name="description_noBalanceYetDiscover">Buy your first currency to get started</string>
<string name="action_dismiss">Dismiss</string>
<string name="title_success">Success</string>
Expand Down Expand Up @@ -352,6 +360,8 @@
<string name="action_addPhoneNumber">Add Phone Number</string>
<string name="action_addEmailAddress">Add Email Address</string>
<string name="subtitle_linkedForPayments">Linked for payments</string>
<string name="label_verified">Verified</string>
<string name="label_unverified">Unverified</string>
<string name="prompt_title_unlinkPhone">Unlink Phone Number?</string>
<string name="prompt_description_unlinkPhone">Your phone number will be removed from your profile.</string>
<string name="prompt_title_unlinkEmail">Unlink Email Address?</string>
Expand Down Expand Up @@ -386,7 +396,7 @@
<string name="title_onrampProviderCoinbaseVirtual">Debit Card with Google Pay</string>
<string name="title_onrampProviderCoinbaseDebit">Debit Card</string>
<string name="title_onrampProviderCoinbaseCredit">Credit Card</string>
<string name="title_onrampProviderManualDeposit">Manual Deposit</string>
<string name="title_onrampProviderManualDeposit">Manual USDC Deposit</string>
<string name="title_onrampProviderBackpack">Backpack Wallet</string>
<string name="title_onrampProviderPhantom">Phantom Wallet</string>
<string name="title_onrampProviderSolflare">Solflare Wallet</string>
Expand Down Expand Up @@ -497,6 +507,8 @@
<string name="title_cashReserves">USDF</string>
<string name="action_buyMore">Buy More</string>

<string name="title_addMoney">Add Money</string>

<string name="title_amountToBuy">Amount to Buy</string>
<string name="title_amountToSell">Amount to Sell</string>
<string name="subtitle_buySellCashHint">Enter up to %1$s</string>
Expand All @@ -509,7 +521,7 @@
<string name="label_solanaUsdc">Solana USDC with</string>
<string name="title_sellToken">Sell %1$s</string>
<string name="title_purchasingToken">Purchasing %1$s</string>
<string name="title_depositingToken">Depositing %1$s</string>
<string name="title_addingMoney">Adding Money</string>
<string name="title_sellingToken">Selling %1$s</string>
<string name="label_sellWarning">Review the above before confirming.\nOnce made, your transaction is irreversible.</string>

Expand Down Expand Up @@ -730,6 +742,10 @@
<string name="title_buyWithPhantom">Buy With Phantom</string>
<string name="description_buyWithPhantom">Purchase using Solana USDC in Phantom. Simply connect your wallet and confirm the transaction</string>


<string name="title_addMoneyWithPhantom">Add Money With Phantom</string>
<string name="description_addMoneyWithPhantom">Add money using Solana USDC in Phantom. Your USDC will be automatically converted to USDF 1:1</string>

<string name="title_confirmation">Confirmation</string>
<string name="title_buyWithPhantomConnected">Connected</string>
<string name="description_buyWithPhantomConnected">Confirm the transaction in Phantom to continue</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,15 @@ internal fun BalanceScreen(
val balanceState by viewModel.stateFlow.collectAsStateWithLifecycle()
val tokenState by tokenViewModel.stateFlow.collectAsStateWithLifecycle()
BalanceScreenContent(
depositFirstUx = balanceState.depositFirstUx,
addMoneyUx = balanceState.depositFirstUx,
tokenState = tokenState,
dispatchEvent = viewModel::dispatchEvent
)
}

@Composable
private fun BalanceScreenContent(
depositFirstUx: Boolean = false,
addMoneyUx: Boolean = false,
tokenState: SelectTokenViewModel.State,
dispatchEvent: (BalanceViewModel.Event) -> Unit
) {
Expand Down Expand Up @@ -100,8 +100,8 @@ private fun BalanceScreenContent(

Text(
modifier = Modifier.fillMaxWidth(0.6f),
text = if (depositFirstUx) {
stringResource(R.string.description_noBalanceYet)
text = if (addMoneyUx) {
stringResource(R.string.description_noBalanceYetForBalance)
} else {
stringResource(R.string.description_noBalanceYetDiscover)
},
Expand All @@ -118,8 +118,8 @@ private fun BalanceScreenContent(
.padding(top = CodeTheme.dimens.grid.x2)
.align(Alignment.CenterHorizontally),
contentPadding = PaddingValues(),
text = if (depositFirstUx) {
stringResource(R.string.action_depositFunds)
text = if (addMoneyUx) {
stringResource(R.string.action_addMoney)
} else {
stringResource(R.string.action_discoverCurrencies)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ internal class BalanceViewModel @Inject constructor(
}

init {
featureFlags.observe(FeatureFlag.DepositFirstUX)
featureFlags.observe(FeatureFlag.AddMoneyUX)
.onEach { dispatchEvent(Event.DepositFirstUxEnabled(it)) }
.launchIn(viewModelScope)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.flipcash.app.tokens.TokenCoordinator
import com.flipcash.features.cash.R
import com.flipcash.libs.coroutines.DispatcherProvider
import com.flipcash.shared.amountentry.AmountEntryDelegate
import com.flipcash.shared.amountentry.AmountEntryLabel
import com.flipcash.shared.amountentry.AmountEntryStyle
import com.getcode.manager.BottomBarAction
import com.getcode.manager.BottomBarManager
Expand Down Expand Up @@ -75,7 +76,7 @@ internal class CashScreenViewModel @Inject constructor(
exchange = exchange,
scope = viewModelScope,
style = AmountEntryStyle(
actionLabel = resources.getString(R.string.action_next),
actionLabel = AmountEntryLabel.Plain(resources.getString(R.string.action_next)),
infoHint = { resources.getString(R.string.subtitle_giveCashHint, it) },
overMaxHint = { resources.getString(R.string.subtitle_giveCashHintLimitExceeded, it) },
),
Expand Down
2 changes: 2 additions & 0 deletions apps/flipcash/features/contact-verification/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,7 @@ dependencies {
implementation(project(":apps:flipcash:shared:analytics"))
implementation(project(":apps:flipcash:shared:featureflags"))
implementation(project(":apps:flipcash:shared:phone"))
implementation(project(":apps:flipcash:shared:userflags"))

implementation(project(":libs:messaging"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,14 @@ fun EmailVerificationContent(
}
}.launchIn(this)
}

LaunchedEffect(viewModel) {
viewModel.eventFlow
.filterIsInstance<EmailVerificationViewModel.Event.OnEntrySaved>()
.onEach {
keyboard.hideIfVisible {
flowNavigator.exitWithResult(VerificationResult.Success)
}
}.launchIn(this)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import androidx.lifecycle.viewModelScope
import com.flipcash.app.core.verification.email.EmailCodeChannel
import com.flipcash.app.core.verification.email.EmailDeeplinkOrigin
import com.flipcash.app.core.extensions.onResult
import com.flipcash.app.userflags.UserFlagsCoordinator
import com.getcode.opencode.utils.base64
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
Expand Down Expand Up @@ -45,6 +46,7 @@ class EmailVerificationViewModel @Inject constructor(
private val resources: ResourceHelper,
private val dispatchers: DispatcherProvider,
private val emailCodeChannel: EmailCodeChannel,
private val userFlags: UserFlagsCoordinator,
) : BaseViewModel<EmailVerificationViewModel.State, EmailVerificationViewModel.Event>(
initialState = State(),
updateStateForEvent = updateStateForEvent,
Expand All @@ -66,6 +68,8 @@ class EmailVerificationViewModel @Inject constructor(
data class OnOriginSet(val origin: EmailDeeplinkOrigin?) : Event
data class OnDataProvided(val email: String?, val code: String?) : Event
data object OnSendCodeClicked : Event
/** Emitted in skip-verification mode once the entered email is persisted locally. */
data object OnEntrySaved : Event
data object OnResendCodeClicked: Event
data class OnSendingCodeChanged(
val loading: Boolean = false,
Expand Down Expand Up @@ -111,11 +115,27 @@ class EmailVerificationViewModel @Inject constructor(

eventFlow
.filterIsInstance<Event.OnSendCodeClicked>()
.map {
.onEach {
val emailAddress = stateFlow.value.email.text.toString()
ContactMethod.Email(emailAddress, computeClientData())
}.onEach { handleSendVerificationCode(it) }
.launchIn(viewModelScope)
val requiresVerification = userFlags.resolvedFlags.value
.requireCoinbaseEmailVerification.effectiveValue

if (!requiresVerification) {
dispatchEvent(Event.OnSendingCodeChanged(loading = true))
viewModelScope.launch {
delay(1.seconds)
dispatchEvent(Event.OnSendingCodeChanged(success = true))
delay(1.seconds)
// Skip server verification: record the email locally as unverified
// and complete. The profile write is persisted by ProfileCoordinator.
verificationController.setLocalUnverified(ContactMethod.Email(emailAddress))
dispatchEvent(Event.OnEntrySaved)
dispatchEvent(Event.OnSendingCodeChanged())
}
} else {
handleSendVerificationCode(ContactMethod.Email(emailAddress, computeClientData()))
}
}.launchIn(viewModelScope)

eventFlow
.filterIsInstance<Event.OnResendCodeClicked>()
Expand Down Expand Up @@ -270,6 +290,7 @@ class EmailVerificationViewModel @Inject constructor(
val updateStateForEvent: (Event) -> ((State) -> State) = { event ->
when (event) {
is Event.OnOriginSet -> { state -> state }
Event.OnEntrySaved -> { state -> state }
is Event.OnDataProvided -> { state ->
state.copy(
email = TextFieldState(event.email ?: state.email.text.toString()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@ import com.getcode.manager.BottomBarManager
import com.getcode.util.resources.FakeResourceHelper
import com.flipcash.app.core.MainCoroutineRule
import com.flipcash.app.core.dispatchers.TestDispatchers
import com.flipcash.app.userflags.FieldOverride
import com.flipcash.app.userflags.ResolvedFlag
import com.flipcash.app.userflags.ResolvedUserFlags
import com.flipcash.app.userflags.UserFlagsCoordinator
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
Expand All @@ -32,12 +38,21 @@ class EmailVerificationViewModelErrorTest {
private val verificationController: ContactVerificationController = mock()
private val profileController = mockk<ProfileController>(relaxed = true)
private val resources = FakeResourceHelper()
private val userFlags = mockk<UserFlagsCoordinator>(relaxed = true)

private lateinit var dispatchers: TestDispatchers

@Before
fun setUp() {
BottomBarManager.clear()
// Verification required → OnSendCodeClicked takes the server send path.
val resolvedFlags = mockk<ResolvedUserFlags>(relaxed = true) {
every { requireCoinbaseEmailVerification } returns ResolvedFlag(
serverValue = true,
override = FieldOverride.None,
)
}
every { userFlags.resolvedFlags } returns MutableStateFlow(resolvedFlags)
}

@After
Expand All @@ -52,6 +67,7 @@ class EmailVerificationViewModelErrorTest {
resources = resources,
dispatchers = dispatchers,
emailCodeChannel = EmailCodeChannel(),
userFlags = userFlags,
)
}

Expand Down
1 change: 1 addition & 0 deletions apps/flipcash/features/currency-creator/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ android {
dependencies {
implementation(project(":apps:flipcash:features:bill-customization"))
implementation(project(":apps:flipcash:shared:bills"))
implementation(project(":apps:flipcash:shared:featureflags"))
implementation(project(":apps:flipcash:shared:currency-creator"))
implementation(project(":apps:flipcash:shared:onramp:deeplinks"))
implementation(project(":apps:flipcash:shared:payments"))
Expand Down
Loading
Loading