gnunet-svn
[Top][All Lists]
Advanced

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

[taler-taler-android] 02/14: [wallet] Improved templates UX and PoS conf


From: gnunet
Subject: [taler-taler-android] 02/14: [wallet] Improved templates UX and PoS confirmation codes
Date: Tue, 26 Sep 2023 18:31:22 +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 b38d99fd60737a4087948bd3e4ee6b18d756c639
Author: Iván Ávalos <avalos@disroot.org>
AuthorDate: Wed Aug 16 16:03:45 2023 +0200

    [wallet] Improved templates UX and PoS confirmation codes
---
 .../taler/wallet/payment/PayTemplateFragment.kt    | 152 ++++++++++++++-------
 .../net/taler/wallet/payment/PaymentManager.kt     |   1 +
 .../wallet/payment/TransactionPaymentComposable.kt |   6 +
 .../net/taler/wallet/transactions/Transactions.kt  |   1 +
 wallet/src/main/res/values/strings.xml             |   1 +
 5 files changed, 111 insertions(+), 50 deletions(-)

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 ab6dada..633ab20 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/PayTemplateFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/PayTemplateFragment.kt
@@ -21,16 +21,21 @@ import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 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
+import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.OutlinedTextField
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
@@ -43,6 +48,8 @@ import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.unit.dp
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.asFlow
+import androidx.navigation.NavOptions
 import androidx.navigation.fragment.findNavController
 import net.taler.common.Amount
 import net.taler.common.AmountParserException
@@ -84,15 +91,22 @@ class PayTemplateFragment: Fragment() {
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
+        // TODO: this is not ideal, if the template is fixed, the
+        //  user shouldn't even have to go through this fragment.
         if (uri?.queryParameterNames?.isEmpty() == true) {
             createOrder(emptyMap())
         }
     }
 
