gnunet-svn
[Top][All Lists]
Advanced

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

[taler-taler-android] branch master updated (5671315 -> c522bcc)


From: gnunet
Subject: [taler-taler-android] branch master updated (5671315 -> c522bcc)
Date: Tue, 14 Feb 2023 20:30:22 +0100

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

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

    from 5671315  [wallet] Update libraries after release
     new c8de14a  [wallet] Convert multiline text into a single line when 
pasting into subject field
     new c0d94e7  [wallet] Add Confirm with Bank button to transactions list
     new 23dd3cd  [wallet] Added serializer for unknown fields in 
TalerErrorInfo.
     new 2731915  [wallet] Add support for ext+taler:// URI
     new c522bcc  [wallet] Small Material 3 style fixes

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 wallet/src/main/AndroidManifest.xml                |  4 ++
 .../src/main/java/net/taler/wallet/MainActivity.kt | 23 +++++---
 .../net/taler/wallet/backend/WalletBackendApi.kt   |  2 +-
 .../net/taler/wallet/backend/WalletResponse.kt     | 63 ++++++++++++----------
 .../wallet/peer/OutgoingPullIntroComposable.kt     |  2 +-
 .../wallet/peer/OutgoingPullResultComposable.kt    |  4 +-
 .../wallet/peer/OutgoingPushIntroComposable.kt     |  2 +-
 .../wallet/peer/OutgoingPushResultComposable.kt    |  4 +-
 .../wallet/transactions/TransactionAdapter.kt      | 20 +++++--
 .../transactions/TransactionWithdrawalFragment.kt  |  2 +-
 .../wallet/transactions/TransactionsFragment.kt    | 13 +++--
 .../res/layout/fragment_transaction_payment.xml    |  8 +--
 wallet/src/main/res/layout/fragment_uri_input.xml  |  8 +--
 .../src/main/res/layout/list_item_transaction.xml  |  6 ++-
 14 files changed, 103 insertions(+), 58 deletions(-)

diff --git a/wallet/src/main/AndroidManifest.xml 
b/wallet/src/main/AndroidManifest.xml
index 01e82a6..c40b87e 100644
--- a/wallet/src/main/AndroidManifest.xml
+++ b/wallet/src/main/AndroidManifest.xml
@@ -65,6 +65,10 @@
                 <data
                     android:scheme="TALER"
                     tools:ignore="AppLinkUrlError" />
+                <data android:scheme="ext+taler" />
+                <data
+                    android:scheme="EXT+TALER"
+                    tools:ignore="AppLinkUrlError" />
                 <data android:scheme="payto" />
             </intent-filter>
         </activity>
diff --git a/wallet/src/main/java/net/taler/wallet/MainActivity.kt 
b/wallet/src/main/java/net/taler/wallet/MainActivity.kt
index c3fc433..fcc9dee 100644
--- a/wallet/src/main/java/net/taler/wallet/MainActivity.kt
+++ b/wallet/src/main/java/net/taler/wallet/MainActivity.kt
@@ -235,9 +235,13 @@ class MainActivity : AppCompatActivity(), 
OnNavigationItemSelectedListener,
             }
 
             val normalizedURL = u.lowercase(ROOT)
+            var ext = false
             val action = normalizedURL.substring(
                 if (normalizedURL.startsWith("taler://", ignoreCase = true)) {
                     "taler://".length
+                } else if (normalizedURL.startsWith("ext+taler://", ignoreCase 
= true)) {
+                    ext = true
+                    "ext+taler://".length
                 } else if (normalizedURL.startsWith("taler+http://";, 
ignoreCase = true) &&
                     model.devMode.value == true
                 ) {
@@ -247,37 +251,42 @@ class MainActivity : AppCompatActivity(), 
OnNavigationItemSelectedListener,
                 }
             )
 
