taler-android

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

commit 48b774fc535a3f6a844fc18989fcc8b8dce112c6
parent 7d7c271fae5c582b20331bd54af36e67bc806e63
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date:   Wed, 15 Jul 2026 17:55:10 +0200

improve contact import

Diffstat:
Mwallet/src/main/java/net/taler/wallet/contacts/ContactManager.kt | 4++--
Mwallet/src/main/java/net/taler/wallet/contacts/ContactScreen.kt | 65+++++++++++++++++++++++++++++++++++++++++++++++++----------------
2 files changed, 51 insertions(+), 18 deletions(-)

diff --git a/wallet/src/main/java/net/taler/wallet/contacts/ContactManager.kt b/wallet/src/main/java/net/taler/wallet/contacts/ContactManager.kt @@ -60,7 +60,7 @@ sealed class LookupResult { data object Idle : LookupResult() data object Loading : LookupResult() data class Error(val error: TalerErrorInfo) : LookupResult() - data class Success(val targetUri: String) : LookupResult() + data class Success(val targetUri: String, val source: String) : LookupResult() } class ContactManager( @@ -138,7 +138,7 @@ class ContactManager( message = "Alias not found", )) } else { - _lookupResult.value = LookupResult.Success(targetUri) + _lookupResult.value = LookupResult.Success(targetUri, taldirBaseUri) } }.onError { Log.e(TAG, "Error looking up contact: $it") diff --git a/wallet/src/main/java/net/taler/wallet/contacts/ContactScreen.kt b/wallet/src/main/java/net/taler/wallet/contacts/ContactScreen.kt @@ -88,7 +88,7 @@ fun ContactScreen( var initialSource by remember { mutableStateOf("") } var initialPetname by remember { mutableStateOf("") } - fun fillFromUri(uriString: String) { + fun fillFromUri(uriString: String, overriddenSource: String? = null) { val uri = uriString.toUri() val segments = uri.pathSegments var mailboxAddressTmp = uriString @@ -99,17 +99,22 @@ fun ContactScreen( mailboxAddressTmp = mailboxAddressTmp.removePrefix("""$alias/""") val mailboxAddressFull = mailboxAddressTmp.split("?")[0] val mailboxAddress = mailboxAddressFull.split("/").last() - val mailboxBaseUri = buildString { - append("https://") - append(mailboxAddressFull.removeSuffix(mailboxAddress)) + val mailboxBaseUri = if (mailboxAddressFull.startsWith("http://") || mailboxAddressFull.startsWith("https://")) { + mailboxAddressFull.removeSuffix(mailboxAddress) + } else { + buildString { + append("https://") + append(mailboxAddressFull.removeSuffix(mailboxAddress)) + } } val petname = uri.getQueryParameter("petname") ?: "" + val source = overriddenSource ?: uri.getQueryParameter("source") ?: "QR" initialAlias = alias initialAliasType = aliasType initialMailboxBaseUri = mailboxBaseUri initialMailboxAddress = mailboxAddress - initialSource = uriString + initialSource = source initialPetname = petname showAddDialog = true } @@ -121,9 +126,9 @@ fun ContactScreen( LaunchedEffect(lookupResult) { if (lookupResult is LookupResult.Success) { - val uri = (lookupResult as LookupResult.Success).targetUri + val res = lookupResult as LookupResult.Success contactManager.resetLookup() - fillFromUri(uri) + fillFromUri(res.targetUri, res.source) } } @@ -235,6 +240,7 @@ fun ContactScreen( contactManager.taldirLookupAlias(alias, aliasType) }, lookupResult = lookupResult, + existingContacts = (contactResult as? ContactResult.Success)?.contacts ?: emptyList() ) } } @@ -251,18 +257,23 @@ fun AddContactDialog( onAdd: (String, String, String, String, String, String) -> Unit, onLookup: (String, String) -> Unit, lookupResult: LookupResult, + existingContacts: List<ContactEntry> = emptyList(), ) { var alias by remember { mutableStateOf(initialAlias) } var aliasType by remember { mutableStateOf(initialAliasType) } - var mailboxBaseUri by remember { mutableStateOf(initialMailboxBaseUri) } - var mailboxAddress by remember { mutableStateOf(initialMailboxAddress) } + var mailbox by remember { + mutableStateOf(if (initialMailboxBaseUri.isEmpty()) "" else "$initialMailboxBaseUri$initialMailboxAddress") + } var source by remember { mutableStateOf(initialSource) } var petname by remember { mutableStateOf(initialPetname) } + val isDuplicate = existingContacts.any { it.alias == alias && it.aliasType == aliasType } + LaunchedEffect(initialAlias) { alias = initialAlias } LaunchedEffect(initialAliasType) { aliasType = initialAliasType } - LaunchedEffect(initialMailboxBaseUri) { mailboxBaseUri = initialMailboxBaseUri } - LaunchedEffect(initialMailboxAddress) { mailboxAddress = initialMailboxAddress } + LaunchedEffect(initialMailboxBaseUri, initialMailboxAddress) { + mailbox = if (initialMailboxBaseUri.isEmpty()) "" else "$initialMailboxBaseUri$initialMailboxAddress" + } LaunchedEffect(initialSource) { source = initialSource } LaunchedEffect(initialPetname) { petname = initialPetname } @@ -281,15 +292,26 @@ fun AddContactDialog( value = alias, onValueChange = { alias = it }, label = { Text("Alias") }, + isError = isDuplicate, modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp) ) OutlinedTextField( value = aliasType, onValueChange = { aliasType = it }, label = { Text("Alias Type") }, + isError = isDuplicate, modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp) ) + if (isDuplicate) { + Text( + text = "A contact with this alias and type already exists.", + color = MaterialTheme.colorScheme.error, + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.padding(bottom = 8.dp) + ) + } + if (lookupResult is LookupResult.Loading) { CircularProgressIndicator( modifier = Modifier @@ -315,15 +337,26 @@ fun AddContactDialog( ) } - OutlinedTextField(value = mailboxBaseUri, onValueChange = { mailboxBaseUri = it }, label = { Text("Mailbox Base URI") }, modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp)) - OutlinedTextField(value = mailboxAddress, onValueChange = { mailboxAddress = it }, label = { Text("Mailbox Address") }, modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp)) - OutlinedTextField(value = source, onValueChange = { source = it }, label = { Text("Source") }, modifier = Modifier.fillMaxWidth()) + OutlinedTextField( + value = mailbox, + onValueChange = { mailbox = it }, + label = { Text("Mailbox") }, + modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp) + ) } }, confirmButton = { Button( - onClick = { onAdd(alias, aliasType, mailboxBaseUri, mailboxAddress, source, petname) }, - enabled = alias.isNotBlank() && petname.isNotBlank() + onClick = { + val mailboxParts = mailbox.split("/") + val base = if (mailboxParts.size > 1) { + mailboxParts.dropLast(1).joinToString("/", postfix = "/") + } else "" + val addr = mailboxParts.last() + val finalSource = source.ifBlank { "local" } + onAdd(alias, aliasType, base, addr, finalSource, petname) + }, + enabled = alias.isNotBlank() && petname.isNotBlank() && mailbox.isNotBlank() && !isDuplicate ) { Text(stringResource(R.string.ok)) }