commit 6593d340aaa45c9e677d5ac2c6b591e6b72a2f3d parent 1853a28ba31a3137f2b21bd320901235adccce36 Author: Iván Ávalos <avalos@disroot.org> Date: Wed, 14 Jan 2026 23:52:59 +0100 [wallet] fix "not possible to deposit" error Diffstat:
9 files changed, 46 insertions(+), 29 deletions(-)
diff --git a/taler-kotlin-android/src/main/java/net/taler/common/Amount.kt b/taler-kotlin-android/src/main/java/net/taler/common/Amount.kt @@ -281,7 +281,7 @@ data class Amount( } override fun equals(other: Any?): Boolean { - return other is Amount && (value == other.value && fraction == other.fraction) + return other is Amount && (value == other.value && fraction == other.fraction && currency == other.currency) } override fun compareTo(other: Amount): Int { diff --git a/wallet/src/main/java/net/taler/wallet/accounts/Accounts.kt b/wallet/src/main/java/net/taler/wallet/accounts/Accounts.kt @@ -114,10 +114,10 @@ data class PaytoUriIban( .authority(targetType) .apply { if (bic != null) appendPath(bic) } .appendPath(iban) - .appendQueryParameter("receiver-name", receiverName) - .appendQueryParameter("receiver-postal-code", receiverPostalCode) - .appendQueryParameter("receiver-town", receiverTown) .apply { + receiverName?.let { appendQueryParameter("receiver-name", it) } + receiverPostalCode?.let { appendQueryParameter("receiver-postal-code", it) } + receiverTown?.let { appendQueryParameter("receiver-town", it) } params.forEach { (key, value) -> if (value.isNotEmpty() && build().getQueryParameter(key) == null) { appendQueryParameter(key, value) @@ -161,6 +161,7 @@ data class PaytoUriTalerBank( .appendPath(host) .appendPath(account) .apply { + receiverName?.let { appendQueryParameter("receiver-name", it) } params.forEach { (key, value) -> if (value.isNotEmpty()) { appendQueryParameter(key, value) @@ -204,6 +205,7 @@ data class PaytoUriBitcoin( } } .apply { + receiverName?.let { appendQueryParameter("receiver-name", it) } params.forEach { (key, value) -> if (value.isNotEmpty()) { appendQueryParameter(key, value) diff --git a/wallet/src/main/java/net/taler/wallet/accounts/AddAccountComposable.kt b/wallet/src/main/java/net/taler/wallet/accounts/AddAccountComposable.kt @@ -14,7 +14,7 @@ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -package net.taler.wallet.deposit +package net.taler.wallet.accounts import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -52,14 +52,18 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import net.taler.wallet.BottomInsetsSpacer import net.taler.wallet.R -import net.taler.wallet.accounts.KnownBankAccountInfo -import net.taler.wallet.accounts.PaytoUri -import net.taler.wallet.accounts.PaytoUriBitcoin -import net.taler.wallet.accounts.PaytoUriIban -import net.taler.wallet.accounts.PaytoUriTalerBank import net.taler.wallet.backend.TalerErrorInfo import net.taler.wallet.compose.ErrorComposable import net.taler.wallet.compose.WarningLabel +import net.taler.wallet.deposit.AddAccountBitcoin +import net.taler.wallet.deposit.AddAccountIBAN +import net.taler.wallet.deposit.AddAccountTaler +import net.taler.wallet.deposit.GetDepositWireTypesResponse +import net.taler.wallet.deposit.WireType +import net.taler.wallet.deposit.WireTypeDetails +import net.taler.wallet.deposit.getBitcoinPayto +import net.taler.wallet.deposit.getIbanPayto +import net.taler.wallet.deposit.getTalerPayto import net.taler.wallet.useDebounce @Composable @@ -115,9 +119,14 @@ fun AddAccountComposable( var bitcoinAddress by rememberSaveable(presetPaytoUri) { mutableStateOf("") } // TODO: fill-in bitcoin address val paytoUri = when(selectedWireType) { - WireType.IBAN -> getIbanPayto(ibanName, ibanZip, ibanTown, ibanIban) - WireType.TalerBank -> getTalerPayto(talerName, talerHost, talerAccount) - WireType.Bitcoin -> getBitcoinPayto(bitcoinAddress) + WireType.IBAN -> getIbanPayto( + ibanName.trim(), + ibanZip?.trim(), + ibanTown?.trim(), + ibanIban.trim() + ) + WireType.TalerBank -> getTalerPayto(talerName.trim(), talerHost.trim(), talerAccount.trim()) + WireType.Bitcoin -> getBitcoinPayto(bitcoinAddress.trim()) else -> null } diff --git a/wallet/src/main/java/net/taler/wallet/accounts/AddAccountFragment.kt b/wallet/src/main/java/net/taler/wallet/accounts/AddAccountFragment.kt @@ -22,7 +22,6 @@ import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -36,7 +35,6 @@ import net.taler.wallet.MainViewModel import net.taler.wallet.R import net.taler.wallet.compose.LoadingScreen import net.taler.wallet.compose.TalerSurface -import net.taler.wallet.deposit.AddAccountComposable import net.taler.wallet.deposit.GetDepositWireTypesResponse import net.taler.wallet.showError diff --git a/wallet/src/main/java/net/taler/wallet/compose/AmountInputFIeld.kt b/wallet/src/main/java/net/taler/wallet/compose/AmountInputFIeld.kt @@ -119,7 +119,7 @@ fun AmountCurrencyField( CurrencyDropdown( modifier = Modifier.weight(1f), currencies = currencies, - onCurrencyChanged = { onAmountChanged(amount.copy(currency = it)) }, + onCurrencyChanged = { onAmountChanged(amount.copy(currency = it, spec = null)) }, initialCurrency = amount.currency, readOnly = readOnly || !enabled, ) @@ -183,7 +183,7 @@ private fun CurrencyDropdown( }, onClick = { selectedIndex = index - onCurrencyChanged(currencies[index]) + onCurrencyChanged(s) expanded = false } ) diff --git a/wallet/src/main/java/net/taler/wallet/deposit/DepositAmountComposable.kt b/wallet/src/main/java/net/taler/wallet/deposit/DepositAmountComposable.kt @@ -26,7 +26,6 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Button import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -59,6 +58,7 @@ import net.taler.wallet.useDebounce @Composable fun DepositAmountComposable( state: DepositState.AccountSelected, + knownCurrencies: List<String>, getCurrencySpec: (currency: String) -> CurrencySpecification?, checkDeposit: suspend (amount: Amount) -> CheckDepositResult, onMakeDeposit: (amount: Amount) -> Unit, @@ -71,11 +71,10 @@ fun DepositAmountComposable( ) { var checkResult by remember { mutableStateOf<CheckDepositResult>(CheckDepositResult.None) } // TODO: handle unavailable scopes in UI (i.e. explain restrictions) - val currencies = state.account.currencies?.distinct() ?: emptyList() + // if currencies is null, we assume any (known) currency is supported + val currencies = state.account.currencies?.distinct() ?: knownCurrencies var amount by remember(currencies) { mutableStateOf(currencies.firstOrNull()?.let { Amount.zero(it) }) } - val spec = remember(amount) { amount?.let { getCurrencySpec(it.currency) } } - val maxDepositable = remember(amount) { amount?.let { state.maxDepositable[it.currency] } } Column( modifier = Modifier @@ -96,9 +95,8 @@ fun DepositAmountComposable( if (currencies.isEmpty() || amount == null) { ErrorComposable( - // FIXME: i18n string error = TalerErrorInfo.makeCustomError( - "It is not possible to deposit to this account, please select another one"), + stringResource(R.string.send_deposits_no_currencies_error)), modifier = Modifier.fillMaxSize(), devMode = false, onClose = onClose, @@ -106,10 +104,11 @@ fun DepositAmountComposable( return } + val spec = remember(amount) { getCurrencySpec(amount!!.currency) } + val maxDepositable = remember(amount) { state.maxDepositable[amount!!.currency] } + amount.useDebounce { - if (!amount!!.isZero()) { - checkResult = checkDeposit(amount!!) - } + checkResult = checkDeposit(amount!!) } AnimatedVisibility(maxDepositable?.rawAmount != null) { @@ -237,6 +236,7 @@ fun DepositAmountComposablePreview() { ) DepositAmountComposable( state = state, + knownCurrencies = listOf("CHF", "EUR", "MXN", "USD"), checkDeposit = { CheckDepositResult.Success( totalDepositCost = Amount.fromJSONString("KUDOS:10"), effectiveDepositAmount = Amount.fromJSONString("KUDOS:12"), @@ -264,6 +264,7 @@ fun DepositAmountComposableErrorPreview() { ) DepositAmountComposable( state = state, + knownCurrencies = listOf("CHF", "EUR", "MXN", "USD"), checkDeposit = { CheckDepositResult.Success( totalDepositCost = Amount.fromJSONString("KUDOS:10"), effectiveDepositAmount = Amount.fromJSONString("KUDOS:12"), diff --git a/wallet/src/main/java/net/taler/wallet/deposit/DepositFragment.kt b/wallet/src/main/java/net/taler/wallet/deposit/DepositFragment.kt @@ -33,6 +33,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.map import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController import kotlinx.coroutines.launch @@ -74,6 +75,9 @@ class DepositFragment : Fragment() { TalerSurface { val state by depositManager.depositState.collectAsStateLifecycleAware() val knownBankAccounts by accountManager.bankAccounts.collectAsStateLifecycleAware() + val knownCurrencies by model.balanceManager.balances.map { + it.map { bl -> bl.currency } + }.observeAsState(emptyList()) val devMode by model.devMode.observeAsState(false) BackHandler(state is DepositState.AccountSelected) { @@ -112,6 +116,7 @@ class DepositFragment : Fragment() { is DepositState.AccountSelected -> { DepositAmountComposable( state = s, + knownCurrencies = knownCurrencies, getCurrencySpec = exchangeManager::getSpecForCurrency, checkDeposit = { a -> depositManager.checkDepositFees(s.account.paytoUri, a) diff --git a/wallet/src/main/java/net/taler/wallet/deposit/DepositManager.kt b/wallet/src/main/java/net/taler/wallet/deposit/DepositManager.kt @@ -40,6 +40,7 @@ import net.taler.wallet.backend.WalletBackendApi import net.taler.wallet.balances.BalanceManager import net.taler.wallet.balances.ScopeInfo import org.json.JSONObject +import androidx.core.net.toUri class DepositManager( private val api: WalletBackendApi, @@ -52,7 +53,7 @@ class DepositManager( fun isSupportedPayToUri(uriString: String): Boolean { if (!uriString.startsWith("payto://")) return false - val u = Uri.parse(uriString) + val u = uriString.toUri() if (!u.authority.equals("iban", ignoreCase = true)) return false return u.pathSegments.isNotEmpty() } @@ -196,7 +197,7 @@ fun getIbanPayto( iban = iban, bic = null, targetPath = "", - params = mapOf("receiver-name" to receiverName), + params = mapOf(), receiverName = receiverName, receiverPostalCode = receiverPostalCode, receiverTown = receiverTown, @@ -206,7 +207,7 @@ fun getTalerPayto(receiverName: String, host: String, account: String) = PaytoUr host = host, account = account, targetPath = "", - params = mapOf("receiver-name" to receiverName), + params = mapOf(), receiverName = receiverName, ).paytoUri diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml @@ -315,6 +315,7 @@ GNU Taler is immune to many types of fraud such as credit card data theft, phish <string name="send_deposit_kyc_auth_warning_account">Do not use a different bank account for the deposit, or the verification will fail.</string> <string name="send_deposit_kyc_auth_warning_subject">This is mandatory, otherwise the verification will fail.</string> <string name="send_deposit_name">Account holder</string> + <string name="send_deposits_no_currencies_error">No known currencies are available to deposit.</string> <string name="send_deposit_no_methods_error">No supported wire methods</string> <string name="send_deposit_postal_code">Postal code (optional)</string> <string name="send_deposit_select_account_title">Select bank account</string>