commit 4b58e98a43c7056fae48dd55b1975966fd4bd1fa parent 5f7921186b533f5a210c999b2b26557a4ebb8908 Author: Iván Ávalos <avalos@disroot.org> Date: Mon, 26 Jan 2026 18:58:54 +0100 [wallet] fix #10919 (handle missing-tos) Diffstat:
8 files changed, 38 insertions(+), 26 deletions(-)
diff --git a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeAdapter.kt b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeAdapter.kt @@ -102,7 +102,7 @@ internal class ExchangeAdapter( private fun openMenu(anchor: View, item: ExchangeItem) = PopupMenu(context, anchor).apply { inflate(R.menu.exchange) - if (item.tosStatus == ExchangeTosStatus.Accepted) { + if (item.tosStatus.isAccepted()) { menu.findItem(R.id.action_view_tos).isVisible = true menu.findItem(R.id.action_accept_tos).isVisible = false menu.findItem(R.id.action_forget_tos).isVisible = devMode diff --git a/wallet/src/main/java/net/taler/wallet/exchanges/Exchanges.kt b/wallet/src/main/java/net/taler/wallet/exchanges/Exchanges.kt @@ -54,5 +54,7 @@ enum class ExchangeTosStatus { Accepted, @SerialName("missing-tos") - MissingTos, + MissingTos; + + fun isAccepted() = this in listOf(Accepted, MissingTos) } \ No newline at end of file diff --git a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullComposable.kt b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullComposable.kt @@ -91,7 +91,7 @@ fun OutgoingPullComposable( var option by rememberSaveable { mutableStateOf(DEFAULT_EXPIRY) } var hours by rememberSaveable { mutableLongStateOf(DEFAULT_EXPIRY.hours) } - val tosReview = checkResult != null && checkResult?.tosStatus != ExchangeTosStatus.Accepted + val tosReview = checkResult != null && checkResult!!.tosStatus!!.isAccepted() amount.amount.useDebounce { if (amount.debounce) { @@ -258,7 +258,7 @@ fun OutgoingPullComposable( enabled = tosReview || (res != null && !amount.amount.isZero() && subject.isNotBlank()), onClick = { val ex = res?.exchangeBaseUrl ?: error("clickable without exchange") - if (res.tosStatus == ExchangeTosStatus.Accepted) { + if (res.tosStatus?.isAccepted() == true) { onCreateInvoice( amount, subject, @@ -268,7 +268,7 @@ fun OutgoingPullComposable( } else onTosAccept(ex) }, ) { - if (checkResult != null && checkResult?.tosStatus != ExchangeTosStatus.Accepted) { + if (checkResult != null && !checkResult!!.tosStatus!!.isAccepted()) { Text(text = stringResource(R.string.exchange_tos_view)) } else { Text(text = stringResource(R.string.receive_peer_create_button_amount, diff --git a/wallet/src/main/java/net/taler/wallet/peer/PeerManager.kt b/wallet/src/main/java/net/taler/wallet/peer/PeerManager.kt @@ -108,7 +108,7 @@ class PeerManager( _outgoingPullState.value = OutgoingChecking } - if (exchangeItem.tosStatus != ExchangeTosStatus.Accepted) { + if (!exchangeItem.tosStatus.isAccepted()) { _outgoingPullState.value = OutgoingIntro return CheckPeerPullCreditResult( tosStatus = exchangeItem.tosStatus, @@ -320,7 +320,7 @@ class PeerManager( return@b } - _incomingPushState.value = if (exchange.tosStatus == ExchangeTosStatus.Accepted) { + _incomingPushState.value = if (exchange.tosStatus.isAccepted()) { IncomingTerms( amountRaw = response.amountRaw, amountEffective = response.amountEffective, @@ -364,7 +364,7 @@ class PeerManager( var newState = state if (state is IncomingTosReview) { exchanges.find { it.exchangeBaseUrl == state.exchangeBaseUrl }?.let { exchange -> - if (exchange.tosStatus == ExchangeTosStatus.Accepted) { + if (exchange.tosStatus.isAccepted()) { newState = IncomingTerms( amountRaw = state.amountRaw, amountEffective = state.amountEffective, @@ -386,7 +386,7 @@ class PeerManager( var newState = state if (state is OutgoingChecked) { exchanges.find { it.exchangeBaseUrl == state.exchangeBaseUrl }?.let { exchange -> - if (exchange.tosStatus == ExchangeTosStatus.Accepted) { + if (exchange.tosStatus.isAccepted()) { newState = OutgoingChecked( amountRaw = state.amountRaw, amountEffective = state.amountEffective, diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/ReviewExchangeTosFragment.kt b/wallet/src/main/java/net/taler/wallet/withdraw/ReviewExchangeTosFragment.kt @@ -45,6 +45,8 @@ import net.taler.common.fadeOut import net.taler.wallet.MainViewModel import net.taler.wallet.R import net.taler.wallet.databinding.FragmentReviewExchangeTosBinding +import net.taler.wallet.exchanges.ExchangeTosStatus +import net.taler.wallet.showError import java.text.ParseException import java.util.Locale @@ -114,8 +116,14 @@ class ReviewExchangeTosFragment : Fragment(), AdapterView.OnItemSelectedListener selectedLang = language ?: lc tos = exchangeManager.getExchangeTos(exchangeBaseUrl, selectedLang) + val tos = tos + if (tos == null || tos.status == ExchangeTosStatus.MissingTos) { + onTosError(getString(R.string.exchange_tos_missing)) + return + } + // Setup language adapter - val languages = tos?.tosAvailableLanguages ?: emptyList() + val languages = tos.tosAvailableLanguages langAdapter?.clear() langAdapter?.addAll(languages.map { lang -> Locale(lang).displayLanguage @@ -134,21 +142,19 @@ class ReviewExchangeTosFragment : Fragment(), AdapterView.OnItemSelectedListener } // FIXME: better null handling! - tos?.let { - val sections = try { - parseTos(markwon, it.content) - } catch (e: ParseException) { - onTosError(e.message ?: "Unknown Error") - return - } + val sections = try { + parseTos(markwon, tos.content) + } catch (e: ParseException) { + onTosError(e.message ?: "Unknown Error") + return + } - adapter.setSections(sections) - ui.tosList.adapter = adapter - ui.tosList.fadeIn() + adapter.setSections(sections) + ui.tosList.adapter = adapter + ui.tosList.fadeIn() - ui.acceptTosCheckBox.fadeIn() - ui.progressBar.fadeOut() - } + ui.acceptTosCheckBox.fadeIn() + ui.progressBar.fadeOut() } private fun setupInsets() { diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/TosSection.kt b/wallet/src/main/java/net/taler/wallet/withdraw/TosSection.kt @@ -17,7 +17,9 @@ package net.taler.wallet.withdraw import io.noties.markwon.Markwon +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import net.taler.wallet.exchanges.ExchangeTosStatus import org.commonmark.node.Code import org.commonmark.node.Document import org.commonmark.node.Heading @@ -81,6 +83,7 @@ private fun getNodeText(rootNode: Node): String { @Serializable data class TosResponse( + val status: ExchangeTosStatus, val content: String, val currentEtag: String, val contentLanguage: String? = null, diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt b/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt @@ -308,7 +308,7 @@ class WithdrawManager( val exchangeBaseUrl = details.info.defaultExchangeBaseUrl ?: details.info.possibleExchanges.firstOrNull()?.exchangeBaseUrl - + // Handle no exchanges configured by bank. if (exchangeBaseUrl == null) { _withdrawStatus.updateAndGet { value -> @@ -417,7 +417,7 @@ class WithdrawManager( scope.launch { _withdrawStatus.update { value -> updateSelections(value.copy( - status = if (exchange.tosStatus != ExchangeTosStatus.Accepted) { + status = if (!exchange.tosStatus.isAccepted()) { TosReviewRequired } else { InfoReceived @@ -472,7 +472,7 @@ class WithdrawManager( var newStatus = status status.exchangeBaseUrl?.let { exchangeBaseUrl -> exchanges.find { it.exchangeBaseUrl == exchangeBaseUrl }?.let { exchange -> - if (exchange.tosStatus == ExchangeTosStatus.Accepted) { + if (exchange.tosStatus.isAccepted()) { newStatus = status.copy(status = InfoReceived) } } diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml @@ -423,6 +423,7 @@ GNU Taler is immune to many types of fraud such as credit card data theft, phish <string name="exchange_tos">Terms of service</string> <string name="exchange_tos_accept">Accept terms of service</string> <string name="exchange_tos_forget">Reject terms of service</string> + <string name="exchange_tos_missing">Terms of service were not provided by payment service</string> <string name="exchange_tos_view">Review terms of service</string> <string name="exchange_tos_error">Error showing terms of service: %1$s</string>