diff --git a/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/tipping/TipSelection.kt b/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/tipping/TipSelection.kt index baf33284b..abe40f32c 100644 --- a/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/tipping/TipSelection.kt +++ b/apps/flipcash/core/src/main/kotlin/com/flipcash/app/core/tipping/TipSelection.kt @@ -47,6 +47,7 @@ data class TipSelectionState( val canTip: Boolean = false, /** Suggested tip presets in the sender's preferred currency; updates when the region changes. */ val presets: List = emptyList(), + val minimum: Fiat? = null, ) { /** A positive amount is chosen and no submission is currently in flight. */ val canConfirm: Boolean get() = amount != null && sendState.isIdle diff --git a/apps/flipcash/core/src/main/res/values/strings.xml b/apps/flipcash/core/src/main/res/values/strings.xml index 77ac9e66c..a1b24ea77 100644 --- a/apps/flipcash/core/src/main/res/values/strings.xml +++ b/apps/flipcash/core/src/main/res/values/strings.xml @@ -845,6 +845,10 @@ Are You Sure? Anyone you sent the link to won\'t be able to collect the cash + You tipped + You received + You sent + You received a tip %1$s of %2$s You sent %1$s You received %1$s diff --git a/apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/bills/decor/TipCardDecorator.kt b/apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/bills/decor/TipCardDecorator.kt index 21c4e95a8..8ced74fc6 100644 --- a/apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/bills/decor/TipCardDecorator.kt +++ b/apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/bills/decor/TipCardDecorator.kt @@ -57,11 +57,12 @@ internal data class TipCardDecorator(private val tipCard: Scannable.TipCard) : S // the UI, so we route directly rather than plumbing a callback through the session. LaunchedEffect(tipPresented, selection.canTip) { if (tipPresented && !selection.canTip) { - session?.presentDepositOptions { route -> - navigator.openAsSheet(route) + session?.presentDepositOptions( // No giveable balance to tip with → steer to add-money / discover, and dismiss the // tip card so we don't leave it stranded behind the prompt. - context.onDismiss() + onDismiss = { context.onDismiss() } + ) { route -> + navigator.openAsSheet(route) } } } diff --git a/apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/ui/modals/TipUserModal.kt b/apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/ui/modals/TipUserModal.kt index ffb67c857..fe1d0262c 100644 --- a/apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/ui/modals/TipUserModal.kt +++ b/apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/ui/modals/TipUserModal.kt @@ -81,8 +81,7 @@ internal fun TipUserModal( token = selection.token, modifier = Modifier.fillMaxWidth(), onSelectToken = { - val threshold = selection.amount?.value ?: selection.presets.firstOrNull() - navigator.openAsSheet(AppRoute.Sheets.TokenSelection(TokenPurpose.Tip(threshold))) + navigator.openAsSheet(AppRoute.Sheets.TokenSelection(TokenPurpose.Tip( selection.minimum))) }, ) diff --git a/apps/flipcash/shared/chat-ui/src/main/kotlin/com/flipcash/shared/chat/ui/MessageBubble.kt b/apps/flipcash/shared/chat-ui/src/main/kotlin/com/flipcash/shared/chat/ui/MessageBubble.kt index c1cdb5782..2f8b3dff0 100644 --- a/apps/flipcash/shared/chat-ui/src/main/kotlin/com/flipcash/shared/chat/ui/MessageBubble.kt +++ b/apps/flipcash/shared/chat-ui/src/main/kotlin/com/flipcash/shared/chat/ui/MessageBubble.kt @@ -55,7 +55,6 @@ import com.getcode.opencode.model.financial.Fiat import com.getcode.theme.CodeTheme import com.getcode.ui.components.PriceWithFlag import com.getcode.ui.core.addIf -import com.getcode.util.resources.R enum class BubblePosition { Solo, First, Middle, Last } @@ -212,7 +211,7 @@ private fun CashBubble( ) { val subtitleRes = when (action) { MessageContent.Cash.Action.TIPPED -> - if (isFromSelf) R.string.subtitle_youTipped else R.string.subtitle_someoneTippedYou + if (isFromSelf) R.string.subtitle_youTipped else R.string.subtitle_youReceivedTip MessageContent.Cash.Action.SENT -> if (isFromSelf) R.string.subtitle_youSent else R.string.subtitle_youReceived } diff --git a/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/SessionController.kt b/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/SessionController.kt index 89760ac8d..e03c82350 100644 --- a/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/SessionController.kt +++ b/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/SessionController.kt @@ -44,7 +44,7 @@ interface DepositOperations { * an add-money prompt when the wallet is empty, or a discover-currencies prompt when * the user has funds (e.g. reserves) but nothing giveable. */ - fun presentDepositOptions(onRoute: ((AppRoute) -> Unit)? = null) + fun presentDepositOptions(onDismiss: (() -> Unit)? = null, onRoute: ((AppRoute) -> Unit)? = null) } interface SessionController : BillOperations, CodeScanOperations, CashLinkOperations, DepositOperations, TipCardOperations { diff --git a/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/delegates/DepositDelegate.kt b/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/delegates/DepositDelegate.kt index 0c55471c5..12db35e12 100644 --- a/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/delegates/DepositDelegate.kt +++ b/apps/flipcash/shared/session/src/main/kotlin/com/flipcash/app/session/internal/delegates/DepositDelegate.kt @@ -53,7 +53,7 @@ class DepositDelegate @Inject constructor( .launchIn(scope) } - override fun presentDepositOptions(onRoute: ((AppRoute) -> Unit)?) { + override fun presentDepositOptions(onDismiss: (() -> Unit)?, onRoute: ((AppRoute) -> Unit)?) { // Prompt to add money only when the wallet is empty and add-money is available. // Otherwise the user has funds (e.g. reserves) but nothing giveable — or can't // add money at all — so steer them to discover/buy a currency. @@ -61,13 +61,17 @@ class DepositDelegate @Inject constructor( val hasBalance = stateHolder.current.hasBalance if (!hasBalance) { - presentAddMoney(addMoneyEnabled, onRoute) + presentAddMoney(addMoneyEnabled, onRoute, onDismiss) } else { - presentDiscoverCurrencies(onRoute) + presentDiscoverCurrencies(onRoute, onDismiss) } } - private fun presentAddMoney(addMoneyEnabled: Boolean, onRoute: ((AppRoute) -> Unit)?) { + private fun presentAddMoney( + addMoneyEnabled: Boolean, + onRoute: ((AppRoute) -> Unit)?, + onDismiss: (() -> Unit)? + ) { BottomBarManager.showInfo( title = resources.getString(R.string.title_noBalanceYet), message = if (addMoneyEnabled) { @@ -90,10 +94,11 @@ class DepositDelegate @Inject constructor( }, ), showCancel = true, + onDismiss = { onDismiss?.invoke() } ) } - private fun presentDiscoverCurrencies(onRoute: ((AppRoute) -> Unit)?) { + private fun presentDiscoverCurrencies(onRoute: ((AppRoute) -> Unit)?, onDismiss: (() -> Unit)?) { BottomBarManager.showInfo( title = resources.getString(R.string.title_noCommunityCurrenciesYet), message = resources.getString(R.string.description_noCommunityCurrenciesYet), @@ -105,6 +110,7 @@ class DepositDelegate @Inject constructor( }, ), showCancel = true, + onDismiss = { onDismiss?.invoke() } ) } diff --git a/apps/flipcash/shared/tipping/src/main/kotlin/com/flipcash/shared/tipping/TippingCoordinator.kt b/apps/flipcash/shared/tipping/src/main/kotlin/com/flipcash/shared/tipping/TippingCoordinator.kt index 4f7ed58db..34aa0b992 100644 --- a/apps/flipcash/shared/tipping/src/main/kotlin/com/flipcash/shared/tipping/TippingCoordinator.kt +++ b/apps/flipcash/shared/tipping/src/main/kotlin/com/flipcash/shared/tipping/TippingCoordinator.kt @@ -26,6 +26,7 @@ import com.getcode.opencode.exchange.VerifiedFiatCalculator import com.getcode.opencode.model.core.errors.ComputeVerifiedFiatError import com.getcode.opencode.model.financial.Fiat import com.getcode.opencode.model.financial.Token +import com.getcode.opencode.utils.combine import com.getcode.util.resources.ResourceHelper import com.getcode.view.LoadingSuccessState import kotlinx.coroutines.CoroutineScope @@ -101,16 +102,23 @@ class TippingCoordinator @Inject constructor( tokenCoordinator.tokens.map { tokens -> tokens.find { it.address == mint } } } + /** The largest tippable amount (send-limit ∧ balance), surfaced by the amount entry. */ + val maxTipAmount: StateFlow get() = tipPaymentDelegate.maxTipAmount + + /** The smallest tippable amount (lowest preset tier), surfaced by the amount entry. */ + val minTipAmount: StateFlow get() = tipPaymentDelegate.minTipAmount + /** The combined tip selection (amount chosen in the modal + app-global token + send state). */ override val selection: StateFlow = combine( - combine(_amount, selectedToken, _sendState, _userId, _canTip) { amount, token, sendState, userId, canTip -> + combine(_amount, selectedToken, _sendState, _userId, _canTip, minTipAmount) { amount, token, sendState, userId, canTip, min -> TipSelectionState( amount = amount, token = token, sendState = sendState, userId = userId, canTip = canTip, + minimum = min ) }, tipPaymentDelegate.tipPresets, @@ -128,12 +136,6 @@ class TippingCoordinator @Inject constructor( .launchIn(scope) } - /** The largest tippable amount (send-limit ∧ balance), surfaced by the amount entry. */ - val maxTipAmount: StateFlow get() = tipPaymentDelegate.maxTipAmount - - /** The smallest tippable amount (lowest preset tier), surfaced by the amount entry. */ - val minTipAmount: StateFlow get() = tipPaymentDelegate.minTipAmount - /** Whether [amount] exceeds the per-transaction send limit for its currency. */ fun exceedsSendLimit(amount: Fiat): Boolean = tipPaymentDelegate.exceedsSendLimit(amount)