+            // Remove ext+ scheme prefix if present
+            val u2 = if (ext) {
+                "taler://" + u.substring("ext+taler://".length)
+            } else u
+
             when {
                 action.startsWith("pay/", ignoreCase = true) -> {
                     Log.v(TAG, "navigating!")
                     nav.navigate(R.id.action_global_promptPayment)
-                    model.paymentManager.preparePay(u)
+                    model.paymentManager.preparePay(u2)
                 }
                 action.startsWith("tip/", ignoreCase = true) -> {
                     Log.v(TAG, "navigating!")
                     nav.navigate(R.id.action_global_promptTip)
-                    model.tipManager.prepareTip(u)
+                    model.tipManager.prepareTip(u2)
                 }
                 action.startsWith("withdraw/", ignoreCase = true) -> {
                     Log.v(TAG, "navigating!")
                     // there's more than one entry point, so use global action
                     nav.navigate(R.id.action_global_promptWithdraw)
-                    model.withdrawManager.getWithdrawalDetails(u)
+                    model.withdrawManager.getWithdrawalDetails(u2)
                 }
                 action.startsWith("refund/", ignoreCase = true) -> {
                     model.showProgressBar.value = true
-                    model.refundManager.refund(u).observe(this, 
Observer(::onRefundResponse))
+                    model.refundManager.refund(u2).observe(this, 
Observer(::onRefundResponse))
                 }
                 action.startsWith("pay-pull/", ignoreCase = true) -> {
                     nav.navigate(R.id.action_global_prompt_pull_payment)
-                    model.peerManager.checkPeerPullPayment(u)
+                    model.peerManager.checkPeerPullPayment(u2)
                 }
                 action.startsWith("pay-push/", ignoreCase = true) -> {
                     nav.navigate(R.id.action_global_prompt_push_payment)
-                    model.peerManager.checkPeerPushPayment(u)
+                    model.peerManager.checkPeerPushPayment(u2)
                 }
                 else -> {
-                    showError(R.string.error_unsupported_uri, "From: 
$from\nURI: $u")
+                    showError(R.string.error_unsupported_uri, "From: 
$from\nURI: $u2")
                 }
             }
         }
diff --git a/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt 
b/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt
index 9fc5cac..8ec5873 100644
--- a/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt
+++ b/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt
@@ -165,7 +165,7 @@ class WalletBackendApi(
                         WalletResponse.Success(t)
                     }
                 } catch (e: Exception) {
-                    val info = TalerErrorInfo(NONE, "", e.toString(), null)
+                    val info = TalerErrorInfo(NONE, "", e.toString())
                     WalletResponse.Error(info)
                 }
                 cont.resume(response)
diff --git a/wallet/src/main/java/net/taler/wallet/backend/WalletResponse.kt 
b/wallet/src/main/java/net/taler/wallet/backend/WalletResponse.kt
index 414d784..22dcba9 100644
--- a/wallet/src/main/java/net/taler/wallet/backend/WalletResponse.kt
+++ b/wallet/src/main/java/net/taler/wallet/backend/WalletResponse.kt
@@ -19,13 +19,14 @@ package net.taler.wallet.backend
 import kotlinx.serialization.KSerializer
 import kotlinx.serialization.SerialName
 import kotlinx.serialization.Serializable
-import kotlinx.serialization.descriptors.PrimitiveKind.STRING
-import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
+import kotlinx.serialization.builtins.MapSerializer
+import kotlinx.serialization.builtins.serializer
+import kotlinx.serialization.descriptors.SerialDescriptor
 import kotlinx.serialization.encoding.Decoder
 import kotlinx.serialization.encoding.Encoder
 import kotlinx.serialization.json.JsonDecoder
-import kotlinx.serialization.json.JsonObject
-import org.json.JSONObject
+import kotlinx.serialization.json.JsonElement
+import kotlinx.serialization.json.jsonPrimitive
 
 @Serializable
 sealed class WalletResponse<T> {
@@ -52,7 +53,7 @@ sealed class WalletResponse<T> {
     }
 }
 
