diff --git a/wallet/src/main/java/net/taler/wallet/HandleUriScreen.kt b/wallet/src/main/java/net/taler/wallet/HandleUriScreen.kt index 03a3c56a..05e85ce8 100644 --- a/wallet/src/main/java/net/taler/wallet/HandleUriScreen.kt +++ b/wallet/src/main/java/net/taler/wallet/HandleUriScreen.kt @@ -14,6 +14,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.core.net.toUri import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Dispatchers @@ -46,6 +47,7 @@ fun HandleUriScreen( val networkStatus by model.networkManager.networkStatus.observeAsState() val payStatus by model.paymentManager.payStatus.observeAsState(PayStatus.None) val scope = rememberCoroutineScope() + val invalidUriMessage = stringResource(R.string.uri_invalid) fun processTalerUri() { if (processing) return @@ -146,7 +148,7 @@ fun HandleUriScreen( onNavigate(WalletDestination.Contacts(addContactUri = u2), true) } else -> { - errorInfo = TalerErrorInfo.makeCustomError("Unsupported URI: $u2") + errorInfo = TalerErrorInfo.makeCustomError("$invalidUriMessage: $u2") } } } catch (_: CancellationException) { diff --git a/wallet/src/main/java/net/taler/wallet/compose/ErrorComposable.kt b/wallet/src/main/java/net/taler/wallet/compose/ErrorComposable.kt index 117a6d5a..5c1498d6 100644 --- a/wallet/src/main/java/net/taler/wallet/compose/ErrorComposable.kt +++ b/wallet/src/main/java/net/taler/wallet/compose/ErrorComposable.kt @@ -115,7 +115,29 @@ fun ErrorComposable( ) } } else { - Text( + val known = remember(error) { error.errorMessage() } + if (known != null) { + Text( + text = stringResource(known.message), + style = MaterialTheme.typography.bodyLarge, + textAlign = TextAlign.Center, + ) + known.guidance?.let { + Text( + text = stringResource(it), + modifier = Modifier.padding(top = 8.dp), + textAlign = TextAlign.Center, + ) + } + // wallet-core's English diagnostic, for support + if (error.userFacingMsg.isNotEmpty()) Text( + text = error.userFacingMsg, + modifier = Modifier.padding(top = 16.dp), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + textAlign = TextAlign.Center, + ) + } else Text( text = error.userFacingMsg.ifEmpty { stringResource(R.string.error_code_only, error.rawCode) }, diff --git a/wallet/src/main/java/net/taler/wallet/compose/ErrorMessages.kt b/wallet/src/main/java/net/taler/wallet/compose/ErrorMessages.kt new file mode 100644 index 00000000..8905f033 --- /dev/null +++ b/wallet/src/main/java/net/taler/wallet/compose/ErrorMessages.kt @@ -0,0 +1,63 @@ +/* + * This file is part of GNU Taler + * (C) 2026 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 + */ + +package net.taler.wallet.compose + +import androidx.annotation.StringRes +import net.taler.wallet.R +import net.taler.wallet.backend.TalerErrorCode.* +import net.taler.wallet.backend.TalerErrorInfo + +/** + * A translated explanation of a wallet-core error, and what the user can do. + * wallet-core's own [TalerErrorInfo.hint] is English diagnostic text and stays + * available as technical detail. + */ +data class ErrorMessage( + @param:StringRes val message: Int, + @param:StringRes val guidance: Int? = null, +) + +fun TalerErrorInfo.errorMessage(): ErrorMessage? = when (code) { + WALLET_NETWORK_ERROR, + WALLET_UNEXPECTED_REQUEST_ERROR, + WALLET_EXCHANGE_UNAVAILABLE -> ErrorMessage( + R.string.error_network, + R.string.error_network_guidance, + ) + WALLET_HTTP_REQUEST_GENERIC_TIMEOUT, + GENERIC_TIMEOUT -> ErrorMessage( + R.string.error_timeout, + R.string.error_network_guidance, + ) + WALLET_RECEIVED_MALFORMED_RESPONSE -> ErrorMessage(R.string.error_malformed_response) + WALLET_ORDER_ALREADY_CLAIMED -> ErrorMessage( + R.string.error_order_claimed, + R.string.error_order_new_guidance, + ) + WALLET_ORDER_ALREADY_PAID -> ErrorMessage(R.string.error_order_paid) + WALLET_MERCHANT_ORDER_NOT_FOUND -> ErrorMessage( + R.string.error_order_not_found, + R.string.error_order_new_guidance, + ) + WALLET_TALER_URI_MALFORMED, + WALLET_INVALID_TALER_PAY_URI -> ErrorMessage(R.string.uri_invalid) + WALLET_KYC_LIMIT_EXCEEDED -> ErrorMessage(R.string.error_kyc_limit) + WALLET_NO_SUITABLE_EXCHANGE -> ErrorMessage(R.string.error_no_suitable_exchange) + WALLET_PEER_PULL_DEBIT_PURSE_GONE, + WALLET_PEER_PUSH_CREDIT_PURSE_GONE -> ErrorMessage(R.string.error_peer_gone) + else -> null +} diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml index 89750709..22787d12 100644 --- a/wallet/src/main/res/values/strings.xml +++ b/wallet/src/main/res/values/strings.xml @@ -67,6 +67,17 @@ GNU Taler is immune to many types of fraud such as credit card data theft, phish Error Error code %1$d Export error diagnostics + The wallet could not reach the service. + Check your connection and try again. + The service took too long to respond. + A service returned a response the wallet could not understand. + This order was already claimed by another wallet. + This order was already paid. + The merchant order no longer exists or has expired. + Return to the merchant and request a new order. + This amount exceeds the limit available without identity verification. + No compatible payment service is available for this operation. + This payment is no longer available. Initialization error An error occurred while starting the wallet. This can happen after an app update or if the wallet data is incompatible. Please retry, and if the problem persists, export the logs and report this issue to the developers. Export database