taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

commit 8e6aaa36486b30b17d586c02f15bf4fb1beb73e1
parent b292f85b3970fcd8bf9339944d075d5c62f65bd0
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date:   Sun, 18 Oct 2026 18:59:52 +0200

Improve UX for alias registration flows that were completed out of band

Diffstat:
Mwallet/src/main/java/net/taler/wallet/contacts/AliasRegistrationScreen.kt | 54+++++++++++++++++++++++++++++++++++++++++++++++++++---
Mwallet/src/main/java/net/taler/wallet/contacts/ContactManager.kt | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mwallet/src/main/res/values/strings.xml | 2++
3 files changed, 115 insertions(+), 5 deletions(-)

diff --git a/wallet/src/main/java/net/taler/wallet/contacts/AliasRegistrationScreen.kt b/wallet/src/main/java/net/taler/wallet/contacts/AliasRegistrationScreen.kt @@ -17,8 +17,10 @@ package net.taler.wallet.contacts import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize 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 @@ -79,6 +81,12 @@ fun AliasRegistrationScreen( }, onComplete = { alias, aliasType, taldirBaseUri, targetUri, solution -> model.contactManager.completeTaldirRegister(alias, aliasType, taldirBaseUri, targetUri, solution) + }, + onRecover = { alias, aliasType, taldirBaseUri, targetUri -> + model.contactManager.recoverRegistration(alias, aliasType, taldirBaseUri, targetUri) + }, + onDelete = { alias, aliasType, taldirBaseUri, durationMonths -> + model.contactManager.taldirDeleteAlias(alias, aliasType, taldirBaseUri, durationMonths) } ) } @@ -91,6 +99,8 @@ fun AliasRegistrationContent( onNavigateBack: () -> Unit, onRegister: (String, String, String, String, Int) -> Unit, onComplete: (String, String, String, String, String) -> Unit, + onRecover: (String, String, String, String) -> Unit, + onDelete: (String, String, String, Int) -> Unit, ) { var aliasType by remember { mutableStateOf("email") } var aliasTypeExpanded by remember { mutableStateOf(false) } @@ -213,6 +223,23 @@ fun AliasRegistrationContent( ) { Text(stringResource(R.string.alias_registration_button)) } + + Spacer(modifier = Modifier.height(8.dp)) + + Button( + onClick = { + onDelete( + alias, + aliasType, + taldirBaseUri, + durationMonths.toIntOrNull() ?: 12 + ) + }, + enabled = aliasType.isNotBlank() && alias.isNotBlank() && registerResult !is RegisterResult.Loading, + modifier = Modifier.fillMaxWidth() + ) { + Text(stringResource(R.string.alias_registration_delete)) + } } when (val res = registerResult) { @@ -258,6 +285,23 @@ fun AliasRegistrationContent( ) { Text(stringResource(R.string.ok)) } + + Spacer(modifier = Modifier.height(8.dp)) + + Button( + onClick = { + onRecover( + alias, + aliasType, + taldirBaseUri, + res.targetUri + ) + }, + enabled = alias.isNotBlank(), + modifier = Modifier.fillMaxWidth() + ) { + Text(stringResource(R.string.alias_registration_out_of_band)) + } } } is RegisterResult.CompletionLoading -> { @@ -308,7 +352,9 @@ fun AliasRegistrationContentPreview() { registerResult = RegisterResult.Idle, onNavigateBack = {}, onRegister = { _, _, _, _, _ -> }, - onComplete = { _, _, _, _, _ -> } + onComplete = { _, _, _, _, _ -> }, + onRecover = { _, _, _, _ -> }, + onDelete = { _, _, _, _ -> } ) } } @@ -328,10 +374,12 @@ fun AliasRegistrationContentSuccessPreview() { ), emptyList() ), - registerResult = RegisterResult.Success(validFor = null), + registerResult = RegisterResult.Success(validFor = null, targetUri = "https://mailbox.taler.net/alice"), onNavigateBack = {}, onRegister = { _, _, _, _, _ -> }, - onComplete = { _, _, _, _, _ -> } + onComplete = { _, _, _, _, _ -> }, + onRecover = { _, _, _, _ -> }, + onDelete = { _, _, _, _ -> } ) } } diff --git a/wallet/src/main/java/net/taler/wallet/contacts/ContactManager.kt b/wallet/src/main/java/net/taler/wallet/contacts/ContactManager.kt @@ -82,7 +82,7 @@ sealed class RegisterResult { data object Idle : RegisterResult() data object Loading : RegisterResult() data class Error(val error: TalerErrorInfo) : RegisterResult() - data class Success(val validFor: RelativeTime?) : RegisterResult() + data class Success(val validFor: RelativeTime?, val targetUri: String) : RegisterResult() data object CompletionLoading : RegisterResult() data object CompletionSuccess : RegisterResult() } @@ -208,7 +208,7 @@ class ContactManager( put("duration", JSONObject(BackendManager.json.encodeToString(RelativeTime.serializer(), RelativeTime(durationUs)))) }.onSuccess { Log.d(TAG, "Taldir registration initiated for $alias: $it") - _registerResult.value = RegisterResult.Success(it.validFor) + _registerResult.value = RegisterResult.Success(it.validFor, targetUri) }.onError { Log.e(TAG, "Error initiating Taldir registration: $it") _registerResult.value = RegisterResult.Error(it) @@ -240,4 +240,64 @@ class ContactManager( } } } + + fun taldirDeleteAlias( + alias: String, + aliasType: String, + taldirBaseUri: String = "https://taldir.gnunet.org", + durationMonths: Int, + ) { + taldirRegister( + alias = alias, + aliasType = aliasType, + taldirBaseUri = taldirBaseUri, + targetUri = "", + durationMonths = durationMonths, + ) + } + + fun recoverRegistration( + alias: String, + aliasType: String, + taldirBaseUri: String = "https://taldir.gnunet.org", + expectedTargetUri: String, + ) { + _registerResult.value = RegisterResult.Loading + scope.launch { + api.request<LookupAliasResponse>("lookupAlias", LookupAliasResponse.serializer()) { + put("alias", alias) + put("aliasType", aliasType) + put("taldirBaseUrl", taldirBaseUri) + }.onSuccess { resp -> + val targetUri = resp.targetUri ?: "" + if (expectedTargetUri.isEmpty()) { + if (targetUri.isBlank()) { + _registerResult.value = RegisterResult.CompletionSuccess + } else { + _registerResult.value = RegisterResult.Error(TalerErrorInfo( + code = TalerErrorCode.UNKNOWN, + message = "Existing registration is not deleted (points to $targetUri)", + )) + } + } else { + if (targetUri.isNullOrBlank()) { + _registerResult.value = RegisterResult.Error(TalerErrorInfo( + code = TalerErrorCode.UNKNOWN, + message = "Alias not found", + )) + } else if (targetUri != expectedTargetUri) { + _registerResult.value = RegisterResult.Error(TalerErrorInfo( + code = TalerErrorCode.UNKNOWN, + message = "Existing registration points to a different mailbox URI", + )) + } else { + _registerResult.value = RegisterResult.CompletionSuccess + } + } + }.onError { + Log.e(TAG, "Error recovering registration: $it") + _registerResult.value = RegisterResult.Error(it) + } + } + } } diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml @@ -511,6 +511,8 @@ GNU Taler is immune to many types of fraud such as credit card data theft, phish <string name="alias_registration_solution">Challenge Solution</string> <string name="alias_registration_success">Registration successful!</string> <string name="alias_registration_title">Alias Registration</string> + <string name="alias_registration_delete">Delete Alias</string> + <string name="alias_registration_out_of_band">I have completed the registration out of band</string> <string name="contact_add_button">Add Contact</string> <string name="contact_add_intro">Do you want to add this contact to your wallet?</string> <string name="contact_add_title">Add Contact</string>