gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-taler-android] 08/14: [wallet] Refactor amount input into single


From: gnunet
Subject: [taler-taler-android] 08/14: [wallet] Refactor amount input into single composable
Date: Tue, 26 Sep 2023 18:31:28 +0200

This is an automated email from the git hooks/post-receive script.

torsten-grote pushed a commit to branch master
in repository taler-android.

commit ed7f77234259113007af8cabeed32a560ccd4f32
Author: Iván Ávalos <avalos@disroot.org>
AuthorDate: Wed Sep 13 16:57:30 2023 -0600

    [wallet] Refactor amount input into single composable
---
 .../src/main/java/net/taler/common/Amount.kt       |  1 +
 .../java/net/taler/wallet/ReceiveFundsFragment.kt  | 25 ++----
 .../java/net/taler/wallet/SendFundsFragment.kt     | 33 ++------
 .../net/taler/wallet/compose/AmountInputField.kt   | 89 ++++++++++++++++++++++
 .../net/taler/wallet/deposit/PayToUriFragment.kt   | 21 ++---
 .../taler/wallet/payment/PayTemplateComposable.kt  | 83 +++++++++-----------
 .../taler/wallet/payment/PayTemplateFragment.kt    |  2 +-
 7 files changed, 151 insertions(+), 103 deletions(-)

