From cdc78105c708dea877934196b9da6394977e2d11 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 16 Dec 2024 15:59:38 +0100 Subject: [PATCH 01/11] Layout to match the changed design --- .../cashpayment/WooPosCashPaymentScreen.kt | 68 +++++++++++-------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentScreen.kt index 39b9cc03db1..8fe4d05c4b3 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentScreen.kt @@ -2,8 +2,6 @@ package com.woocommerce.android.ui.woopos.cashpayment import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.MaterialTheme import androidx.compose.material.Text @@ -21,6 +19,7 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension @@ -94,10 +93,9 @@ private fun Collecting( onCompleteOrderClicked: () -> Unit, ) { ConstraintLayout( - modifier = Modifier.padding(top = 48.dp.toAdaptivePadding()) - .fillMaxWidth() + modifier = Modifier.fillMaxSize() ) { - val (input, total, changeDue, button) = createRefs() + val (input, total, error, changeDue, button) = createRefs() val focusRequester = remember { FocusRequester() } val keyboardController = LocalSoftwareKeyboardController.current @@ -106,18 +104,26 @@ private fun Collecting( keyboardController?.show() } - val totalBarrier = createStartBarrier(total, changeDue) + Text( + text = state.totalText, + style = MaterialTheme.typography.subtitle1, + modifier = Modifier + .constrainAs(total) { + top.linkTo(parent.top, margin = 4.dp) + start.linkTo(parent.start, margin = 64.dp) + } + ) var inputText by remember { mutableStateOf(state.enteredAmount) } - val standardMargin = 16.dp.toAdaptivePadding() + val marginBetweenTotalAndInput = 48.dp.toAdaptivePadding() WooPosTypedInputField( modifier = Modifier .focusRequester(focusRequester) .constrainAs(input) { - top.linkTo(parent.top) + top.linkTo(total.bottom, margin = marginBetweenTotalAndInput) start.linkTo(parent.start) - end.linkTo(totalBarrier, margin = standardMargin) + end.linkTo(parent.end) width = Dimension.fillToConstraints }, value = inputText, @@ -138,34 +144,36 @@ private fun Collecting( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Decimal ), - errorMessage = state.errorMessage, - ) - - Text( - text = state.totalText, - style = MaterialTheme.typography.h6, - fontWeight = FontWeight.Bold, - modifier = Modifier - .constrainAs(total) { - top.linkTo(input.top) - bottom.linkTo(input.bottom) - end.linkTo(parent.end, margin = standardMargin) - } ) + val smallMargin = 8.dp.toAdaptivePadding() + val standardMargin = 16.dp.toAdaptivePadding() Text( text = state.changeDueText, - style = MaterialTheme.typography.subtitle2, + style = MaterialTheme.typography.body1, color = WooPosTheme.colors.warning, fontWeight = FontWeight.Normal, modifier = Modifier .constrainAs(changeDue) { - bottom.linkTo(input.bottom) + top.linkTo(input.bottom, margin = smallMargin) + start.linkTo(parent.start, margin = standardMargin) end.linkTo(parent.end, margin = standardMargin) } ) - val buttonTopMargin = 48.dp.toAdaptivePadding() + if (state.errorMessage != null) { + Text( + text = state.errorMessage, + color = MaterialTheme.colors.error, + style = MaterialTheme.typography.body1, + textAlign = TextAlign.Center, + modifier = Modifier.constrainAs(error) { + top.linkTo(changeDue.bottom, margin = smallMargin) + start.linkTo(parent.start, margin = standardMargin) + end.linkTo(parent.end, margin = standardMargin) + } + ) + } WooPosButton( text = state.button.text, @@ -177,7 +185,7 @@ private fun Collecting( }, modifier = Modifier .constrainAs(button) { - top.linkTo(changeDue.bottom, margin = buttonTopMargin) + top.linkTo(input.bottom, margin = 96.dp) end.linkTo(parent.end, margin = standardMargin) start.linkTo(parent.start, margin = standardMargin) width = Dimension.fillToConstraints @@ -222,9 +230,9 @@ fun WooPosTotalsPaymentCashWithLabelScreenPreview() { state = WooPosCashPaymentState.Collecting( enteredAmount = null, errorMessage = null, - changeDueText = "5$", + changeDueText = "Change Due 5$", total = BigDecimal(10), - totalText = "10$", + totalText = "Total: 10$", currencySymbol = "$", currencyPosition = WCSettingsModel.CurrencyPosition.LEFT, decimalSeparator = ".", @@ -250,9 +258,9 @@ fun WooPosTotalsPaymentCashWithErrorScreenPreview() { state = WooPosCashPaymentState.Collecting( enteredAmount = BigDecimal(500), errorMessage = "Amount must be more or equal to total", - changeDueText = "5$", + changeDueText = "Change Due 5$", total = BigDecimal(10), - totalText = "10$", + totalText = "Total: 10$", currencySymbol = "$", currencyPosition = WCSettingsModel.CurrencyPosition.LEFT, decimalSeparator = ".", From 012e011feab8a14d5316fbf5ae5305c0fee5c9e2 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 16 Dec 2024 16:54:08 +0100 Subject: [PATCH 02/11] Changes to make "money" input field look like the one from the design --- .../cashpayment/WooPosCashPaymentScreen.kt | 27 +-- .../composeui/component/WooPosInputFields.kt | 182 +++++++++--------- WooCommerce/src/main/res/values/strings.xml | 1 - 3 files changed, 105 insertions(+), 105 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentScreen.kt index 8fe4d05c4b3..94b6b2d164e 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentScreen.kt @@ -25,14 +25,12 @@ import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension import androidx.hilt.navigation.compose.hiltViewModel import com.woocommerce.android.R -import com.woocommerce.android.ui.compose.component.NullableCurrencyTextFieldValueMapper -import com.woocommerce.android.ui.payments.changeduecalculator.CurrencyVisualTransformation import com.woocommerce.android.ui.woopos.common.composeui.WooPosPreview import com.woocommerce.android.ui.woopos.common.composeui.WooPosTheme import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosButton import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosButtonState +import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosMoneyInputField import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosToolbar -import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosTypedInputField import com.woocommerce.android.ui.woopos.common.composeui.toAdaptivePadding import com.woocommerce.android.ui.woopos.root.navigation.WooPosNavigationEvent import org.wordpress.android.fluxc.model.WCSettingsModel @@ -117,37 +115,30 @@ private fun Collecting( var inputText by remember { mutableStateOf(state.enteredAmount) } val marginBetweenTotalAndInput = 48.dp.toAdaptivePadding() - WooPosTypedInputField( + val standardMargin = 16.dp.toAdaptivePadding() + WooPosMoneyInputField( modifier = Modifier .focusRequester(focusRequester) .constrainAs(input) { top.linkTo(total.bottom, margin = marginBetweenTotalAndInput) - start.linkTo(parent.start) - end.linkTo(parent.end) - width = Dimension.fillToConstraints + start.linkTo(parent.start, margin = standardMargin) + end.linkTo(parent.end, margin = standardMargin) }, value = inputText, - label = stringResource(R.string.woopos_cash_payment_enter_amount_label), - valueMapper = NullableCurrencyTextFieldValueMapper.create( - decimalSeparator = state.decimalSeparator, - numberOfDecimals = state.numberOfDecimals - ), onValueChange = { onAmountChanged(it) inputText = it }, - visualTransformation = CurrencyVisualTransformation( - state.currencySymbol, - state.currencyPosition - ), - singleLine = true, keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Decimal ), + currencySymbol = state.currencySymbol, + currencyPosition = state.currencyPosition, + decimalSeparator = state.decimalSeparator, + numberOfDecimals = state.numberOfDecimals, ) val smallMargin = 8.dp.toAdaptivePadding() - val standardMargin = 16.dp.toAdaptivePadding() Text( text = state.changeDueText, style = MaterialTheme.typography.body1, diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt index f790e2e4876..f80d7577805 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt @@ -1,7 +1,6 @@ package com.woocommerce.android.ui.woopos.common.composeui.component import androidx.compose.foundation.background -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.IntrinsicSize @@ -15,11 +14,10 @@ import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.MaterialTheme import androidx.compose.material.Text -import androidx.compose.material.TextFieldColors -import androidx.compose.material.TextFieldDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -28,81 +26,92 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.input.VisualTransformation -import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp -import com.woocommerce.android.ui.compose.component.BigDecimalTextFieldValueMapper -import com.woocommerce.android.ui.compose.component.TextFieldValueMapper -import com.woocommerce.android.ui.compose.component.WCOutlinedTypedTextField +import com.woocommerce.android.ui.compose.component.NullableCurrencyTextFieldValueMapper +import com.woocommerce.android.ui.payments.changeduecalculator.CurrencyVisualTransformation import com.woocommerce.android.ui.woopos.common.composeui.WooPosPreview import com.woocommerce.android.ui.woopos.common.composeui.WooPosTheme -import com.woocommerce.android.ui.woopos.common.composeui.toAdaptivePadding +import com.woocommerce.android.util.WooLog +import com.woocommerce.android.util.WooLog.T +import org.wordpress.android.fluxc.model.WCSettingsModel import java.math.BigDecimal @Composable -fun WooPosTypedInputField( - value: T, - onValueChange: (T) -> Unit, - label: String, - valueMapper: TextFieldValueMapper, - modifier: Modifier = Modifier, - helperText: String? = null, - enabled: Boolean = true, - readOnly: Boolean = false, - leadingIcon: @Composable (() -> Unit)? = null, - trailingIcon: @Composable (() -> Unit)? = null, - errorMessage: String? = null, - visualTransformation: VisualTransformation = VisualTransformation.None, +fun WooPosMoneyInputField( + value: BigDecimal?, + onValueChange: (BigDecimal?) -> Unit, + currencySymbol: String, + currencyPosition: WCSettingsModel.CurrencyPosition, + decimalSeparator: String, + numberOfDecimals: Int, + textStyle: TextStyle = MaterialTheme.typography.h6, + textColor: Color = MaterialTheme.colors.onBackground, keyboardOptions: KeyboardOptions = KeyboardOptions.Default, keyboardActions: KeyboardActions = KeyboardActions.Default, - singleLine: Boolean = true, - maxLines: Int = Int.MAX_VALUE, - interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, - colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors( - focusedBorderColor = Color.Transparent, - unfocusedBorderColor = Color.Transparent, - disabledBorderColor = Color.Transparent, - errorBorderColor = Color.Transparent, - focusedLabelColor = MaterialTheme.colors.onBackground.copy(alpha = 0.8f), - ), - placeholderText: String? = null + contentAlignment: Alignment = Alignment.CenterStart, + modifier: Modifier = Modifier, ) { - Column(modifier = modifier) { - WCOutlinedTypedTextField( - value = value, - onValueChange = onValueChange, - label = label, - valueMapper = valueMapper, - helperText = helperText, - enabled = enabled, - readOnly = readOnly, - leadingIcon = leadingIcon, - trailingIcon = trailingIcon, - isError = errorMessage != null, - visualTransformation = visualTransformation, - keyboardOptions = keyboardOptions, - keyboardActions = keyboardActions, - singleLine = singleLine, - maxLines = maxLines, - interactionSource = interactionSource, - colors = colors, - placeholderText = placeholderText, + val visualTransformation = remember { + CurrencyVisualTransformation( + currencySymbol = currencySymbol, + currencyPosition = currencyPosition ) + } - val errorTextStyle = MaterialTheme.typography.subtitle2 - if (errorMessage != null) { - Text( - text = errorMessage, - color = MaterialTheme.colors.error, - fontWeight = FontWeight.Normal, - style = errorTextStyle, - textAlign = TextAlign.Start, - modifier = Modifier - .padding(start = 16.dp.toAdaptivePadding()) - ) - } + val valueMapper = NullableCurrencyTextFieldValueMapper.create( + decimalSeparator = decimalSeparator, + numberOfDecimals = numberOfDecimals + ) + + var currentValue by remember { + mutableStateOf(value) + } + var textFieldValue by remember(value != currentValue) { + currentValue = value + mutableStateOf(TextFieldValue(valueMapper.printValue(value))) + } + + Box( + modifier = modifier.background(Color.Transparent), + contentAlignment = contentAlignment, + ) { + BasicTextField( + value = textFieldValue, + onValueChange = onValueChange@{ updatedValue -> + if (updatedValue.text == textFieldValue.text) { + textFieldValue = updatedValue + return@onValueChange + } + val transformedText = valueMapper.transformText(textFieldValue.text, updatedValue.text) + runCatching { valueMapper.parseText(transformedText) } + .onSuccess { + textFieldValue = TextFieldValue( + text = transformedText, + composition = updatedValue.composition, + selection = TextRange( + (updatedValue.selection.start + transformedText.length - updatedValue.text.length) + .coerceIn(0, transformedText.length) + ) + ) + if (!valueMapper.equals(currentValue, it)) { + currentValue = it + onValueChange(it) + } + } + .onFailure { + WooLog.e(T.POS, "Failed to parse text: $transformedText", it) + } + }, + textStyle = textStyle.copy(color = textColor), + singleLine = true, + keyboardActions = keyboardActions, + keyboardOptions = keyboardOptions, + visualTransformation = visualTransformation, + cursorBrush = SolidColor(MaterialTheme.colors.onBackground), + ) } } @@ -121,21 +130,17 @@ fun WooPosInputField( var labelWidth by remember { mutableIntStateOf(0) } Box( - modifier = modifier - .background(Color.Transparent), + modifier = modifier.background(Color.Transparent), contentAlignment = contentAlignment, ) { if (value.isEmpty()) { - Text( - text = label, + Text(text = label, style = textStyle.copy(color = textColor.copy(alpha = 0.2f)), maxLines = 1, softWrap = false, - modifier = Modifier - .onGloballyPositioned { coordinates -> - labelWidth = coordinates.size.width - } - ) + modifier = Modifier.onGloballyPositioned { coordinates -> + labelWidth = coordinates.size.width + }) } val density = LocalDensity.current @@ -164,30 +169,35 @@ fun WooPosInputField( fun WooPosTypedInputFieldPreview() { WooPosTheme { Column(modifier = Modifier.padding(16.dp)) { - WooPosTypedInputField( - value = BigDecimal.TEN, + WooPosMoneyInputField( + value = BigDecimal.ZERO, onValueChange = {}, - valueMapper = BigDecimalTextFieldValueMapper.create(true), - label = "Label", + currencySymbol = "$", + currencyPosition = WCSettingsModel.CurrencyPosition.LEFT, + decimalSeparator = ".", + numberOfDecimals = 2, ) Spacer(modifier = Modifier.size(8.dp)) - WooPosTypedInputField( + WooPosMoneyInputField( value = BigDecimal.TEN, onValueChange = {}, - valueMapper = BigDecimalTextFieldValueMapper.create(true), - label = "", + currencySymbol = "$", + currencyPosition = WCSettingsModel.CurrencyPosition.LEFT, + decimalSeparator = ".", + numberOfDecimals = 2, ) Spacer(modifier = Modifier.size(8.dp)) - WooPosTypedInputField( + WooPosMoneyInputField( value = BigDecimal.TEN, - valueMapper = BigDecimalTextFieldValueMapper.create(true), onValueChange = {}, - label = "", - errorMessage = "Please enter a valid amount", + currencySymbol = "$", + currencyPosition = WCSettingsModel.CurrencyPosition.LEFT, + decimalSeparator = ".", + numberOfDecimals = 2, ) } } diff --git a/WooCommerce/src/main/res/values/strings.xml b/WooCommerce/src/main/res/values/strings.xml index 95020e1112a..d3004c51c95 100644 --- a/WooCommerce/src/main/res/values/strings.xml +++ b/WooCommerce/src/main/res/values/strings.xml @@ -4339,7 +4339,6 @@ Cash payment Mark payment as complete - Type amount received Total: %s Change due %s No change due From 7d9d77fce96cf77063c1ac2b1475642ac16287c0 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 16 Dec 2024 17:12:39 +0100 Subject: [PATCH 03/11] Another approach to the label --- .../cashpayment/WooPosCashPaymentScreen.kt | 1 + .../composeui/component/WooPosInputFields.kt | 45 +++++++++++-------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentScreen.kt index 94b6b2d164e..5ccb00b5c78 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentScreen.kt @@ -132,6 +132,7 @@ private fun Collecting( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Decimal ), + textStyle = MaterialTheme.typography.h4, currencySymbol = state.currencySymbol, currencyPosition = state.currencyPosition, decimalSeparator = state.decimalSeparator, diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt index f80d7577805..7d6fa4c31b6 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt @@ -38,6 +38,7 @@ import com.woocommerce.android.util.WooLog import com.woocommerce.android.util.WooLog.T import org.wordpress.android.fluxc.model.WCSettingsModel import java.math.BigDecimal +import kotlin.text.isEmpty @Composable fun WooPosMoneyInputField( @@ -78,6 +79,12 @@ fun WooPosMoneyInputField( modifier = modifier.background(Color.Transparent), contentAlignment = contentAlignment, ) { + val textFieldColor = if (textFieldValue.text.isEmpty()) { + textColor.copy(alpha = 0.2f) + } else { + textColor + } + BasicTextField( value = textFieldValue, onValueChange = onValueChange@{ updatedValue -> @@ -86,31 +93,33 @@ fun WooPosMoneyInputField( return@onValueChange } val transformedText = valueMapper.transformText(textFieldValue.text, updatedValue.text) - runCatching { valueMapper.parseText(transformedText) } - .onSuccess { - textFieldValue = TextFieldValue( - text = transformedText, - composition = updatedValue.composition, - selection = TextRange( - (updatedValue.selection.start + transformedText.length - updatedValue.text.length) - .coerceIn(0, transformedText.length) + runCatching { valueMapper.parseText(transformedText) }.onSuccess { + textFieldValue = TextFieldValue( + text = transformedText, + composition = updatedValue.composition, + selection = TextRange( + (updatedValue.selection.start + transformedText.length - updatedValue.text.length).coerceIn( + 0, + transformedText.length ) ) - if (!valueMapper.equals(currentValue, it)) { - currentValue = it - onValueChange(it) - } - } - .onFailure { - WooLog.e(T.POS, "Failed to parse text: $transformedText", it) + ) + + if (!valueMapper.equals(currentValue, it)) { + currentValue = it + onValueChange(it) } + }.onFailure { + WooLog.e(T.POS, "Failed to parse text: $transformedText", it) + } }, - textStyle = textStyle.copy(color = textColor), + textStyle = textStyle.copy(color = textFieldColor), singleLine = true, keyboardActions = keyboardActions, keyboardOptions = keyboardOptions, visualTransformation = visualTransformation, cursorBrush = SolidColor(MaterialTheme.colors.onBackground), + modifier = Modifier.width(IntrinsicSize.Min), ) } } @@ -170,7 +179,7 @@ fun WooPosTypedInputFieldPreview() { WooPosTheme { Column(modifier = Modifier.padding(16.dp)) { WooPosMoneyInputField( - value = BigDecimal.ZERO, + value = null, onValueChange = {}, currencySymbol = "$", currencyPosition = WCSettingsModel.CurrencyPosition.LEFT, @@ -181,7 +190,7 @@ fun WooPosTypedInputFieldPreview() { Spacer(modifier = Modifier.size(8.dp)) WooPosMoneyInputField( - value = BigDecimal.TEN, + value = BigDecimal.ZERO, onValueChange = {}, currencySymbol = "$", currencyPosition = WCSettingsModel.CurrencyPosition.LEFT, From 47fc116858b1b726f0544afc7fe1e9c2a59de8bf Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 16 Dec 2024 17:13:47 +0100 Subject: [PATCH 04/11] Fixed formatting --- .../woopos/common/composeui/component/WooPosInputFields.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt index 7d6fa4c31b6..1fc2d92652f 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt @@ -143,13 +143,15 @@ fun WooPosInputField( contentAlignment = contentAlignment, ) { if (value.isEmpty()) { - Text(text = label, + Text( + text = label, style = textStyle.copy(color = textColor.copy(alpha = 0.2f)), maxLines = 1, softWrap = false, modifier = Modifier.onGloballyPositioned { coordinates -> labelWidth = coordinates.size.width - }) + } + ) } val density = LocalDensity.current From 8f343fc618d76be736e589c57d0068eae1b719c7 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 17 Dec 2024 09:05:48 +0100 Subject: [PATCH 05/11] Label. Larger totals --- .../cashpayment/WooPosCashPaymentScreen.kt | 2 +- .../composeui/component/WooPosInputFields.kt | 30 +++++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentScreen.kt index 5ccb00b5c78..8b7ca8b6f19 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentScreen.kt @@ -104,7 +104,7 @@ private fun Collecting( Text( text = state.totalText, - style = MaterialTheme.typography.subtitle1, + style = MaterialTheme.typography.h6, modifier = Modifier .constrainAs(total) { top.linkTo(parent.top, margin = 4.dp) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt index 1fc2d92652f..aa35602253b 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.TextFieldValue @@ -75,11 +76,34 @@ fun WooPosMoneyInputField( mutableStateOf(TextFieldValue(valueMapper.printValue(value))) } + var labelWidth by remember { mutableIntStateOf(0) } + Box( modifier = modifier.background(Color.Transparent), contentAlignment = contentAlignment, ) { - val textFieldColor = if (textFieldValue.text.isEmpty()) { + val showLabel = textFieldValue.text.isEmpty() + if (showLabel) { + Text( + text = visualTransformation.filter(AnnotatedString("0.00")).text, + style = textStyle.copy(color = textColor.copy(alpha = 0.2f)), + maxLines = 1, + softWrap = false, + modifier = Modifier.onGloballyPositioned { coordinates -> + labelWidth = coordinates.size.width + } + ) + } + + val density = LocalDensity.current + + val textFieldModifier = if (showLabel) { + Modifier.width(with(density) { labelWidth.toDp() + 4.dp }) + } else { + Modifier.width(IntrinsicSize.Min) + } + + val textFieldColor = if (showLabel) { textColor.copy(alpha = 0.2f) } else { textColor @@ -119,7 +143,7 @@ fun WooPosMoneyInputField( keyboardOptions = keyboardOptions, visualTransformation = visualTransformation, cursorBrush = SolidColor(MaterialTheme.colors.onBackground), - modifier = Modifier.width(IntrinsicSize.Min), + modifier = textFieldModifier, ) } } @@ -177,7 +201,7 @@ fun WooPosInputField( @WooPosPreview @Composable -fun WooPosTypedInputFieldPreview() { +fun WooPosMoneyInputFieldPreview() { WooPosTheme { Column(modifier = Modifier.padding(16.dp)) { WooPosMoneyInputField( From 094e90e52e5a8a092747f4e625b63069bd2b5225 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 17 Dec 2024 09:08:58 +0100 Subject: [PATCH 06/11] Removed no change due --- .../android/ui/woopos/cashpayment/WooPosCashPaymentViewModel.kt | 2 +- WooCommerce/src/main/res/values/strings.xml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentViewModel.kt index 76166b3e1e9..99d3b7bd7b8 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentViewModel.kt @@ -74,7 +74,7 @@ class WooPosCashPaymentViewModel @Inject constructor( priceFormat(changeDue) ) } else { - resourceProvider.getString(R.string.woopos_cash_payment_no_chang_due) + "" } _state.value = currentState.copy( diff --git a/WooCommerce/src/main/res/values/strings.xml b/WooCommerce/src/main/res/values/strings.xml index d3004c51c95..85d27ca9e15 100644 --- a/WooCommerce/src/main/res/values/strings.xml +++ b/WooCommerce/src/main/res/values/strings.xml @@ -4341,7 +4341,6 @@ Mark payment as complete Total: %s Change due %s - No change due Error trying to process payment. Try again. Send From 66c2c2aed753d61b1bc282d61b95fabdbe0a0874 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 17 Dec 2024 09:26:40 +0100 Subject: [PATCH 07/11] Do not show currency symbol when empty --- .../composeui/component/WooPosInputFields.kt | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt index aa35602253b..3ca524d5b9e 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt @@ -30,6 +30,7 @@ import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.unit.dp import com.woocommerce.android.ui.compose.component.NullableCurrencyTextFieldValueMapper import com.woocommerce.android.ui.payments.changeduecalculator.CurrencyVisualTransformation @@ -63,6 +64,13 @@ fun WooPosMoneyInputField( ) } + val visualTransformationWithoutCurrency = remember { + CurrencyVisualTransformation( + currencySymbol = "", + currencyPosition = currencyPosition + ) + } + val valueMapper = NullableCurrencyTextFieldValueMapper.create( decimalSeparator = decimalSeparator, numberOfDecimals = numberOfDecimals @@ -85,7 +93,7 @@ fun WooPosMoneyInputField( val showLabel = textFieldValue.text.isEmpty() if (showLabel) { Text( - text = visualTransformation.filter(AnnotatedString("0.00")).text, + text = visualTransformation.filter(AnnotatedString("0.00")).text.toString(), style = textStyle.copy(color = textColor.copy(alpha = 0.2f)), maxLines = 1, softWrap = false, @@ -141,7 +149,13 @@ fun WooPosMoneyInputField( singleLine = true, keyboardActions = keyboardActions, keyboardOptions = keyboardOptions, - visualTransformation = visualTransformation, + visualTransformation = VisualTransformation { + if (it.text.isEmpty()) { + visualTransformationWithoutCurrency.filter(it) + } else { + visualTransformation.filter(it) + } + }, cursorBrush = SolidColor(MaterialTheme.colors.onBackground), modifier = textFieldModifier, ) From 417321c4f76d947b245e18ccf5f764ccab919516 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 17 Dec 2024 09:33:15 +0100 Subject: [PATCH 08/11] Moved modified to be the first optional parameter --- .../ui/woopos/common/composeui/component/WooPosInputFields.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt index 3ca524d5b9e..132f78a950a 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt @@ -52,10 +52,10 @@ fun WooPosMoneyInputField( numberOfDecimals: Int, textStyle: TextStyle = MaterialTheme.typography.h6, textColor: Color = MaterialTheme.colors.onBackground, + modifier: Modifier = Modifier, keyboardOptions: KeyboardOptions = KeyboardOptions.Default, keyboardActions: KeyboardActions = KeyboardActions.Default, contentAlignment: Alignment = Alignment.CenterStart, - modifier: Modifier = Modifier, ) { val visualTransformation = remember { CurrencyVisualTransformation( @@ -167,12 +167,12 @@ fun WooPosInputField( value: String, onValueChange: (String) -> Unit, label: String = "", + modifier: Modifier = Modifier, textStyle: TextStyle = MaterialTheme.typography.h6, textColor: Color = MaterialTheme.colors.onBackground, keyboardOptions: KeyboardOptions = KeyboardOptions.Default, keyboardActions: KeyboardActions = KeyboardActions.Default, contentAlignment: Alignment = Alignment.CenterStart, - modifier: Modifier = Modifier, ) { var labelWidth by remember { mutableIntStateOf(0) } From b1269dd2f036fe8a9ae64bf2a851d5b5c07e7f05 Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 17 Dec 2024 15:28:21 +0100 Subject: [PATCH 09/11] Do not fetch the order if state stored in a bundle --- .../ui/woopos/cashpayment/WooPosCashPaymentViewModel.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentViewModel.kt index 99d3b7bd7b8..c7a08735306 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentViewModel.kt @@ -25,13 +25,16 @@ class WooPosCashPaymentViewModel @Inject constructor( private val _state = savedState.getStateFlow( scope = viewModelScope, initialValue = WooPosCashPaymentState.Initiating, - key = "woo_pos_cash_payment_state" + key = WOO_POS_CASH_PAYMENT_STATE_KEY ) val state: StateFlow = _state init { viewModelScope.launch { + val savedStateValue = savedState.get(WOO_POS_CASH_PAYMENT_STATE_KEY) + if (savedStateValue != null && savedStateValue != WooPosCashPaymentState.Initiating) return@launch + val order = repository.getOrderById(orderId)!! _state.value = WooPosCashPaymentState.Collecting( enteredAmount = null, @@ -115,4 +118,8 @@ class WooPosCashPaymentViewModel @Inject constructor( } } } + + private companion object { + const val WOO_POS_CASH_PAYMENT_STATE_KEY = "woo_pos_cash_payment_state" + } } From f9021eb76ed9628f199029a2a14a9c8fec23393e Mon Sep 17 00:00:00 2001 From: Andrey Date: Tue, 17 Dec 2024 15:40:16 +0100 Subject: [PATCH 10/11] Save textFieldValue in a bundle --- .../woopos/common/composeui/component/WooPosInputFields.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt index 132f78a950a..590e9d3289a 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/common/composeui/component/WooPosInputFields.kt @@ -19,6 +19,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -79,7 +80,10 @@ fun WooPosMoneyInputField( var currentValue by remember { mutableStateOf(value) } - var textFieldValue by remember(value != currentValue) { + var textFieldValue by rememberSaveable( + value != currentValue, + stateSaver = TextFieldValue.Saver, + ) { currentValue = value mutableStateOf(TextFieldValue(valueMapper.printValue(value))) } From b7b691f4c4e64589e8c10bdbf8247278a12195e9 Mon Sep 17 00:00:00 2001 From: Andrey Date: Wed, 18 Dec 2024 09:31:14 +0100 Subject: [PATCH 11/11] Fixed unit tests --- .../woopos/cashpayment/WooPosCashPaymentViewModelTest.kt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentViewModelTest.kt index 8f2296be282..92387042644 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/cashpayment/WooPosCashPaymentViewModelTest.kt @@ -57,8 +57,6 @@ class WooPosCashPaymentViewModelTest { .thenReturn("Complete Order") whenever(resourceProvider.getString(R.string.woopos_cash_payment_change_due, "20.00")) .thenReturn("Change Due: $20.00") - whenever(resourceProvider.getString(R.string.woopos_cash_payment_no_chang_due)) - .thenReturn("No Change Due") val savedStateHandle = SavedStateHandle(mapOf("orderId" to orderId)) @@ -108,11 +106,9 @@ class WooPosCashPaymentViewModelTest { } @Test - fun `given invalid amount less than total, when onUIEvent AmountChanged, then button is disabled and no change due`() = runTest { + fun `given invalid amount less than total, when onUIEvent AmountChanged, then button is disabled`() = runTest { // GIVEN val enteredAmount = BigDecimal("30.00") - whenever(resourceProvider.getString(R.string.woopos_cash_payment_no_chang_due)) - .thenReturn("No Change Due") // WHEN viewModel.onUIEvent( @@ -124,7 +120,7 @@ class WooPosCashPaymentViewModelTest { assertThat(state).isInstanceOf(WooPosCashPaymentState.Collecting::class.java) val collectingState = state as WooPosCashPaymentState.Collecting assertThat(collectingState.enteredAmount).isEqualTo(enteredAmount) - assertThat(collectingState.changeDueText).isEqualTo("No Change Due") + assertThat(collectingState.changeDueText).isEqualTo("") assertThat(collectingState.button.status).isEqualTo(WooPosCashPaymentState.Collecting.Button.Status.DISABLED) }