-    fun createOrder(params: Map<String, String>) {
-        uriString?.let {
-            model.paymentManager.preparePayForTemplate(it, 
params,).invokeOnCompletion {
-                findNavController().navigate(R.id.action_global_promptPayment)
+    private fun createOrder(params: Map<String, String>) {
+        uriString ?: return
+        model.paymentManager.preparePayForTemplate(uriString!!, 
params,).invokeOnCompletion {
+            if (model.paymentManager.payStatus.value is PayStatus.Prepared) {
+                val navOptions = NavOptions.Builder()
+                    .setPopUpTo(R.id.nav_main, true)
+                    .build()
+                findNavController()
+                    .navigate(R.id.action_global_promptPayment, null, 
navOptions)
             }
         }
     }
@@ -126,61 +140,99 @@ fun PayTemplateComposable(
         } else null,
     ) }
 
-    Column(horizontalAlignment = Alignment.End) {
-        if ("summary" in queryParams) {
-            OutlinedTextField(
-                modifier = Modifier
-                    .padding(horizontal = 16.dp)
-                    .fillMaxWidth(),
-                value = summary!!,
-                isError = summary!!.isBlank(),
-                onValueChange = { summary = it },
-                singleLine = true,
-                label = { 
Text(stringResource(R.string.withdraw_manual_ready_subject)) },
-            )
-        }
+    val payStatus by model.paymentManager.payStatus.asFlow()
+        .collectAsState(initial = PayStatus.None)
 
-        if ("amount" in queryParams) {
-            AmountField(
-                modifier = Modifier
-                    .padding(16.dp)
-                    .fillMaxWidth(),
-                amount = amount!!,
-                currencies = currencies,
-                onAmountChosen = { amount = it },
+    // If wallet is empty, there's no way the user can pay something
+    if (payStatus is PayStatus.InsufficientBalance || currencies.isEmpty()) {
+        Box(
+            modifier = Modifier.fillMaxSize(),
+            contentAlignment = Alignment.Center,
+        ) {
+            Text(
+                stringResource(R.string.payment_balance_insufficient),
+                style = MaterialTheme.typography.titleLarge,
+                color = MaterialTheme.colorScheme.error,
             )
         }
+    } else when (payStatus) {
+        is PayStatus.None -> {
+            Column(horizontalAlignment = Alignment.End) {
+                if ("summary" in queryParams) {
+                    OutlinedTextField(
+                        modifier = Modifier
+                            .padding(horizontal = 16.dp)
+                            .fillMaxWidth(),
+                        value = summary!!,
+                        isError = summary!!.isBlank(),
+                        onValueChange = { summary = it },
+                        singleLine = true,
+                        label = { 
Text(stringResource(R.string.withdraw_manual_ready_subject)) },
+                    )
+                }
 
-        Button(
-            modifier = Modifier.padding(16.dp),
-            enabled = summary == null || summary!!.isNotBlank(),
-            onClick = {
-                if (amount != null) {
-                    val result = model.createAmount(
-                        amount!!.amountStr,
-                        amount!!.currency,
+                if ("amount" in queryParams) {
+                    AmountField(
+                        modifier = Modifier
+                            .padding(16.dp)
+                            .fillMaxWidth(),
+                        amount = amount!!,
+                        currencies = currencies,
+                        onAmountChosen = { amount = it },
                     )
-                    when (result) {
-                        AmountResult.InsufficientBalance -> {
-                            
fragment.showError(R.string.payment_balance_insufficient)
-                        }
-                        AmountResult.InvalidAmount -> {
-                            fragment.showError(R.string.receive_amount_invalid)
-                        }
-                        else -> {
-                            onSubmit(
-                                mutableMapOf<String, String>().apply {
-                                    if (summary != null) put("summary", 
summary!!)
-                                    if (amount != null) put("amount", 
amount!!.toJSONString())
-                                }
+                }
+
+                Button(
+                    modifier = Modifier.padding(16.dp),
+                    enabled = summary == null || summary!!.isNotBlank(),
+                    onClick = {
+                        if (amount != null) {
+                            val result = model.createAmount(
+                                amount!!.amountStr,
+                                amount!!.currency,
                             )
+                            when (result) {
+                                AmountResult.InsufficientBalance -> {
+                                    
fragment.showError(R.string.payment_balance_insufficient)
+                                }
+                                AmountResult.InvalidAmount -> {
+                                    
fragment.showError(R.string.receive_amount_invalid)
+                                }
+                                else -> {
+                                    onSubmit(
+                                        mutableMapOf<String, String>().apply {
+                                            if (summary != null) 
put("summary", summary!!)
+                                            if (amount != null) put("amount", 
amount!!.toJSONString())
+                                        }
+                                    )
+                                }
+                            }
                         }
-                    }
+                    },
+                ) {
+                    Text(stringResource(R.string.payment_create_order))
                 }
-            },
-        ) {
-            Text(stringResource(R.string.payment_create_order))
+            }
+        }
+        is PayStatus.Loading -> {
+            Box(
+                modifier = Modifier.fillMaxSize(),
+                contentAlignment = Alignment.Center,
+            ) { CircularProgressIndicator() }
+        }
+        is PayStatus.AlreadyPaid -> {
+            Box(
+                modifier = Modifier.fillMaxSize(),
+                contentAlignment = Alignment.Center,
+            ) {
+                Text(
+                    stringResource(R.string.payment_already_paid),
+                    style = MaterialTheme.typography.titleLarge,
+                    color = MaterialTheme.colorScheme.error,
+                )
+            }
         }
+        else -> {}
     }
 }
 
diff --git a/wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt 
b/wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt
index 538f2e1..c98e0b2 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt
@@ -104,6 +104,7 @@ class PaymentManager(
     }
 
     fun preparePayForTemplate(url: String, params: Map<String, String>) = 
scope.launch {
+        mPayStatus.value = PayStatus.Loading
         api.request("preparePayForTemplate", PreparePayResponse.serializer()) {
             put("talerPayTemplateUri", url)
             put("templateParams", JSONObject().apply {
diff --git 
a/wallet/src/main/java/net/taler/wallet/payment/TransactionPaymentComposable.kt 
b/wallet/src/main/java/net/taler/wallet/payment/TransactionPaymentComposable.kt
index e6a65d1..c08bc76 100644
--- 
a/wallet/src/main/java/net/taler/wallet/payment/TransactionPaymentComposable.kt
+++ 
b/wallet/src/main/java/net/taler/wallet/payment/TransactionPaymentComposable.kt
@@ -88,6 +88,12 @@ fun TransactionPaymentComposable(
             amount = t.amountEffective - t.amountRaw,
             amountType = AmountType.Negative,
         )
+        if (t.posConfirmation != null) {
+            TransactionInfoComposable(
+                label = stringResource(id = 
R.string.payment_confirmation_code),
+                info = t.posConfirmation,
+            )
+        }
         PurchaseDetails(info = t.info) {
             onFulfill(t.info.fulfillmentUrl ?: "")
         }
diff --git a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt 
b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
index c6be73a..536d433 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
@@ -218,6 +218,7 @@ class TransactionPayment(
     override val error: TalerErrorInfo? = null,
     override val amountRaw: Amount,
     override val amountEffective: Amount,
+    val posConfirmation: String? = null,
 ) : Transaction() {
     override val icon = R.drawable.ic_cash_usd_outline
     override val detailPageNav = R.id.action_nav_transactions_detail_payment
diff --git a/wallet/src/main/res/values/strings.xml 
b/wallet/src/main/res/values/strings.xml
index 824c922..6fa1227 100644
--- a/wallet/src/main/res/values/strings.xml
+++ b/wallet/src/main/res/values/strings.xml
@@ -133,6 +133,7 @@ GNU Taler is immune against many types of fraud, such as 
phishing of credit card
     <string name="payment_already_paid">You\'ve already paid for this 
purchase.</string>
     <string name="payment_pay_template_title">Customize your order</string>
     <string name="payment_create_order">Create order</string>
+    <string name="payment_confirmation_code">Confirmation code</string>
 
     <string name="receive_amount">Amount to receive</string>
     <string name="receive_amount_invalid">Amount invalid</string>

-- 
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]