diff --git a/taler-kotlin-android/src/main/java/net/taler/common/Amount.kt 
b/taler-kotlin-android/src/main/java/net/taler/common/Amount.kt
index 4861568..5fb36fa 100644
--- a/taler-kotlin-android/src/main/java/net/taler/common/Amount.kt
+++ b/taler-kotlin-android/src/main/java/net/taler/common/Amount.kt
@@ -90,6 +90,7 @@ public data class Amount(
         }
 
         public fun isValidAmountStr(str: String): Boolean {
+            if (str.count { it == '.' } > 1) return false
             val split = str.split(".")
             try {
                 checkValue(split[0].toLongOrNull())
diff --git a/wallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt 
b/wallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt
index dbff6ae..1511128 100644
--- a/wallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt
@@ -29,12 +29,10 @@ import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material3.Button
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.OutlinedTextField
 import androidx.compose.material3.Surface
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
@@ -46,7 +44,6 @@ import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.input.KeyboardType.Companion.Decimal
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
 import androidx.core.os.bundleOf
@@ -55,7 +52,7 @@ import androidx.fragment.app.activityViewModels
 import androidx.lifecycle.lifecycleScope
 import androidx.navigation.fragment.findNavController
 import net.taler.common.Amount
-import net.taler.common.Amount.Companion.isValidAmountStr
+import net.taler.wallet.compose.AmountInputField
 import net.taler.wallet.compose.TalerSurface
 import net.taler.wallet.exchanges.ExchangeItem
 
@@ -134,28 +131,20 @@ private fun ReceiveFundsIntro(
             modifier = Modifier
                 .padding(16.dp),
         ) {
-            OutlinedTextField(
+            AmountInputField(
                 modifier = Modifier
                     .weight(1f)
                     .padding(end = 16.dp),
                 value = text,
-                keyboardOptions = KeyboardOptions.Default.copy(keyboardType = 
Decimal),
                 onValueChange = { input ->
                     isError = false
-                    val filtered = input.filter { it.isDigit() || it == '.' }
-                    if (filtered.endsWith('.') || isValidAmountStr(filtered)) 
text = filtered
+                    text = input
+                },
+                label = { Text(stringResource(R.string.receive_amount)) },
+                supportingText = {
+                    if (isError) 
Text(stringResource(R.string.receive_amount_invalid))
                 },
                 isError = isError,
-                label = {
-                    if (isError) {
-                        Text(
-                            stringResource(R.string.receive_amount_invalid),
-                            color = MaterialTheme.colorScheme.error,
-                        )
-                    } else {
-                        Text(stringResource(R.string.receive_amount))
-                    }
-                }
             )
             Text(
                 modifier = Modifier,
diff --git a/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt 
b/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt
index c2680d5..2e5eb52 100644
--- a/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt
@@ -27,12 +27,10 @@ import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material3.Button
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.OutlinedTextField
 import androidx.compose.material3.Surface
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
@@ -44,7 +42,6 @@ import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
 import androidx.core.os.bundleOf
@@ -52,7 +49,7 @@ import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
 import androidx.navigation.fragment.findNavController
 import net.taler.common.Amount
-import net.taler.common.Amount.Companion.isValidAmountStr
+import net.taler.wallet.compose.AmountInputField
 import net.taler.wallet.compose.TalerSurface
 
 class SendFundsFragment : Fragment() {
@@ -116,34 +113,20 @@ private fun SendFundsIntro(
             modifier = Modifier
                 .padding(16.dp),
         ) {
-            OutlinedTextField(
+            AmountInputField(
                 modifier = Modifier
                     .weight(1f)
                     .padding(end = 16.dp),
                 value = text,
-                keyboardOptions = KeyboardOptions.Default.copy(keyboardType = 
KeyboardType.Decimal),
                 onValueChange = { input ->
                     isError = false
-                    insufficientBalance = false
-                    val filtered = input.filter { it.isDigit() || it == '.' }
-                    if (filtered.endsWith('.') || isValidAmountStr(filtered)) 
text = filtered
+                    text = input
                 },
-                isError = isError || insufficientBalance,
-                label = {
-                    if (isError) {
-                        Text(
-                            stringResource(R.string.receive_amount_invalid),
-                            color = MaterialTheme.colorScheme.error,
-                        )
-                    } else if (insufficientBalance) {
-                        Text(
-                            
stringResource(R.string.payment_balance_insufficient),
-                            color = MaterialTheme.colorScheme.error,
-                        )
-                    } else {
-                        Text(stringResource(R.string.send_amount))
-                    }
-                }
+                label = { Text(stringResource(R.string.send_amount)) },
+                supportingText = {
+                    if (isError) 
Text(stringResource(R.string.receive_amount_invalid))
+                },
+                isError = isError,
             )
             Text(
                 modifier = Modifier,
diff --git a/wallet/src/main/java/net/taler/wallet/compose/AmountInputField.kt 
b/wallet/src/main/java/net/taler/wallet/compose/AmountInputField.kt
new file mode 100644
index 0000000..79a01c8
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/compose/AmountInputField.kt
@@ -0,0 +1,89 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2023 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under 
the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
+ * A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.wallet.compose
+
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.LocalTextStyle
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextFieldColors
+import androidx.compose.material3.TextFieldDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.input.VisualTransformation
+import net.taler.common.Amount
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun AmountInputField(
+    value: String,
+    onValueChange: (value: String) -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    readOnly: Boolean = false,
+    textStyle: TextStyle = LocalTextStyle.current,
+    label: @Composable (() -> Unit)? = null,
+    leadingIcon: @Composable (() -> Unit)? = null,
+    trailingIcon: @Composable (() -> Unit)? = null,
+    supportingText: @Composable (() -> Unit)? = null,
+    isError: Boolean = false,
+    visualTransformation: VisualTransformation = VisualTransformation.None,
+    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
+    keyboardActions: KeyboardActions = KeyboardActions.Default,
+    interactionSource: MutableInteractionSource = remember { 
MutableInteractionSource() },
+    shape: Shape = TextFieldDefaults.outlinedShape,
+    colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors()
+) {
+    OutlinedTextField(
+        value = if (value == "0" || value.endsWith(".0")) value.trimEnd('0') 
else value,
+        onValueChange = { input ->
+            if (input.isNotBlank()) {
+                val filtered = input.filter { it.isDigit() || it == '.' }.let {
+                    if (it == "" || it.endsWith(".")) "${it}0" else it
+                }
+                if (Amount.isValidAmountStr(filtered)) {
+                    onValueChange(filtered)
+                }
+            } else onValueChange("0")
+        },
+        modifier = modifier,
+        enabled = enabled,
+        readOnly = readOnly,
+        textStyle = textStyle,
+        label = label,
+        placeholder = { Text("0") },
+        leadingIcon = leadingIcon,
+        trailingIcon = trailingIcon,
+        supportingText = supportingText,
+        isError = isError,
+        visualTransformation = visualTransformation,
+        keyboardOptions = keyboardOptions.copy(keyboardType = 
KeyboardType.Decimal),
+        keyboardActions = keyboardActions,
+        singleLine = true,
+        maxLines = 1,
+        interactionSource = interactionSource,
+        shape = shape,
+        colors = colors,
+    )
+}
\ No newline at end of file
diff --git a/wallet/src/main/java/net/taler/wallet/deposit/PayToUriFragment.kt 
b/wallet/src/main/java/net/taler/wallet/deposit/PayToUriFragment.kt
index 2584763..d4c9f6c 100644
--- a/wallet/src/main/java/net/taler/wallet/deposit/PayToUriFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/deposit/PayToUriFragment.kt
@@ -30,7 +30,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material3.Button
 import androidx.compose.material3.DropdownMenu
@@ -56,7 +55,6 @@ import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.platform.LocalFocusManager
 import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
 import androidx.core.os.bundleOf
@@ -67,6 +65,7 @@ import net.taler.common.Amount
 import net.taler.wallet.AmountResult
 import net.taler.wallet.MainViewModel
 import net.taler.wallet.R
+import net.taler.wallet.compose.AmountInputField
 import net.taler.wallet.compose.TalerSurface
 
 class PayToUriFragment : Fragment() {
@@ -136,24 +135,18 @@ private fun PayToComposable(
         var amountError by rememberSaveable { mutableStateOf("") }
         var currency by rememberSaveable { mutableStateOf(currencies[0]) }
         val focusRequester = remember { FocusRequester() }
-        OutlinedTextField(
-            modifier = Modifier
-                .focusRequester(focusRequester),
+        AmountInputField(
+            modifier = Modifier.focusRequester(focusRequester),
             value = amountText,
             onValueChange = { input ->
                 amountError = ""
                 amountText = input
             },
-            keyboardOptions = KeyboardOptions.Default.copy(keyboardType = 
KeyboardType.Decimal),
-            singleLine = true,
+            label = { Text(stringResource(R.string.send_amount)) },
+            supportingText = {
+                if (amountError.isNotBlank()) Text(amountError)
+            },
             isError = amountError.isNotBlank(),
-            label = {
-                if (amountError.isBlank()) {
-                    Text(stringResource(R.string.send_amount))
-                } else {
-                    Text(amountError, color = MaterialTheme.colorScheme.error)
-                }
-            }
         )
         CurrencyDropdown(
             modifier = Modifier
diff --git 
a/wallet/src/main/java/net/taler/wallet/payment/PayTemplateComposable.kt 
b/wallet/src/main/java/net/taler/wallet/payment/PayTemplateComposable.kt
index 8bbff03..59a088d 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/PayTemplateComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/PayTemplateComposable.kt
@@ -22,7 +22,6 @@ import androidx.compose.foundation.layout.Row
 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.material3.Button
 import androidx.compose.material3.CircularProgressIndicator
 import androidx.compose.material3.ExperimentalMaterial3Api
@@ -38,12 +37,12 @@ import androidx.compose.ui.Alignment.Companion.Center
 import androidx.compose.ui.Alignment.Companion.End
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
 import net.taler.common.Amount
 import net.taler.wallet.AmountResult
 import net.taler.wallet.R
+import net.taler.wallet.compose.AmountInputField
 import net.taler.wallet.compose.TalerSurface
 import net.taler.wallet.deposit.CurrencyDropdown
 
@@ -104,11 +103,14 @@ fun PayTemplateDefault(
     onError: (msgRes: Int) -> Unit,
     onSubmit: (summary: String?, amount: Amount?) -> Unit,
 ) {
+    val amountDefault = amountStatus as? AmountFieldStatus.Default
+
     var localSummary by remember { mutableStateOf(summary) }
     var localAmount by remember { mutableStateOf(
-        (amountStatus as? AmountFieldStatus.Default)?.let { s ->
-            Amount.fromString(s.currency ?: currencies[0], s.amountStr ?: "0")
-        }
+        amountDefault?.let { s -> s.amountStr ?: "0" }
+    ) }
+    var localCurrency by remember { mutableStateOf(
+        amountDefault?.let { s -> s.currency ?: currencies[0] }
     ) }
 
     Column(horizontalAlignment = End) {
@@ -126,15 +128,21 @@ fun PayTemplateDefault(
         }
 
         localAmount?.let { amount ->
-            AmountField(
-                modifier = Modifier
-                    .padding(16.dp)
-                    .fillMaxWidth(),
-                amount = amount,
-                currencies = currencies,
-                fixedCurrency = (amountStatus as? 
AmountFieldStatus.Default)?.currency != null,
-                onAmountChosen = { localAmount = it },
-            )
+            localCurrency?.let { currency ->
+                AmountField(
+                    modifier = Modifier
+                        .padding(16.dp)
+                        .fillMaxWidth(),
+                    amount = amount,
+                    currency = currency,
+                    currencies = currencies,
+                    fixedCurrency = (amountStatus as? 
AmountFieldStatus.Default)?.currency != null,
+                    onAmountChosen = { a, c ->
+                        localAmount = a
+                        localCurrency = c
+                    },
+                )
+            }
         }
 
         Button(
@@ -142,14 +150,12 @@ fun PayTemplateDefault(
             enabled = localSummary == null || localSummary!!.isNotBlank(),
             onClick = {
                 localAmount?.let { amount ->
-                    val result = onCreateAmount(
-                        amount.amountStr,
-                        amount.currency,
-                    )
-                    when (result) {
-                        AmountResult.InsufficientBalance -> 
onError(R.string.payment_balance_insufficient)
-                        AmountResult.InvalidAmount -> 
onError(R.string.receive_amount_invalid)
-                        else -> onSubmit(summary, amount)
+                    localCurrency?.let { currency ->
+                        when (val res = onCreateAmount(amount, currency)) {
+                            is AmountResult.InsufficientBalance -> 
onError(R.string.payment_balance_insufficient)
+                            is AmountResult.InvalidAmount -> 
onError(R.string.receive_amount_invalid)
+                            is AmountResult.Success -> onSubmit(summary, 
res.amount)
+                        }
                     }
                 }
             },
@@ -184,43 +190,30 @@ fun PayTemplateLoading() {
 }
 
 @Composable
-@OptIn(ExperimentalMaterial3Api::class)
-// TODO can we combine this with existing amount composables, e.g. whats in 
PayToComposable?
 private fun AmountField(
     modifier: Modifier = Modifier,
     currencies: List<String>,
     fixedCurrency: Boolean,
-    amount: Amount,
-    onAmountChosen: (Amount) -> Unit,
+    amount: String,
+    currency: String,
+    onAmountChosen: (amount: String, currency: String) -> Unit,
 ) {
     Row(
         modifier = modifier,
     ) {
-        val amountText = if (amount.value == 0L) "" else 
amount.value.toString()
-        OutlinedTextField(
+        AmountInputField(
             modifier = Modifier
                 .padding(end = 16.dp)
                 .weight(1f),
-            value = amountText,
-            placeholder = { Text("0") },
-            onValueChange = { input ->
-                if (input.isNotBlank()) {
-                    onAmountChosen(Amount.fromString(amount.currency, input))
-                } else {
-                    onAmountChosen(Amount.zero(amount.currency))
-                }
-            },
-            keyboardOptions = KeyboardOptions.Default.copy(keyboardType = 
KeyboardType.Decimal),
-            singleLine = true,
-            label = { Text(stringResource(R.string.send_amount)) },
+            value = amount,
+            onValueChange = { onAmountChosen(it, currency) },
+            label = { Text(stringResource(R.string.send_amount)) }
         )
         CurrencyDropdown(
             modifier = Modifier.weight(1f),
-            initialCurrency = amount.currency,
+            initialCurrency = currency,
             currencies = currencies,
-            onCurrencyChanged = { c ->
-                onAmountChosen(Amount.fromString(c, amount.amountStr))
-            },
+            onCurrencyChanged = { onAmountChosen(amount, it) },
             readOnly = fixedCurrency,
         )
     }
@@ -232,7 +225,7 @@ fun PayTemplateComposablePreview() {
     TalerSurface {
         PayTemplateComposable(
             summary = "Donation",
-            amountStatus = AmountFieldStatus.Default("ARS",  "20"),
+            amountStatus = AmountFieldStatus.Default("20",  "ARS"),
             currencies = listOf("KUDOS", "ARS"),
             // TODO create previews for other states
             payStatus = PayStatus.None,
diff --git 
a/wallet/src/main/java/net/taler/wallet/payment/PayTemplateFragment.kt 
b/wallet/src/main/java/net/taler/wallet/payment/PayTemplateFragment.kt
index c4b8132..01160ec 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/PayTemplateFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/PayTemplateFragment.kt
@@ -60,7 +60,7 @@ class PayTemplateFragment : Fragment() {
             when (parts.size) {
                 0 -> AmountFieldStatus.Default()
                 1 -> AmountFieldStatus.Default(currency = parts[0])
-                2 -> AmountFieldStatus.Default(parts[0], parts[1])
+                2 -> AmountFieldStatus.Default(parts[1], parts[0])
                 else -> AmountFieldStatus.Invalid
             }
         } else AmountFieldStatus.FixedAmount

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]