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
5 changes: 4 additions & 1 deletion apps/flipcash/core/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -538,12 +538,15 @@
<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_buyToken">Buy %1$s</string>
<string name="title_sellToken">Sell %1$s</string>
<string name="title_confirmPurchase">Confirm Purchase</string>
<string name="title_confirmSale">Confirm Sale</string>
<string name="title_purchasingToken">Purchasing %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>
<string name="label_buyWarning">Review the above before confirming.\nOnce made, your transaction is irreversible.</string>

<string name="label_yes">Yes</string>
<string name="label_no">No</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ 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.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.flipcash.app.core.tokens.SwapResult
import com.flipcash.app.core.tokens.SwapStep
import com.flipcash.app.tokens.internal.TokenBuyReceiptScreen
Expand All @@ -23,13 +25,14 @@ import kotlinx.coroutines.flow.onEach
internal fun BuyReceiptScreen() {
val flowNavigator = rememberFlowNavigator<SwapStep, SwapResult>()
val viewModel = flowSharedViewModel<SwapViewModel>()
val state by viewModel.stateFlow.collectAsStateWithLifecycle()

Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
AppBarWithTitle(
title = stringResource(R.string.title_buyToken),
title = stringResource(R.string.title_confirmPurchase),
titleAlignment = Alignment.CenterHorizontally,
backButton = true,
onBackIconClicked = { flowNavigator.back() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ 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.map
import kotlinx.coroutines.flow.onEach

@Composable
Expand All @@ -33,7 +32,7 @@ internal fun SellReceiptScreen() {
horizontalAlignment = Alignment.CenterHorizontally,
) {
AppBarWithTitle(
title = stringResource(R.string.title_sellToken, state.tokenName),
title = stringResource(R.string.title_confirmSale),
titleAlignment = Alignment.CenterHorizontally,
backButton = true,
onBackIconClicked = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,19 @@ package com.flipcash.app.tokens.internal
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
Expand All @@ -25,6 +31,7 @@ import com.flipcash.app.core.ui.ReceiptLineItem
import com.flipcash.app.core.ui.TokenBalanceRow
import com.flipcash.app.core.ui.TokenBalanceStyle
import com.flipcash.app.core.ui.rememberTokenBalanceRowStyling
import com.flipcash.app.core.ui.shimmer
import com.flipcash.app.tokens.ui.SwapViewModel
import com.flipcash.features.tokens.R
import com.getcode.opencode.model.financial.Fiat
Expand Down Expand Up @@ -62,7 +69,7 @@ private fun TokenBuyReceiptScreen(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = CodeTheme.dimens.grid.x5),
text = stringResource(R.string.label_sellWarning),
text = stringResource(R.string.label_buyWarning),
style = CodeTheme.typography.textSmall,
color = CodeTheme.colors.textSecondary,
textAlign = TextAlign.Center,
Expand Down Expand Up @@ -97,9 +104,9 @@ private fun TokenBuyReceiptScreen(
)
) {
BuyReceipt(
fundingToken = state.fundingTokenWithBalance!!.token,
desiredToken = state.tokenWithBalance!!.token,
purchaseAmount = state.confirmedEnteredAmount!!,
fundingToken = state.fundingTokenWithBalance?.token,
desiredToken = state.tokenWithBalance?.token,
purchaseAmount = state.confirmedEnteredAmount,
feeAmount = state.feeAmount,
)
}
Expand All @@ -108,16 +115,20 @@ private fun TokenBuyReceiptScreen(

@Composable
private fun BuyReceipt(
fundingToken: Token,
desiredToken: Token,
purchaseAmount: Fiat,
fundingToken: Token?,
desiredToken: Token?,
purchaseAmount: Fiat?,
feeAmount: Fiat,
modifier: Modifier = Modifier,
) {
// The funding token and confirmed amount resolve asynchronously after landing here, so
// either can briefly be null on first composition. Render a shimmer in place of each token
// row until its data arrives rather than crashing or hiding the row entirely.
val feeAdjustedPurchaseAmount by remember(purchaseAmount, feeAmount) {
derivedStateOf {
if (!feeAmount.hasDisplayableValue) return@derivedStateOf purchaseAmount
purchaseAmount + feeAmount
purchaseAmount?.let {
if (!feeAmount.hasDisplayableValue) it else it + feeAmount
}
}
}

Expand All @@ -144,29 +155,34 @@ private fun BuyReceipt(
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = stringResource(R.string.subtitle_youPay),
text = stringResource(R.string.subtitle_youReceive),
style = CodeTheme.typography.textSmall,
color = CodeTheme.colors.textSecondary,
)
TokenBalanceRow(
tokenWithBalance = TokenWithBalance(
token = fundingToken,
balance = feeAdjustedPurchaseAmount
),
showName = false,
showLogo = true,
showFlag = false,
horizontalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x2),
styling = rememberTokenBalanceRowStyling(
balanceDisplayStyle = TokenBalanceStyle.Large(
textStyle = CodeTheme.typography.displaySmall.bolded()

if (desiredToken != null && purchaseAmount != null) {
TokenBalanceRow(
tokenWithBalance = TokenWithBalance(
token = desiredToken,
balance = purchaseAmount
),
),
contentPadding = PaddingValues(0.dp),
)
showName = false,
showLogo = true,
showFlag = false,
horizontalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x2),
styling = rememberTokenBalanceRowStyling(
balanceDisplayStyle = TokenBalanceStyle.Large(
textStyle = CodeTheme.typography.displaySmall.bolded()
),
),
contentPadding = PaddingValues(0.dp),
)
} else {
TokenBalanceRowPlaceholder()
}
}

if (feeAmount.isPositive) {
if (feeAmount.isPositive && purchaseAmount != null) {
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x2),
Expand All @@ -192,26 +208,56 @@ private fun BuyReceipt(
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = stringResource(R.string.subtitle_youReceive),
text = stringResource(R.string.subtitle_youPay),
style = CodeTheme.typography.textSmall,
color = CodeTheme.colors.textSecondary,
)
TokenBalanceRow(
tokenWithBalance = TokenWithBalance(
token = desiredToken,
balance = purchaseAmount
),
showName = false,
showLogo = true,
showFlag = false,
horizontalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x2),
styling = rememberTokenBalanceRowStyling(
balanceDisplayStyle = TokenBalanceStyle.Large(
textStyle = CodeTheme.typography.displaySmall.bolded()
if (fundingToken != null && feeAdjustedPurchaseAmount != null) {
TokenBalanceRow(
tokenWithBalance = TokenWithBalance(
token = fundingToken,
balance = feeAdjustedPurchaseAmount!!
),
),
contentPadding = PaddingValues(0.dp),
)
showName = false,
showLogo = true,
showFlag = false,
horizontalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x2),
styling = rememberTokenBalanceRowStyling(
balanceDisplayStyle = TokenBalanceStyle.Large(
textStyle = CodeTheme.typography.displaySmall.bolded()
),
),
contentPadding = PaddingValues(0.dp),
)
} else {
TokenBalanceRowPlaceholder()
}
}
}
}

/**
* Shimmer stand-in for a [TokenBalanceRow] shown with a logo + large balance, used while the
* token or amount is still resolving. Mirrors the row's layout: a circular logo followed by the
* balance text.
*/
@Composable
private fun TokenBalanceRowPlaceholder(modifier: Modifier = Modifier) {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(CodeTheme.dimens.grid.x2),
verticalAlignment = Alignment.CenterVertically,
) {
Box(
Modifier
.size(CodeTheme.dimens.staticGrid.x6)
.shimmer(CircleShape)
)
Box(
Modifier
.width(CodeTheme.dimens.grid.x20)
.height(CodeTheme.dimens.grid.x6)
.shimmer()
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,6 @@ private fun SelectTokenScreenContent(
},
enableGreaterThanAmount = { _, amount ->
when (val purpose = state.purpose) {
is TokenPurpose.Swap -> {
amount.nativeAmount.valueGreaterThanOrEqualTo(purpose.amount)
}
is TokenPurpose.LaunchFunding -> {
amount.nativeAmount.valueGreaterThanOrEqualTo(purpose.amount)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,7 @@ class SwapViewModel @Inject constructor(
val amountFiat = verifiedFiatCalculator.compute(
amount = Fiat(delegateState.enteredAmount, rate.currency),
token = token,
balance = tokenCoordinator.balanceForToken(token),
rate = rate,
).getOrElse {
BottomBarManager.showAlert(
Expand Down
Loading