-@Serializable
+@Serializable(with = TalerErrorInfoDeserializer::class)
 data class TalerErrorInfo(
     // Numeric error code defined defined in the
     // GANA gnu-taler-error-codes registry.
@@ -65,42 +66,50 @@ data class TalerErrorInfo(
     // for the instance of the error.
     val message: String? = null,
 
-    // Error details
-    @Serializable(JSONObjectDeserializer::class)
-    val details: JSONObject? = null,
-
-    // KYC URL (in case KYC is required)
-    val kycUrl: String? = null,
+    // Error extra details
+    val extra: Map<String, JsonElement> = mapOf(),
 ) {
     val userFacingMsg: String
         get() {
             return StringBuilder().apply {
                 hint?.let { append(it) }
                 message?.let { append(" ").append(it) }
-                details?.let { details ->
-                    if (details.length() > 0) {
-                        append("\n\n")
-                        details.optJSONObject("errorResponse")?.let { 
errorResponse ->
-                            append(errorResponse.optString("code")).append(" ")
-                            append(errorResponse.optString("hint"))
-                        } ?: append(details.toString(2))
-                    }
-                }
             }.toString()
         }
+
+    fun getStringExtra(key: String): String? =
+        extra[key]?.jsonPrimitive?.content
 }
 
-class JSONObjectDeserializer : KSerializer<JSONObject> {
+class TalerErrorInfoDeserializer : KSerializer<TalerErrorInfo> {
+    private val stringToJsonElementSerializer = 
MapSerializer(String.serializer(), JsonElement.serializer())
+
+    override val descriptor: SerialDescriptor
+        get() = stringToJsonElementSerializer.descriptor
+
+    override fun deserialize(decoder: Decoder): TalerErrorInfo {
+        // Decoder -> JsonInput
+        require(decoder is JsonDecoder)
+        val json = decoder.json
+        val filtersMap = 
decoder.decodeSerializableValue(stringToJsonElementSerializer)
+
+        val code = filtersMap["code"]?.let {
+            json.decodeFromJsonElement(TalerErrorCode.serializer(), it)
+        } ?: TalerErrorCode.UNKNOWN
+        val hint = filtersMap["hint"]?.let {
+            json.decodeFromJsonElement(String.serializer(), it)
+        }
+        val message = filtersMap["message"]?.let {
+            json.decodeFromJsonElement(String.serializer(), it)
+        }
 
-    override val descriptor = 
PrimitiveSerialDescriptor("JSONObjectDeserializer", STRING)
+        val knownKeys = setOf("code", "hint", "message")
+        val unknownFilters = filtersMap.filter { (key, _) -> 
!knownKeys.contains(key) }
 
-    override fun deserialize(decoder: Decoder): JSONObject {
-        val input = decoder as JsonDecoder
-        val tree = input.decodeJsonElement() as JsonObject
-        return JSONObject(tree.toString())
+        return TalerErrorInfo(code, hint, message, unknownFilters)
     }
 
-    override fun serialize(encoder: Encoder, value: JSONObject) {
+    override fun serialize(encoder: Encoder, value: TalerErrorInfo) {
         error("not supported")
     }
 }
diff --git 
a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullIntroComposable.kt 
b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullIntroComposable.kt
index 7fd01f5..6d74ba6 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullIntroComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullIntroComposable.kt
@@ -78,7 +78,7 @@ fun OutgoingPullIntroComposable(
             value = subject,
             onValueChange = { input ->
                 if (input.length <= MAX_LENGTH_SUBJECT)
-                    subject = input
+                    subject = input.replace('\n', ' ')
             },
             isError = subject.isBlank(),
             label = {
diff --git 
a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullResultComposable.kt 
b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullResultComposable.kt
index a68ae16..e6d9ec9 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullResultComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullResultComposable.kt
@@ -35,6 +35,7 @@ import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
+import kotlinx.serialization.json.JsonPrimitive
 import net.taler.common.QrCodeManager
 import net.taler.wallet.R
 import net.taler.wallet.backend.TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED
@@ -42,7 +43,6 @@ import net.taler.wallet.backend.TalerErrorInfo
 import net.taler.wallet.compose.QrCodeUriComposable
 import net.taler.wallet.compose.TalerSurface
 import net.taler.wallet.compose.getQrCodeSize
-import org.json.JSONObject
 
 @Composable
 fun OutgoingPullResultComposable(state: OutgoingState, onClose: () -> Unit) {
@@ -141,7 +141,7 @@ fun PeerPullResponseLandscapePreview() {
 @Composable
 fun PeerPullErrorPreview() {
     Surface {
-        val json = JSONObject().apply { put("foo", "bar") }
+        val json = mapOf("foo" to JsonPrimitive("bar"))
         val response = 
OutgoingError(TalerErrorInfo(WALLET_WITHDRAWAL_KYC_REQUIRED, "hint", "message", 
json))
         OutgoingPullResultComposable(response) {}
     }
diff --git 
a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushIntroComposable.kt 
b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushIntroComposable.kt
index 0f30081..7fd7c8b 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushIntroComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushIntroComposable.kt
@@ -79,7 +79,7 @@ fun OutgoingPushIntroComposable(
             value = subject,
             onValueChange = { input ->
                 if (input.length <= MAX_LENGTH_SUBJECT)
-                    subject = input
+                    subject = input.replace('\n', ' ')
             },
             isError = subject.isBlank(),
             label = {
diff --git 
a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushResultComposable.kt 
b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushResultComposable.kt
index d81ec64..0fb3f2c 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushResultComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushResultComposable.kt
@@ -35,6 +35,7 @@ import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.unit.dp
+import kotlinx.serialization.json.JsonPrimitive
 import net.taler.common.QrCodeManager
 import net.taler.wallet.R
 import net.taler.wallet.backend.TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED
@@ -42,7 +43,6 @@ import net.taler.wallet.backend.TalerErrorInfo
 import net.taler.wallet.compose.QrCodeUriComposable
 import net.taler.wallet.compose.TalerSurface
 import net.taler.wallet.compose.getQrCodeSize
-import org.json.JSONObject
 
 @Composable
 fun OutgoingPushResultComposable(state: OutgoingState, onClose: () -> Unit) {
@@ -141,7 +141,7 @@ fun PeerPushResponseLandscapePreview() {
 @Composable
 fun PeerPushErrorPreview() {
     Surface {
-        val json = JSONObject().apply { put("foo", "bar") }
+        val json = mapOf("foo" to JsonPrimitive("bar"))
         val response = 
OutgoingError(TalerErrorInfo(WALLET_WITHDRAWAL_KYC_REQUIRED, "hint", "message", 
json))
         OutgoingPushResultComposable(response) {}
     }
diff --git 
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt 
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt
index fa30f5c..e65a751 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt
@@ -127,11 +127,21 @@ internal class TransactionAdapter(
             }
         }
 
-        private fun bindActionButton(transaction: Transaction) {
-            actionButton.visibility = transaction.handleKyc({ GONE }) {
-                actionButton.setOnClickListener { 
listener.onActionButtonClicked(transaction) }
-                actionButton.setText(R.string.transaction_action_kyc)
-                VISIBLE
+        private fun bindActionButton(t: Transaction) {
+            actionButton.setOnClickListener { 
listener.onActionButtonClicked(t) }
+            if (t.error != null) {
+                actionButton.visibility = t.handleKyc({ GONE }) {
+                    actionButton.setText(R.string.transaction_action_kyc)
+                    VISIBLE
+                }
+            } else if (t is TransactionWithdrawal && !t.confirmed) {
+                actionButton.setIconResource(R.drawable.ic_account_balance)
+                actionButton.visibility =
+                    if (t.withdrawalDetails is 
WithdrawalDetails.TalerBankIntegrationApi &&
+                        t.withdrawalDetails.bankConfirmationUrl != null) {
+                        
actionButton.setText(R.string.withdraw_button_confirm_bank)
+                        VISIBLE
+                    } else GONE
             }
         }
 
diff --git 
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionWithdrawalFragment.kt
 
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionWithdrawalFragment.kt
index ffc9005..fe255ad 100644
--- 
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionWithdrawalFragment.kt
+++ 
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionWithdrawalFragment.kt
@@ -111,7 +111,7 @@ class TransactionWithdrawalFragment : 
TransactionDetailFragment() {
     private fun setupActionButton(t: TransactionWithdrawal) {
         ui.actionButton.visibility = t.handleKyc({ GONE }) { error ->
             ui.actionButton.setText(R.string.transaction_action_kyc)
-            error.kycUrl?.let { kycUrl ->
+            error.getStringExtra("kycUrl")?.let { kycUrl ->
                 ui.actionButton.setOnClickListener {
                     launchInAppBrowser(requireContext(), kycUrl)
                 }
diff --git 
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt 
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt
index 08281c4..cd66193 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt
@@ -181,9 +181,16 @@ class TransactionsFragment : Fragment(), 
OnTransactionClickListener, ActionMode.
     }
 
     override fun onActionButtonClicked(transaction: Transaction) {
-        transaction.handleKyc({ error("Unhandled Action Button Event") }) { 
error ->
-            error.kycUrl?.let {
-                launchInAppBrowser(requireContext(), it)
+        if (transaction.error != null) {
+            transaction.handleKyc({ error("Unhandled Action Button Event") }) 
{ error ->
+                error.getStringExtra("kycUrl")?.let {
+                    launchInAppBrowser(requireContext(), it)
+                }
+            }
+        } else if (transaction is TransactionWithdrawal && 
!transaction.confirmed) {
+            if (transaction.withdrawalDetails is 
WithdrawalDetails.TalerBankIntegrationApi &&
+                transaction.withdrawalDetails.bankConfirmationUrl != null) {
+                launchInAppBrowser(requireContext(), 
transaction.withdrawalDetails.bankConfirmationUrl)
             }
         }
     }
diff --git a/wallet/src/main/res/layout/fragment_transaction_payment.xml 
b/wallet/src/main/res/layout/fragment_transaction_payment.xml
index 71447dd..b57315f 100644
--- a/wallet/src/main/res/layout/fragment_transaction_payment.xml
+++ b/wallet/src/main/res/layout/fragment_transaction_payment.xml
@@ -47,7 +47,7 @@
         <TextView
             android:id="@+id/amountPaidWithFeesView"
             style="@style/TransactionContent"
-            android:textColor="@color/red"
+            android:textColor="?colorError"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toBottomOf="@+id/amountPaidWithFeesLabel"
@@ -80,7 +80,7 @@
         <TextView
             android:id="@+id/feeView"
             style="@style/TransactionContent"
-            android:textColor="@color/red"
+            android:textColor="?colorError"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toBottomOf="@+id/feeLabel"
@@ -118,8 +118,10 @@
             android:layout_height="wrap_content"
             android:layout_marginVertical="10dp"
             android:text="@string/transactions_delete"
-            app:backgroundTint="@color/red"
+            android:textColor="?colorOnError"
+            app:backgroundTint="?colorError"
             app:icon="@drawable/ic_delete"
+            app:iconTint="?colorOnError"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintEnd_toEndOf="parent"
             app:layout_constraintStart_toStartOf="parent"
diff --git a/wallet/src/main/res/layout/fragment_uri_input.xml 
b/wallet/src/main/res/layout/fragment_uri_input.xml
index 6ea1a13..95c2297 100644
--- a/wallet/src/main/res/layout/fragment_uri_input.xml
+++ b/wallet/src/main/res/layout/fragment_uri_input.xml
@@ -42,7 +42,7 @@
 
     </com.google.android.material.textfield.TextInputLayout>
 
-    <Button
+    <com.google.android.material.button.MaterialButton
         android:id="@+id/pasteButton"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
@@ -50,9 +50,10 @@
         android:layout_marginTop="16dp"
         android:layout_marginEnd="16dp"
         android:layout_weight="1"
-        android:drawableLeft="@drawable/ic_content_paste"
         android:text="@string/paste"
-        app:drawableTint="?attr/colorOnPrimarySurface"
+        android:textColor="?colorOnPrimary"
+        app:icon="@drawable/ic_content_paste"
+        app:iconTint="?colorOnPrimary"
         app:layout_constraintEnd_toStartOf="@+id/okButton"
         app:layout_constraintHorizontal_chainStyle="spread_inside"
         app:layout_constraintStart_toStartOf="parent"
@@ -68,6 +69,7 @@
         android:layout_marginEnd="16dp"
         android:backgroundTint="@color/green"
         android:text="@string/ok"
+        android:textColor="@android:color/white"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toEndOf="@+id/pasteButton"
         app:layout_constraintTop_toBottomOf="@+id/uriLayout" />
diff --git a/wallet/src/main/res/layout/list_item_transaction.xml 
b/wallet/src/main/res/layout/list_item_transaction.xml
index 5dfdf14..8cd10a8 100644
--- a/wallet/src/main/res/layout/list_item_transaction.xml
+++ b/wallet/src/main/res/layout/list_item_transaction.xml
@@ -66,9 +66,11 @@
         android:id="@+id/actionButton"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:layout_marginTop="8dp"
         android:visibility="gone"
-        android:textColor="?colorOnTertiary"
-        app:backgroundTint="?colorTertiary"
+        android:textColor="?colorOnPrimary"
+        app:iconTint="?colorOnPrimary"
+        app:backgroundTint="?colorPrimary"
         app:layout_constraintStart_toStartOf="@id/title"
         app:layout_constraintTop_toBottomOf="@id/extraInfoView"
         tools:text="Complete KYC"

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