summaryrefslogtreecommitdiff
path: root/wallet/src/main/java/net/taler
diff options
context:
space:
mode:
authorIván Ávalos <avalos@disroot.org>2023-09-13 16:57:30 -0600
committerTorsten Grote <t@grobox.de>2023-09-26 18:30:52 +0200
commited7f77234259113007af8cabeed32a560ccd4f32 (patch)
tree1f24f06752d036ecd0041922d2f5e4affc558c9a /wallet/src/main/java/net/taler
parent9837f4ba999b65d611ce2b4d2d2af2d3d437c116 (diff)
downloadtaler-android-ed7f77234259113007af8cabeed32a560ccd4f32.tar.gz
taler-android-ed7f77234259113007af8cabeed32a560ccd4f32.tar.bz2
taler-android-ed7f77234259113007af8cabeed32a560ccd4f32.zip
[wallet] Refactor amount input into single composable
Diffstat (limited to 'wallet/src/main/java/net/taler')
-rw-r--r--wallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt25
-rw-r--r--wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt33
-rw-r--r--wallet/src/main/java/net/taler/wallet/compose/AmountInputField.kt89
-rw-r--r--wallet/src/main/java/net/taler/wallet/deposit/PayToUriFragment.kt21
-rw-r--r--wallet/src/main/java/net/taler/wallet/payment/PayTemplateComposable.kt83
-rw-r--r--wallet/src/main/java/net/taler/wallet/payment/PayTemplateFragment.kt2
6 files changed, 150 insertions, 103 deletions
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