Skip to content
Merged
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 @@ -15,6 +15,14 @@ sealed interface SwapStep : FlowStep, Parcelable {
@Serializable
data class Entry(val purpose: SwapPurpose, val initialAmount: Fiat? = null) : SwapStep

@Parcelize
@Serializable
data class TokenSelection(val amount: Fiat) : SwapStep

@Parcelize
@Serializable
data object BuyReceipt : SwapStep

@Parcelize
@Serializable
data object SellReceipt : SwapStep
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package com.flipcash.app.core.tokens

import android.os.Parcelable
import com.getcode.opencode.model.financial.Fiat
import com.getcode.opencode.model.financial.LocalFiat
import com.getcode.solana.keys.Mint
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable

@Serializable
@Parcelize
sealed interface TokenPurpose: Parcelable {
@Serializable data object Select : TokenPurpose
@Serializable data class Purchase(val desiredToken: Mint, val amount: Fiat) : TokenPurpose
@Serializable data object Withdraw: TokenPurpose
@Serializable data object Deposit: TokenPurpose
@Serializable data object Balance : TokenPurpose
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
Expand Down Expand Up @@ -56,6 +57,7 @@ data class TokenBalanceRowStyling(
val iconSize: Dp,
val flagSize: Dp,
val selectionStyle: TokenSelectionStyle,
val disabledAlpha: Float,
)

@Composable
Expand All @@ -65,13 +67,15 @@ fun rememberTokenBalanceRowStyling(
iconSize: Dp = CodeTheme.dimens.staticGrid.x6,
flagSize: Dp = CodeTheme.dimens.staticGrid.x3,
selectionStyle: TokenSelectionStyle = TokenSelectionStyle.None,
disabledAlpha: Float = 0.8f,
): TokenBalanceRowStyling =
TokenBalanceRowStyling(
nameTextStyle = nameTextStyle,
balanceDisplayStyle = balanceDisplayStyle,
iconSize = iconSize,
flagSize = flagSize,
selectionStyle = selectionStyle
selectionStyle = selectionStyle,
disabledAlpha = disabledAlpha
)

@Composable
Expand All @@ -82,6 +86,7 @@ fun TokenBalanceRow(
showFlag: Boolean = false,
showLogo: Boolean = true,
isSelected: Boolean? = null,
isEnabled: Boolean = true,
iconOverride: @Composable ((Any?) -> Any?) = { it },
formattedBalance: (Fiat) -> String = { it.formatted() },
horizontalArrangement: Arrangement.Horizontal = Arrangement.SpaceBetween,
Expand All @@ -97,6 +102,7 @@ fun TokenBalanceRow(
iconOverride = iconOverride,
formattedBalance = formattedBalance,
isSelected = isSelected,
isEnabled = isEnabled,
modifier = modifier,
showName = showName,
showFlag = showFlag,
Expand All @@ -116,6 +122,7 @@ fun TokenBalanceRow(
showLogo: Boolean = true,
showFlag: Boolean = false,
isSelected: Boolean? = null,
isEnabled: Boolean = true,
iconOverride: @Composable ((Any?) -> Any?) = { it },
formattedBalance: (Fiat) -> String = { it.formatted() },
horizontalArrangement: Arrangement.Horizontal = Arrangement.SpaceBetween,
Expand All @@ -132,6 +139,7 @@ fun TokenBalanceRow(
showLogo = showLogo,
showFlag = showFlag,
isSelected = isSelected,
isEnabled = isEnabled,
modifier = modifier,
styling = styling,
iconOverride = iconOverride,
Expand All @@ -153,6 +161,7 @@ fun TokenBalanceRow(
showLogo: Boolean = true,
showFlag: Boolean = false,
isSelected: Boolean? = null,
isEnabled: Boolean = true,
iconOverride: @Composable ((Any?) -> Any?) = { it },
formattedBalance: (Fiat) -> String = { it.formatted() },
horizontalArrangement: Arrangement.Horizontal = Arrangement.SpaceBetween,
Expand All @@ -161,14 +170,21 @@ fun TokenBalanceRow(
onClick: (() -> Unit)? = null,
) {
val exchange = LocalExchange.current
val backgroundColor = CodeTheme.colors.background
Row(
modifier = Modifier
.addIf(onClick != null) {
.addIf(onClick != null && isEnabled) {
Modifier.clickable {
onClick?.invoke()
}
}
.then(modifier)
.addIf(!isEnabled) {
Modifier.drawWithContent {
drawContent()
drawRect(color = backgroundColor, alpha = styling.disabledAlpha)
}
}
.padding(contentPadding),
horizontalArrangement = horizontalArrangement,
verticalAlignment = Alignment.CenterVertically,
Expand Down
13 changes: 11 additions & 2 deletions apps/flipcash/core/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@
<string name="subtitle_addCashToWallet">Add Solana USDF to your Flipcash wallet</string>

<string name="title_selectCurrency">Select Currency</string>
<string name="title_selectPaymentCurrency">Select Payment Currency</string>
<string name="title_selectRegion">Select Region</string>
<string name="title_recentRegions">Recent Regions</string>
<string name="title_otherRegions">Other Regions</string>
Expand All @@ -441,8 +442,13 @@
<string name="error_title_tokenNotFound">Something Went Wrong</string>
<string name="error_description_tokenNotFound">Failed to fetch token metadata. Please try again</string>

<string name="subtitle_youPay">You Pay</string>
<string name="subtitle_youReceive">You Receive</string>

<string name="title_insufficientBalanceAfterFees">Insufficient Balance After Fees</string>
<string name="description_insufficientBalanceAfterFees">Switch to maximum amount, or go back and enter a smaller amount</string>
<string name="action_buyMaximumAmount">Buy Maximum Amount</string>

<string name="action_give">Give</string>
<string name="action_send">Send</string>
<string name="title_transactionHistory">Transaction History</string>
Expand Down Expand Up @@ -529,6 +535,7 @@
<string name="action_sell">Sell</string>
<string name="title_amountToWithdraw">Amount to Withdraw</string>
<string name="label_solanaUsdc">Solana USDC with</string>
<string name="title_buyToken">Buy</string>
<string name="title_sellToken">Sell %1$s</string>
<string name="title_purchasingToken">Purchasing %1$s</string>
<string name="title_addingMoney">Adding Money</string>
Expand Down Expand Up @@ -836,7 +843,9 @@

<string name="label_unknownContact">Unknown Contact</string>
<string name="title_allowFullContactAccess">Allow Full Contact Access</string>
><string name="subtitle_allowFullContactAccess">Make sure you can send cash and identify people you know</string>
<string name="action_allowAccess">Allow Access</string>">
<string name="subtitle_allowFullContactAccess">Make sure you can send cash and identify people you know</string>
<string name="action_allowAccess">Allow Access</string>

<string name="label_amountToBuy">Amount to buy</string>
<string name="label_exchangeFee">Exchange fee</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ internal fun SwapEntryScreen(
}
}

LaunchedEffect(viewModel) {
viewModel.eventFlow
.filterIsInstance<SwapViewModel.Event.SelectFundingToken>()
.map { it.amount }
.onEach { flowNavigator.navigateTo(SwapStep.TokenSelection(it)) }
.launchIn(this)
}

LaunchedEffect(viewModel) {
viewModel.eventFlow
.filterIsInstance<SwapViewModel.Event.ShowSellReceipt>()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.flipcash.app.tokens

import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation3.runtime.NavEntry
import androidx.navigation3.runtime.NavKey
import androidx.navigation3.runtime.entryProvider
Expand All @@ -10,15 +14,27 @@ import com.flipcash.app.core.tokens.FundingSource
import com.flipcash.app.core.tokens.SwapPurpose
import com.flipcash.app.core.tokens.SwapResult
import com.flipcash.app.core.tokens.SwapStep
import com.flipcash.app.core.tokens.TokenPurpose
import com.flipcash.app.tokens.ui.SelectTokenViewModel
import com.flipcash.app.tokens.ui.SwapViewModel
import com.getcode.opencode.model.financial.Fiat
import com.getcode.navigation.annotatedEntry
import com.getcode.navigation.core.LocalCodeNavigator
import com.getcode.navigation.flow.rememberInitialStack
import com.getcode.navigation.flow.FlowExitReason
import com.getcode.navigation.flow.FlowHost
import com.getcode.navigation.flow.deliverFlowResult
import com.getcode.navigation.flow.flowSharedViewModel
import com.getcode.navigation.flow.rememberFlowNavigator
import com.getcode.navigation.results.NavResultOrCanceled
import com.getcode.navigation.results.NavResultStateRegistry
import com.getcode.opencode.model.financial.LocalFiat
import com.getcode.solana.keys.Mint
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach

@Composable
fun SwapFlowScreen(
Expand Down Expand Up @@ -76,6 +92,12 @@ private fun swapEntryProvider(
annotatedEntry<SwapStep.Entry> { step ->
SwapEntryScreen(step.purpose, step.initialAmount)
}
annotatedEntry<SwapStep.TokenSelection> { key ->
SwapPurchaseTokenSelectScreen(route.purpose.mint, key.amount)
}
annotatedEntry<SwapStep.BuyReceipt> {
BuyReceiptScreen()
}
annotatedEntry<SwapStep.SellReceipt> { SellReceiptScreen() }
annotatedEntry<SwapStep.PhantomConnect> {
PhantomConnectConfirmationScreen(depositFirstPurpose = depositFirstPurpose)
Expand All @@ -87,3 +109,24 @@ private fun swapEntryProvider(
SwapProcessingScreen()
}
}

@Composable
private fun SwapPurchaseTokenSelectScreen(targetMint: Mint, amount: Fiat) {
val selectionViewModel = hiltViewModel<SelectTokenViewModel>()
val viewModel = flowSharedViewModel<SwapViewModel>()
val flowNavigator = rememberFlowNavigator<SwapStep, SwapResult>()

TokenSelectScreen(TokenPurpose.Purchase(targetMint, amount))

LaunchedEffect(selectionViewModel) {
selectionViewModel.eventFlow
.filterIsInstance<SelectTokenViewModel.Event.OnTokenSelected>()
.filter { it.fromUser }
.map { it.mint }
.onEach {
viewModel.dispatchEvent(SwapViewModel.Event.OnFundingTokenSelected(it))
flowNavigator.navigateTo(SwapStep.BuyReceipt)
}
.launchIn(this)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.flipcash.app.tokens

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.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import com.flipcash.app.core.tokens.SwapResult
import com.flipcash.app.core.tokens.SwapStep
import com.flipcash.app.tokens.internal.TokenBuyReceiptScreen
import com.flipcash.app.tokens.ui.SwapViewModel
import com.flipcash.features.tokens.R
import com.getcode.navigation.flow.flowSharedViewModel
import com.getcode.navigation.flow.rememberFlowNavigator
import com.getcode.ui.components.AppBarWithTitle
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

@Composable
internal fun BuyReceiptScreen() {
val flowNavigator = rememberFlowNavigator<SwapStep, SwapResult>()
val viewModel = flowSharedViewModel<SwapViewModel>()

Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
AppBarWithTitle(
title = stringResource(R.string.title_buyToken),
titleAlignment = Alignment.CenterHorizontally,
backButton = true,
onBackIconClicked = { flowNavigator.back() }
)

TokenBuyReceiptScreen(viewModel)
}

LaunchedEffect(viewModel) {
viewModel.eventFlow
.filterIsInstance<SwapViewModel.Event.OnPurchaseSubmitted>()
.onEach {
flowNavigator.navigateTo(SwapStep.Processing)
}.launchIn(this)
}
}
Loading
Loading