From 6e097a5928d0943d0ccc74ab63c15e44f5deaf9e Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Wed, 21 Oct 2026 00:40:53 +0200 Subject: [PATCH 1/3] fix: place the usdt amount keypad right above continue --- .../screens/wallets/send/SendAmountScreen.kt | 51 +++--- .../screens/wallets/usdt/UsdtSendContent.kt | 145 +++++++++--------- .../widgets/components/WidgetSizeCarousel.kt | 11 +- .../ui/shared/modifiers/BleedHorizontal.kt | 14 ++ changelog.d/next/1627.fixed.md | 1 + journeys/usdt-send-review-and-details.xml | 2 +- 6 files changed, 117 insertions(+), 107 deletions(-) create mode 100644 app/src/main/java/to/bitkit/ui/shared/modifiers/BleedHorizontal.kt create mode 100644 changelog.d/next/1627.fixed.md diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt index 45eabb6bb7..8cfb699cb3 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt @@ -85,6 +85,7 @@ import to.bitkit.ui.components.usdtOverviewAmount import to.bitkit.ui.screens.wallets.usdt.UsdtNetworkButton import to.bitkit.ui.settingsViewModel import to.bitkit.ui.shared.UiConstants +import to.bitkit.ui.shared.modifiers.bleedHorizontal import to.bitkit.ui.shared.modifiers.clickableAlpha import to.bitkit.ui.shared.modifiers.sheetHeight import to.bitkit.ui.shared.util.gradientBackground @@ -305,10 +306,12 @@ private fun SendAmountNodeRunning( VerticalSpacer(16.dp) if (uiState.paykitUsesUsdt) { - UsdtAmountHeader( - amount = uiState.paykitAmount.ifEmpty { "0" }, - modifier = Modifier.testTag("SendNumberField") - ) + Box(contentAlignment = Alignment.CenterStart, modifier = Modifier.weight(1f).fillMaxWidth()) { + UsdtAmountHeader( + amount = uiState.paykitAmount.ifEmpty { "0" }, + modifier = Modifier.testTag("SendNumberField") + ) + } } else { NumberPadTextField( viewModel = amountInputViewModel, @@ -318,7 +321,7 @@ private fun SendAmountNodeRunning( ) } - if (uiState.paykitUsesUsdt) VerticalSpacer(32.dp) else FillHeight(min = 12.dp) + if (!uiState.paykitUsesUsdt) FillHeight(min = 12.dp) if (uiState.paykitRateUnavailable) { BodyS( stringResource(R.string.wallet__payment_request_rate_unavailable), @@ -413,28 +416,24 @@ private fun SendAmountNodeRunning( HorizontalDivider(modifier = Modifier.padding(top = if (uiState.paykitUsesUsdt) 16.dp else 12.dp)) if (uiState.paykitUsesUsdt) { - BoxWithConstraints( - contentAlignment = Alignment.Center, - modifier = Modifier.weight(1f).fillMaxWidth() - ) { - NumberPad( - type = NumberPadType.DECIMAL, - decimalSeparator = DecimalFormatSymbols.getInstance().decimalSeparator.toString(), - availableHeight = this.maxHeight, - enabled = uiState.isAmountEditable, - onPress = { - app?.onUsdtAmountChange( - NumberPadInputHandler.handleInput( - it, - uiState.paykitAmount, - maxLength = 21, - maxDecimals = 6 - ) + NumberPad( + type = NumberPadType.DECIMAL, + decimalSeparator = DecimalFormatSymbols.getInstance().decimalSeparator.toString(), + availableHeight = maxHeight, + enabled = uiState.isAmountEditable, + onPress = { + app?.onUsdtAmountChange( + NumberPadInputHandler.handleInput( + it, + uiState.paykitAmount, + maxLength = 21, + maxDecimals = 6 ) - }, - onDeleteLongPress = { app?.onUsdtAmountChange("") } - ) - } + ) + }, + onDeleteLongPress = { app?.onUsdtAmountChange("") }, + modifier = Modifier.bleedHorizontal(16.dp).fillMaxWidth() + ) } else { NumberPad( enabled = uiState.isAmountEditable, diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtSendContent.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtSendContent.kt index ed5c1a4bfb..77bda67d91 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtSendContent.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtSendContent.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope @@ -91,6 +92,7 @@ import to.bitkit.ui.scaffold.SheetTopBar import to.bitkit.ui.screens.scanner.QrScanningScreen import to.bitkit.ui.screens.wallets.send.HourglassAnimation import to.bitkit.ui.shared.UiConstants +import to.bitkit.ui.shared.modifiers.bleedHorizontal import to.bitkit.ui.shared.modifiers.sheetHeight import to.bitkit.ui.sheets.NewTransactionSheetView import to.bitkit.ui.theme.AppTextStyles @@ -437,76 +439,78 @@ private fun UsdtAmountContent( onNetworkClick: (() -> Unit)?, destinations: ImmutableList, ) { - Column(modifier = Modifier.fillMaxSize()) { - UsdtAmountHeader(amount.ifEmpty { "0" }, modifier = Modifier.testTag("UsdtAmount")) - VerticalSpacer(32.dp) - state.error?.let { - BodyS( - state.errorMessage(it), - color = Colors.Brand, - modifier = Modifier.testTag("UsdtError") - ) - VerticalSpacer(16.dp) - } - FlowRow( - horizontalArrangement = Arrangement.SpaceBetween, - verticalArrangement = Arrangement.spacedBy(8.dp), - modifier = Modifier.fillMaxWidth(), - ) { - Column(modifier = Modifier.padding(end = 8.dp).align(Alignment.CenterVertically)) { - Caption13Up( - stringResource(R.string.wallet__send_available), - color = Colors.White64, - style = AppTextStyles.CaptionM.copy(letterSpacing = 1.sp), + BoxWithConstraints(modifier = Modifier.fillMaxSize()) { + val maxHeight = this.maxHeight + Column(modifier = Modifier.fillMaxSize()) { + Box(contentAlignment = Alignment.CenterStart, modifier = Modifier.weight(1f).fillMaxWidth()) { + UsdtAmountHeader(amount.ifEmpty { "0" }, modifier = Modifier.testTag("UsdtAmount")) + } + state.error?.let { + BodyS( + state.errorMessage(it), + color = Colors.Brand, + modifier = Modifier.testTag("UsdtError") ) - VerticalSpacer(4.dp) - Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) { - if (!hideBalance && wallet.balance != null) BodySSB("$", color = Colors.White64) - BodySSB( - text = if (hideBalance) { - UiConstants.HIDE_BALANCE_SHORT - } else { - wallet.balance?.let { usdtOverviewAmount(it) } ?: "—" - }, - style = AppTextStyles.BodySSB.copy(letterSpacing = 0.sp), - ) - } + VerticalSpacer(16.dp) } - Row( - horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.align(Alignment.CenterVertically), + FlowRow( + horizontalArrangement = Arrangement.SpaceBetween, + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.fillMaxWidth(), ) { - NumberPadActionButton( - text = "USDT", - color = Colors.Usdt, - enabled = false, - textStyle = AppTextStyles.CaptionM.copy(letterSpacing = 1.sp), - onClick = {}, - ) - if (onMaximum != null && amountEditable) { + Column(modifier = Modifier.padding(end = 8.dp).align(Alignment.CenterVertically)) { + Caption13Up( + stringResource(R.string.wallet__send_available), + color = Colors.White64, + style = AppTextStyles.CaptionM.copy(letterSpacing = 1.sp), + ) + VerticalSpacer(4.dp) + Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) { + if (!hideBalance && wallet.balance != null) BodySSB("$", color = Colors.White64) + BodySSB( + text = if (hideBalance) { + UiConstants.HIDE_BALANCE_SHORT + } else { + wallet.balance?.let { usdtOverviewAmount(it) } ?: "—" + }, + style = AppTextStyles.BodySSB.copy(letterSpacing = 0.sp), + ) + } + } + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.align(Alignment.CenterVertically), + ) { NumberPadActionButton( - text = stringResource(R.string.common__max), + text = "USDT", color = Colors.Usdt, - onClick = onMaximum, - enabled = !state.busy, - isLoading = state.preparingMaximum, + enabled = false, textStyle = AppTextStyles.CaptionM.copy(letterSpacing = 1.sp), - modifier = Modifier.testTag("UsdtMax") - ) - } - if (onNetworkClick != null) { - UsdtNetworkButton( - destination, - onNetworkClick.takeUnless { state.busy }, - destinations, + onClick = {}, ) + if (onMaximum != null && amountEditable) { + NumberPadActionButton( + text = stringResource(R.string.common__max), + color = Colors.Usdt, + onClick = onMaximum, + enabled = !state.busy, + isLoading = state.preparingMaximum, + textStyle = AppTextStyles.CaptionM.copy(letterSpacing = 1.sp), + modifier = Modifier.testTag("UsdtMax") + ) + } + if (onNetworkClick != null) { + UsdtNetworkButton( + destination, + onNetworkClick.takeUnless { state.busy }, + destinations, + ) + } } } - } - VerticalSpacer(16.dp) - HorizontalDivider(color = Colors.White10) - BoxWithConstraints(contentAlignment = Alignment.Center, modifier = Modifier.weight(1f).fillMaxWidth()) { + VerticalSpacer(16.dp) + HorizontalDivider(color = Colors.White10) NumberPad( type = NumberPadType.DECIMAL, decimalSeparator = DecimalFormatSymbols.getInstance().decimalSeparator.toString(), @@ -516,17 +520,18 @@ private fun UsdtAmountContent( onPress = { key -> onAmountChange(NumberPadInputHandler.handleInput(key, amount, maxLength = 21, maxDecimals = 6)) }, + modifier = Modifier.bleedHorizontal(16.dp).fillMaxWidth() + ) + VerticalSpacer(16.dp) + PrimaryButton( + text = stringResource(R.string.common__continue), + onClick = onReview, + enabled = amount.isNotEmpty() && !state.busy, + isLoading = state.busy && !state.preparingMaximum, + textStyle = AppTextStyles.BodySSB.copy(letterSpacing = 0.sp), + modifier = Modifier.testTag("UsdtReview") ) } - VerticalSpacer(16.dp) - PrimaryButton( - text = stringResource(R.string.common__continue), - onClick = onReview, - enabled = amount.isNotEmpty() && !state.busy, - isLoading = state.busy && !state.preparingMaximum, - textStyle = AppTextStyles.BodySSB.copy(letterSpacing = 0.sp), - modifier = Modifier.testTag("UsdtReview") - ) } } diff --git a/app/src/main/java/to/bitkit/ui/screens/widgets/components/WidgetSizeCarousel.kt b/app/src/main/java/to/bitkit/ui/screens/widgets/components/WidgetSizeCarousel.kt index 180cd1ede6..2db2afc61a 100644 --- a/app/src/main/java/to/bitkit/ui/screens/widgets/components/WidgetSizeCarousel.kt +++ b/app/src/main/java/to/bitkit/ui/screens/widgets/components/WidgetSizeCarousel.kt @@ -18,17 +18,16 @@ import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.layout.layout import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import kotlinx.coroutines.flow.drop import to.bitkit.R import to.bitkit.models.WidgetSize import to.bitkit.ui.components.Caption13Up import to.bitkit.ui.components.VerticalSpacer +import to.bitkit.ui.shared.modifiers.bleedHorizontal import to.bitkit.ui.theme.Colors @Composable @@ -134,14 +133,6 @@ fun WidgetSizeCarousel( } } -private fun Modifier.bleedHorizontal(padding: Dp): Modifier = layout { measurable, constraints -> - val bleed = padding.roundToPx() - val placeable = measurable.measure(constraints.copy(maxWidth = constraints.maxWidth + bleed * 2)) - layout(constraints.maxWidth, placeable.height) { - placeable.place(-bleed, 0) - } -} - private fun pageToSize(page: Int, supportsSmall: Boolean): WidgetSize { if (!supportsSmall) return WidgetSize.WIDE return if (page == PAGE_SMALL) WidgetSize.SMALL else WidgetSize.WIDE diff --git a/app/src/main/java/to/bitkit/ui/shared/modifiers/BleedHorizontal.kt b/app/src/main/java/to/bitkit/ui/shared/modifiers/BleedHorizontal.kt new file mode 100644 index 0000000000..c4b79afdb6 --- /dev/null +++ b/app/src/main/java/to/bitkit/ui/shared/modifiers/BleedHorizontal.kt @@ -0,0 +1,14 @@ +package to.bitkit.ui.shared.modifiers + +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.layout +import androidx.compose.ui.unit.Dp + +/** Widens the element by [padding] on each side, so it spans the parent's horizontal padding. */ +fun Modifier.bleedHorizontal(padding: Dp): Modifier = layout { measurable, constraints -> + val bleed = padding.roundToPx() + val placeable = measurable.measure(constraints.copy(maxWidth = constraints.maxWidth + bleed * 2)) + layout(constraints.maxWidth, placeable.height) { + placeable.place(-bleed, 0) + } +} diff --git a/changelog.d/next/1627.fixed.md b/changelog.d/next/1627.fixed.md new file mode 100644 index 0000000000..18bd54e5ec --- /dev/null +++ b/changelog.d/next/1627.fixed.md @@ -0,0 +1 @@ +The USDT amount keypad now sits right above Continue, with the amount centred above it. diff --git a/journeys/usdt-send-review-and-details.xml b/journeys/usdt-send-review-and-details.xml index 64186accf5..17365f1f68 100644 --- a/journeys/usdt-send-review-and-details.xml +++ b/journeys/usdt-send-review-and-details.xml @@ -2,7 +2,7 @@ Use an onboarded wallet with USDT, a fresh BTC/USD rate, an owned recipient and existing sent/received activity. Quotes require the service with MAX support. No payment is submitted. Enable USDT in the introductory sheet or Settings, General, Payments before starting this flow. - Open USDT Send and enter the owned recipient. On Amount, verify the BTC equivalent above the dollar amount, 32-point gap before Available balance, USDT badge, MAX and network pill. Enter a six-decimal amount and verify no precision is lost. With a large available balance and BNB Smart Chain selected, verify both values remain fully readable; the controls may wrap onto a second row. + Open USDT Send and enter the owned recipient. On Amount, verify the BTC equivalent above the dollar amount, both centred in the free space above Available balance, USDT badge, MAX and network pill. The controls sit right above the keypad, the keypad spans the sheet edge to edge and Continue sits right below it. Enter a six-decimal amount and verify no precision is lost. With a large available balance and BNB Smart Chain selected, verify both values remain fully readable; the controls may wrap onto a second row. Open the network pill and verify the full USDT Network picker, Tether rows and selected teal check. Select another EVM network. Verify the amount and valid recipient remain. Change to Solana with an EVM recipient and verify recipient entry is required. Enter an owned plain Solana address and verify the previous amount remains. Return to Arbitrum, enter the owned EVM address and verify the amount still remains. From recipient entry with another network selected, paste an explicit Arbitrum USDT URI. Verify the selected network becomes Arbitrum and the link amount is retained. A plain 0x address preserves the selected network. Tap MAX. Verify it fills a positive amount below the balance and remains on Amount. Continue and verify the payment plus maximum fee fits within the balance. Back out without swiping. From 33a05097e448eed6f3c005c9e75b2547c190764b Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Wed, 21 Oct 2026 01:46:39 +0200 Subject: [PATCH 2/3] fix: keep continue reachable on compact usdt amount screens --- .../screens/wallets/send/SendAmountScreen.kt | 28 +++++++++++++------ .../screens/wallets/usdt/UsdtSendContent.kt | 14 ++++++---- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt index 8cfb699cb3..6eb82b2e30 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt @@ -10,11 +10,14 @@ 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.heightIn 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.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.DropdownMenu import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon @@ -82,6 +85,7 @@ import to.bitkit.ui.components.UsdtAmountHeader import to.bitkit.ui.components.VerticalSpacer import to.bitkit.ui.components.WalletBalanceContent import to.bitkit.ui.components.usdtOverviewAmount +import to.bitkit.ui.screens.wallets.usdt.USDT_KEYPAD_HEIGHT_RATIO import to.bitkit.ui.screens.wallets.usdt.UsdtNetworkButton import to.bitkit.ui.settingsViewModel import to.bitkit.ui.shared.UiConstants @@ -301,17 +305,25 @@ private fun SendAmountNodeRunning( } Column( - modifier = Modifier.padding(horizontal = 16.dp) + modifier = Modifier + .padding(horizontal = 16.dp) + .then( + if (uiState.paykitUsesUsdt) { + Modifier.verticalScroll(rememberScrollState()).heightIn(min = maxHeight) + } else { + Modifier + } + ) ) { VerticalSpacer(16.dp) if (uiState.paykitUsesUsdt) { - Box(contentAlignment = Alignment.CenterStart, modifier = Modifier.weight(1f).fillMaxWidth()) { - UsdtAmountHeader( - amount = uiState.paykitAmount.ifEmpty { "0" }, - modifier = Modifier.testTag("SendNumberField") - ) - } + FillHeight() + UsdtAmountHeader( + amount = uiState.paykitAmount.ifEmpty { "0" }, + modifier = Modifier.testTag("SendNumberField") + ) + FillHeight() } else { NumberPadTextField( viewModel = amountInputViewModel, @@ -419,7 +431,7 @@ private fun SendAmountNodeRunning( NumberPad( type = NumberPadType.DECIMAL, decimalSeparator = DecimalFormatSymbols.getInstance().decimalSeparator.toString(), - availableHeight = maxHeight, + availableHeight = maxHeight * USDT_KEYPAD_HEIGHT_RATIO, enabled = uiState.isAmountEditable, onPress = { app?.onUsdtAmountChange( diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtSendContent.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtSendContent.kt index 77bda67d91..b8f064d5e4 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtSendContent.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtSendContent.kt @@ -5,7 +5,6 @@ import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope @@ -102,6 +101,9 @@ import to.bitkit.ui.utils.NumberPadInputHandler import java.text.DecimalFormatSymbols import kotlin.time.Duration.Companion.seconds +/** Share of the amount step's height the keypad sizes its keys from, so keys shrink before the amount gets squeezed. */ +internal const val USDT_KEYPAD_HEIGHT_RATIO = 0.4f + @Composable @Suppress("CyclomaticComplexMethod") internal fun UsdtPaymentContent( @@ -441,10 +443,10 @@ private fun UsdtAmountContent( ) { BoxWithConstraints(modifier = Modifier.fillMaxSize()) { val maxHeight = this.maxHeight - Column(modifier = Modifier.fillMaxSize()) { - Box(contentAlignment = Alignment.CenterStart, modifier = Modifier.weight(1f).fillMaxWidth()) { - UsdtAmountHeader(amount.ifEmpty { "0" }, modifier = Modifier.testTag("UsdtAmount")) - } + Column(modifier = Modifier.fillMaxWidth().verticalScroll(rememberScrollState()).heightIn(min = maxHeight)) { + FillHeight() + UsdtAmountHeader(amount.ifEmpty { "0" }, modifier = Modifier.testTag("UsdtAmount")) + FillHeight() state.error?.let { BodyS( state.errorMessage(it), @@ -514,7 +516,7 @@ private fun UsdtAmountContent( NumberPad( type = NumberPadType.DECIMAL, decimalSeparator = DecimalFormatSymbols.getInstance().decimalSeparator.toString(), - availableHeight = maxHeight, + availableHeight = maxHeight * USDT_KEYPAD_HEIGHT_RATIO, enabled = !state.busy && amountEditable, onDeleteLongPress = { onAmountChange("") }, onPress = { key -> From af98a0daa164a7d7f2d8f5909f9b88690142433b Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Wed, 21 Oct 2026 14:59:06 +0200 Subject: [PATCH 3/3] fix: keep the usdt amount layout the same as the bitcoin screens --- .../ui/screens/wallets/send/SendAmountScreen.kt | 10 ++-------- .../ui/screens/wallets/usdt/UsdtSendContent.kt | 11 ++--------- .../widgets/components/WidgetSizeCarousel.kt | 11 ++++++++++- .../bitkit/ui/shared/modifiers/BleedHorizontal.kt | 14 -------------- changelog.d/next/1627.fixed.md | 2 +- journeys/usdt-send-review-and-details.xml | 2 +- 6 files changed, 16 insertions(+), 34 deletions(-) delete mode 100644 app/src/main/java/to/bitkit/ui/shared/modifiers/BleedHorizontal.kt diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt index 6eb82b2e30..9ef94cae3a 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendAmountScreen.kt @@ -85,11 +85,9 @@ import to.bitkit.ui.components.UsdtAmountHeader import to.bitkit.ui.components.VerticalSpacer import to.bitkit.ui.components.WalletBalanceContent import to.bitkit.ui.components.usdtOverviewAmount -import to.bitkit.ui.screens.wallets.usdt.USDT_KEYPAD_HEIGHT_RATIO import to.bitkit.ui.screens.wallets.usdt.UsdtNetworkButton import to.bitkit.ui.settingsViewModel import to.bitkit.ui.shared.UiConstants -import to.bitkit.ui.shared.modifiers.bleedHorizontal import to.bitkit.ui.shared.modifiers.clickableAlpha import to.bitkit.ui.shared.modifiers.sheetHeight import to.bitkit.ui.shared.util.gradientBackground @@ -318,12 +316,10 @@ private fun SendAmountNodeRunning( VerticalSpacer(16.dp) if (uiState.paykitUsesUsdt) { - FillHeight() UsdtAmountHeader( amount = uiState.paykitAmount.ifEmpty { "0" }, modifier = Modifier.testTag("SendNumberField") ) - FillHeight() } else { NumberPadTextField( viewModel = amountInputViewModel, @@ -333,7 +329,7 @@ private fun SendAmountNodeRunning( ) } - if (!uiState.paykitUsesUsdt) FillHeight(min = 12.dp) + FillHeight(min = 12.dp) if (uiState.paykitRateUnavailable) { BodyS( stringResource(R.string.wallet__payment_request_rate_unavailable), @@ -431,7 +427,7 @@ private fun SendAmountNodeRunning( NumberPad( type = NumberPadType.DECIMAL, decimalSeparator = DecimalFormatSymbols.getInstance().decimalSeparator.toString(), - availableHeight = maxHeight * USDT_KEYPAD_HEIGHT_RATIO, + availableHeight = maxHeight, enabled = uiState.isAmountEditable, onPress = { app?.onUsdtAmountChange( @@ -444,7 +440,6 @@ private fun SendAmountNodeRunning( ) }, onDeleteLongPress = { app?.onUsdtAmountChange("") }, - modifier = Modifier.bleedHorizontal(16.dp).fillMaxWidth() ) } else { NumberPad( @@ -457,7 +452,6 @@ private fun SendAmountNodeRunning( ) } - if (uiState.paykitUsesUsdt) VerticalSpacer(16.dp) PrimaryButton( text = stringResource(R.string.common__continue), enabled = uiState.isAmountInputValid && !uiState.isFundingSourceLoading, diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtSendContent.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtSendContent.kt index b8f064d5e4..5a70c1ef00 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtSendContent.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/usdt/UsdtSendContent.kt @@ -91,7 +91,6 @@ import to.bitkit.ui.scaffold.SheetTopBar import to.bitkit.ui.screens.scanner.QrScanningScreen import to.bitkit.ui.screens.wallets.send.HourglassAnimation import to.bitkit.ui.shared.UiConstants -import to.bitkit.ui.shared.modifiers.bleedHorizontal import to.bitkit.ui.shared.modifiers.sheetHeight import to.bitkit.ui.sheets.NewTransactionSheetView import to.bitkit.ui.theme.AppTextStyles @@ -101,9 +100,6 @@ import to.bitkit.ui.utils.NumberPadInputHandler import java.text.DecimalFormatSymbols import kotlin.time.Duration.Companion.seconds -/** Share of the amount step's height the keypad sizes its keys from, so keys shrink before the amount gets squeezed. */ -internal const val USDT_KEYPAD_HEIGHT_RATIO = 0.4f - @Composable @Suppress("CyclomaticComplexMethod") internal fun UsdtPaymentContent( @@ -444,9 +440,8 @@ private fun UsdtAmountContent( BoxWithConstraints(modifier = Modifier.fillMaxSize()) { val maxHeight = this.maxHeight Column(modifier = Modifier.fillMaxWidth().verticalScroll(rememberScrollState()).heightIn(min = maxHeight)) { - FillHeight() UsdtAmountHeader(amount.ifEmpty { "0" }, modifier = Modifier.testTag("UsdtAmount")) - FillHeight() + FillHeight(min = 12.dp) state.error?.let { BodyS( state.errorMessage(it), @@ -516,15 +511,13 @@ private fun UsdtAmountContent( NumberPad( type = NumberPadType.DECIMAL, decimalSeparator = DecimalFormatSymbols.getInstance().decimalSeparator.toString(), - availableHeight = maxHeight * USDT_KEYPAD_HEIGHT_RATIO, + availableHeight = maxHeight, enabled = !state.busy && amountEditable, onDeleteLongPress = { onAmountChange("") }, onPress = { key -> onAmountChange(NumberPadInputHandler.handleInput(key, amount, maxLength = 21, maxDecimals = 6)) }, - modifier = Modifier.bleedHorizontal(16.dp).fillMaxWidth() ) - VerticalSpacer(16.dp) PrimaryButton( text = stringResource(R.string.common__continue), onClick = onReview, diff --git a/app/src/main/java/to/bitkit/ui/screens/widgets/components/WidgetSizeCarousel.kt b/app/src/main/java/to/bitkit/ui/screens/widgets/components/WidgetSizeCarousel.kt index 2db2afc61a..180cd1ede6 100644 --- a/app/src/main/java/to/bitkit/ui/screens/widgets/components/WidgetSizeCarousel.kt +++ b/app/src/main/java/to/bitkit/ui/screens/widgets/components/WidgetSizeCarousel.kt @@ -18,16 +18,17 @@ import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.layout import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import kotlinx.coroutines.flow.drop import to.bitkit.R import to.bitkit.models.WidgetSize import to.bitkit.ui.components.Caption13Up import to.bitkit.ui.components.VerticalSpacer -import to.bitkit.ui.shared.modifiers.bleedHorizontal import to.bitkit.ui.theme.Colors @Composable @@ -133,6 +134,14 @@ fun WidgetSizeCarousel( } } +private fun Modifier.bleedHorizontal(padding: Dp): Modifier = layout { measurable, constraints -> + val bleed = padding.roundToPx() + val placeable = measurable.measure(constraints.copy(maxWidth = constraints.maxWidth + bleed * 2)) + layout(constraints.maxWidth, placeable.height) { + placeable.place(-bleed, 0) + } +} + private fun pageToSize(page: Int, supportsSmall: Boolean): WidgetSize { if (!supportsSmall) return WidgetSize.WIDE return if (page == PAGE_SMALL) WidgetSize.SMALL else WidgetSize.WIDE diff --git a/app/src/main/java/to/bitkit/ui/shared/modifiers/BleedHorizontal.kt b/app/src/main/java/to/bitkit/ui/shared/modifiers/BleedHorizontal.kt deleted file mode 100644 index c4b79afdb6..0000000000 --- a/app/src/main/java/to/bitkit/ui/shared/modifiers/BleedHorizontal.kt +++ /dev/null @@ -1,14 +0,0 @@ -package to.bitkit.ui.shared.modifiers - -import androidx.compose.ui.Modifier -import androidx.compose.ui.layout.layout -import androidx.compose.ui.unit.Dp - -/** Widens the element by [padding] on each side, so it spans the parent's horizontal padding. */ -fun Modifier.bleedHorizontal(padding: Dp): Modifier = layout { measurable, constraints -> - val bleed = padding.roundToPx() - val placeable = measurable.measure(constraints.copy(maxWidth = constraints.maxWidth + bleed * 2)) - layout(constraints.maxWidth, placeable.height) { - placeable.place(-bleed, 0) - } -} diff --git a/changelog.d/next/1627.fixed.md b/changelog.d/next/1627.fixed.md index 18bd54e5ec..6548a033fd 100644 --- a/changelog.d/next/1627.fixed.md +++ b/changelog.d/next/1627.fixed.md @@ -1 +1 @@ -The USDT amount keypad now sits right above Continue, with the amount centred above it. +The USDT amount keypad now sits right above Continue, as on the Bitcoin amount screen. diff --git a/journeys/usdt-send-review-and-details.xml b/journeys/usdt-send-review-and-details.xml index 17365f1f68..71766d93e6 100644 --- a/journeys/usdt-send-review-and-details.xml +++ b/journeys/usdt-send-review-and-details.xml @@ -2,7 +2,7 @@ Use an onboarded wallet with USDT, a fresh BTC/USD rate, an owned recipient and existing sent/received activity. Quotes require the service with MAX support. No payment is submitted. Enable USDT in the introductory sheet or Settings, General, Payments before starting this flow. - Open USDT Send and enter the owned recipient. On Amount, verify the BTC equivalent above the dollar amount, both centred in the free space above Available balance, USDT badge, MAX and network pill. The controls sit right above the keypad, the keypad spans the sheet edge to edge and Continue sits right below it. Enter a six-decimal amount and verify no precision is lost. With a large available balance and BNB Smart Chain selected, verify both values remain fully readable; the controls may wrap onto a second row. + Open USDT Send and enter the owned recipient. On Amount, verify the BTC equivalent above the dollar amount at the top, then the free space, then Available balance, USDT badge, MAX and network pill right above the keypad, and Continue right below the keypad, laid out as on the Bitcoin Amount screen. Enter a six-decimal amount and verify no precision is lost. With a large available balance and BNB Smart Chain selected, verify both values remain fully readable; the controls may wrap onto a second row. Open the network pill and verify the full USDT Network picker, Tether rows and selected teal check. Select another EVM network. Verify the amount and valid recipient remain. Change to Solana with an EVM recipient and verify recipient entry is required. Enter an owned plain Solana address and verify the previous amount remains. Return to Arbitrum, enter the owned EVM address and verify the amount still remains. From recipient entry with another network selected, paste an explicit Arbitrum USDT URI. Verify the selected network becomes Arbitrum and the link amount is retained. A plain 0x address preserves the selected network. Tap MAX. Verify it fills a positive amount below the balance and remains on Amount. Continue and verify the payment plus maximum fee fits within the balance. Back out without swiping.