taler-android

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

commit 0e0ef73c033989b5d0b46196247e8cfc2071fde7
parent baa673c4242ccff7c69ce17833b2ab6f35860e35
Author: Iván Ávalos <avalos@disroot.org>
Date:   Fri, 13 Sep 2024 20:05:03 +0200

[wallet] WIP: accept ToS for incoming push payments (+refactoring)

bug 0009157

Diffstat:
Mwallet/src/main/java/net/taler/wallet/MainViewModel.kt | 2+-
Mwallet/src/main/java/net/taler/wallet/exchanges/ExchangeManager.kt | 46++++++++++++++++++++++++++++++++++++++--------
Mwallet/src/main/java/net/taler/wallet/exchanges/Exchanges.kt | 19+++++++++++++++++++
Mwallet/src/main/java/net/taler/wallet/peer/IncomingComposable.kt | 6+++++-
Mwallet/src/main/java/net/taler/wallet/peer/IncomingPushPaymentFragment.kt | 56++++++++++++++++++++++++++++++++++++++++++--------------
Mwallet/src/main/java/net/taler/wallet/peer/IncomingState.kt | 29+++++++++++++++++++++++------
Mwallet/src/main/java/net/taler/wallet/peer/OutgoingPullComposable.kt | 3++-
Mwallet/src/main/java/net/taler/wallet/peer/PeerManager.kt | 50++++++++++++++++++++++++++++++++++++++++++++------
Mwallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt | 18+++++++++++++++---
Mwallet/src/main/java/net/taler/wallet/withdraw/ReviewExchangeTosFragment.kt | 61+++++++++++++++++++++++++++++++------------------------------
Mwallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt | 88++++++++++++++++++++++++++++++++-----------------------------------------------
Mwallet/src/main/res/navigation/nav_graph.xml | 3+++
12 files changed, 259 insertions(+), 122 deletions(-)

diff --git a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt @@ -110,12 +110,12 @@ class MainViewModel( private val api = WalletBackendApi(app, walletConfig, this, this) val networkManager = NetworkManager(app.applicationContext) - val withdrawManager = WithdrawManager(api, viewModelScope) val paymentManager = PaymentManager(api, viewModelScope) val transactionManager: TransactionManager = TransactionManager(api, viewModelScope) val refundManager = RefundManager(api, viewModelScope) val balanceManager = BalanceManager(api, viewModelScope) val exchangeManager: ExchangeManager = ExchangeManager(api, viewModelScope) + val withdrawManager = WithdrawManager(api, viewModelScope, exchangeManager) val peerManager: PeerManager = PeerManager(api, exchangeManager, viewModelScope) val settingsManager: SettingsManager = SettingsManager(app.applicationContext, api, viewModelScope) val accountManager: AccountManager = AccountManager(api, viewModelScope) diff --git a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeManager.kt b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeManager.kt @@ -31,17 +31,13 @@ import net.taler.common.toEvent import net.taler.wallet.TAG import net.taler.wallet.backend.TalerErrorInfo import net.taler.wallet.backend.WalletBackendApi +import net.taler.wallet.withdraw.TosResponse @Serializable data class ExchangeListResponse( val exchanges: List<ExchangeItem>, ) -@Serializable -data class ExchangeDetailedResponse( - val exchange: ExchangeItem, -) - class ExchangeManager( private val api: WalletBackendApi, private val scope: CoroutineScope, @@ -150,16 +146,50 @@ class ExchangeManager( @WorkerThread suspend fun findExchangeByUrl(exchangeUrl: String): ExchangeItem? { var exchange: ExchangeItem? = null - api.request("getExchangeDetailedInfo", ExchangeDetailedResponse.serializer()) { + api.request("getExchangeEntryByUrl", ExchangeItem.serializer()) { put("exchangeBaseUrl", exchangeUrl) }.onError { - Log.e(TAG, "Error getExchangeDetailedInfo: $it") + Log.e(TAG, "Error getExchangeEntryByUrl: $it") }.onSuccess { - exchange = it.exchange + exchange = it } return exchange } + /** + * Fetch exchange terms of service. + */ + suspend fun getExchangeTos(exchangeBaseUrl: String): TosResponse? { + var result: TosResponse? = null + api.request("getExchangeTos", TosResponse.serializer()) { + put("exchangeBaseUrl", exchangeBaseUrl) + }.onError { error -> + Log.d(TAG, "Error getExchangeTos: $error") + }.onSuccess { + result = it + } + return result + } + + /** + * Accept the currently displayed terms of service. + */ + suspend fun acceptCurrentTos( + exchangeBaseUrl: String, + currentEtag: String, + ): Boolean { + var success = false + api.request<Unit>("setExchangeTosAccepted") { + put("exchangeBaseUrl", exchangeBaseUrl) + put("etag", currentEtag) + }.onError { error -> + Log.d(TAG, "Error setExchangeTosAccepted: $error") + }.onSuccess { + success = true + } + return success + } + fun addDevExchanges() { scope.launch { listOf( diff --git a/wallet/src/main/java/net/taler/wallet/exchanges/Exchanges.kt b/wallet/src/main/java/net/taler/wallet/exchanges/Exchanges.kt @@ -16,6 +16,7 @@ package net.taler.wallet.exchanges +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import net.taler.wallet.balances.ScopeInfo import net.taler.wallet.cleanExchange @@ -33,6 +34,24 @@ data class ExchangeItem( val currency: String? = null, val paytoUris: List<String>, val scopeInfo: ScopeInfo? = null, + val tosStatus: ExchangeTosStatus, ) { val name: String get() = cleanExchange(exchangeBaseUrl) +} + +@Serializable +enum class ExchangeTosStatus { + Unknown, + + @SerialName("pending") + Pending, + + @SerialName("proposed") + Proposed, + + @SerialName("accepted") + Accepted, + + @SerialName("missing-tos") + MissingTos, } \ No newline at end of file diff --git a/wallet/src/main/java/net/taler/wallet/peer/IncomingComposable.kt b/wallet/src/main/java/net/taler/wallet/peer/IncomingComposable.kt @@ -183,7 +183,11 @@ fun ColumnScope.PeerPullTermsComposable( onClick = { onAccept(terms) }, ) { Text( - text = stringResource(id = data.button), + text = if (terms is IncomingTosReview) { + stringResource(id = data.button) + } else { + stringResource(id = R.string.exchange_tos_accept) + }, ) } } diff --git a/wallet/src/main/java/net/taler/wallet/peer/IncomingPushPaymentFragment.kt b/wallet/src/main/java/net/taler/wallet/peer/IncomingPushPaymentFragment.kt @@ -17,17 +17,23 @@ package net.taler.wallet.peer import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.compose.ui.platform.ComposeView +import androidx.core.os.bundleOf import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.fragment.findNavController +import kotlinx.coroutines.launch import net.taler.common.showError import net.taler.wallet.MainViewModel import net.taler.wallet.R +import net.taler.wallet.TAG import net.taler.wallet.compose.TalerSurface import net.taler.wallet.compose.collectAsStateLifecycleAware import net.taler.wallet.showError @@ -35,31 +41,44 @@ import net.taler.wallet.showError class IncomingPushPaymentFragment : Fragment() { private val model: MainViewModel by activityViewModels() private val peerManager get() = model.peerManager + private val exchangeManager get() = model.exchangeManager override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View { - lifecycleScope.launchWhenResumed { - peerManager.incomingPushState.collect { - if (it is IncomingAccepted) { - findNavController().navigate(R.id.action_promptPushPayment_to_nav_main) - } else if (it is IncomingError) { - if (model.devMode.value == true) { - showError(it.info) - } else { - showError(it.info.userFacingMsg) - } - } - } - } return ComposeView(requireContext()).apply { setContent { TalerSurface { val state = peerManager.incomingPushState.collectAsStateLifecycleAware() IncomingComposable(state, incomingPush) { terms -> - peerManager.confirmPeerPushCredit(terms) + if (terms is IncomingTosReview) { + val args = bundleOf("exchangeBaseUrl" to terms.exchangeBaseUrl) + findNavController().navigate(R.id.action_promptPushPayment_to_reviewExchangeTOS, args) + } else { + peerManager.confirmPeerPushCredit(terms) + } + } + } + } + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + lifecycleScope.launch { + viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + peerManager.incomingPushState.collect { + Log.d(TAG, "incomingPushState is $it") + if (it is IncomingAccepted) { + findNavController().navigate(R.id.action_promptPushPayment_to_nav_main) + } else if (it is IncomingError) { + if (model.devMode.value == true) { + showError(it.info) + } else { + showError(it.info.userFacingMsg) + } } } } @@ -70,4 +89,13 @@ class IncomingPushPaymentFragment : Fragment() { super.onStart() activity?.setTitle(R.string.receive_peer_payment_title) } + + override fun onResume() { + super.onResume() + // FIXME: not sure that this is the best approach! + exchangeManager.exchanges.observe(viewLifecycleOwner) { + // detect ToS acceptation + peerManager.refreshPeerPushCreditTos() + } + } } diff --git a/wallet/src/main/java/net/taler/wallet/peer/IncomingState.kt b/wallet/src/main/java/net/taler/wallet/peer/IncomingState.kt @@ -21,18 +21,34 @@ import net.taler.common.Amount import net.taler.wallet.backend.TalerErrorInfo sealed class IncomingState -object IncomingChecking : IncomingState() + +data object IncomingChecking : IncomingState() + open class IncomingTerms( - val amountRaw: Amount, - val amountEffective: Amount, - val contractTerms: PeerContractTerms, - val id: String, + open val amountRaw: Amount, + open val amountEffective: Amount, + open val contractTerms: PeerContractTerms, + open val id: String, ) : IncomingState() +class IncomingTosReview( + override val amountRaw: Amount, + override val amountEffective: Amount, + override val contractTerms: PeerContractTerms, + val exchangeBaseUrl: String, + override val id: String, +) : IncomingTerms( + amountRaw = amountRaw, + amountEffective = amountEffective, + contractTerms = contractTerms, + id = id, +) + class IncomingAccepting(s: IncomingTerms) : IncomingTerms(s.amountRaw, s.amountEffective, s.contractTerms, s.id) -object IncomingAccepted : IncomingState() +data object IncomingAccepted : IncomingState() + data class IncomingError( val info: TalerErrorInfo, ) : IncomingState() @@ -57,4 +73,5 @@ data class PreparePeerPushCreditResponse( val amountRaw: Amount, val amountEffective: Amount, val transactionId: String, + val exchangeBaseUrl: String, ) diff --git a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullComposable.kt b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullComposable.kt @@ -56,6 +56,7 @@ import net.taler.wallet.backend.TalerErrorInfo import net.taler.wallet.cleanExchange import net.taler.wallet.compose.TalerSurface import net.taler.wallet.exchanges.ExchangeItem +import net.taler.wallet.exchanges.ExchangeTosStatus import net.taler.wallet.transactions.AmountType import net.taler.wallet.transactions.TransactionAmountComposable import net.taler.wallet.transactions.TransactionInfoComposable @@ -257,7 +258,7 @@ fun PeerPullComposableCheckedPreview() { TalerSurface { val amountRaw = Amount.fromString("TESTKUDOS", "42.42") val amountEffective = Amount.fromString("TESTKUDOS", "42.23") - val exchangeItem = ExchangeItem("https://example.org", "TESTKUDOS", emptyList()) + val exchangeItem = ExchangeItem("https://example.org", "TESTKUDOS", emptyList(), null, ExchangeTosStatus.Accepted) OutgoingPullComposable( amount = Amount.fromString("TESTKUDOS", "42.23"), state = OutgoingChecked(amountRaw, amountEffective, exchangeItem), diff --git a/wallet/src/main/java/net/taler/wallet/peer/PeerManager.kt b/wallet/src/main/java/net/taler/wallet/peer/PeerManager.kt @@ -17,10 +17,12 @@ package net.taler.wallet.peer import android.util.Log +import androidx.annotation.UiThread import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json @@ -35,6 +37,7 @@ import net.taler.wallet.backend.TalerErrorInfo import net.taler.wallet.backend.WalletBackendApi import net.taler.wallet.exchanges.ExchangeItem import net.taler.wallet.exchanges.ExchangeManager +import net.taler.wallet.exchanges.ExchangeTosStatus import org.json.JSONObject import java.util.concurrent.TimeUnit.HOURS @@ -238,12 +241,27 @@ class PeerManager( api.request("preparePeerPushCredit", PreparePeerPushCreditResponse.serializer()) { put("talerUri", talerUri) }.onSuccess { response -> - _incomingPushState.value = IncomingTerms( - amountRaw = response.amountRaw, - amountEffective = response.amountEffective, - contractTerms = response.contractTerms, - id = response.transactionId, - ) + scope.launch(Dispatchers.IO) { + // FIXME: make sure that exchange gets added to wallet! handle null properly! + exchangeManager.findExchangeByUrl(response.exchangeBaseUrl)?.let { exchange -> + _incomingPushState.value = if (exchange.tosStatus == ExchangeTosStatus.Accepted) { + IncomingTerms( + amountRaw = response.amountRaw, + amountEffective = response.amountEffective, + contractTerms = response.contractTerms, + id = response.transactionId, + ) + } else { + IncomingTosReview( + amountRaw = response.amountRaw, + amountEffective = response.amountEffective, + contractTerms = response.contractTerms, + exchangeBaseUrl = response.exchangeBaseUrl, + id = response.transactionId, + ) + } + } + } }.onError { error -> Log.e(TAG, "got preparePeerPushCredit error result $error") _incomingPushState.value = IncomingError(error) @@ -265,4 +283,24 @@ class PeerManager( } } + @UiThread + fun refreshPeerPushCreditTos() = scope.launch { + _incomingPushState.update { state -> + var newState = state + if (state is IncomingTosReview) { + // FIXME: better null handling! + exchangeManager.findExchangeByUrl(state.exchangeBaseUrl)?.let { exchange -> + if (exchange.tosStatus == ExchangeTosStatus.Accepted) { + newState = IncomingTerms( + amountRaw = state.amountRaw, + amountEffective = state.amountEffective, + contractTerms = state.contractTerms, + id = state.id, + ) + } + } + } + newState + } + } } diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt b/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt @@ -55,6 +55,7 @@ import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.core.os.bundleOf import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.Lifecycle @@ -79,6 +80,7 @@ import net.taler.wallet.compose.LoadingScreen import net.taler.wallet.compose.TalerSurface import net.taler.wallet.compose.collectAsStateLifecycleAware import net.taler.wallet.exchanges.ExchangeItem +import net.taler.wallet.exchanges.ExchangeTosStatus import net.taler.wallet.exchanges.SelectExchangeDialogFragment import net.taler.wallet.getAmount import net.taler.wallet.showError @@ -146,9 +148,8 @@ class PromptWithdrawFragment: Fragment() { }, onTosReview = { // TODO: rewrite ToS review screen in compose - findNavController().navigate( - R.id.action_promptWithdraw_to_reviewExchangeTOS, - ) + val args = bundleOf("exchangeBaseUrl" to s.exchangeBaseUrl) + findNavController().navigate(R.id.action_promptWithdraw_to_reviewExchangeTOS, args) }, onConfirm = { age -> withdrawManager.acceptWithdrawal(age) @@ -224,6 +225,15 @@ class PromptWithdrawFragment: Fragment() { }) } + override fun onResume() { + super.onResume() + // FIXME: not sure this is an ultra reliable approach! + exchangeManager.exchanges.observe(viewLifecycleOwner) { + // detect ToS acceptation + withdrawManager.refreshTosStatus() + } + } + private fun selectExchange() { val exchanges = withdrawManager.withdrawStatus.value.uriInfo?.possibleExchanges ?: return selectExchangeDialog.setExchanges(exchanges) @@ -502,12 +512,14 @@ fun WithdrawalShowInfoPreview() { currency = "KUDOS", paytoUris = emptyList(), scopeInfo = null, + tosStatus = ExchangeTosStatus.Accepted, ), ExchangeItem( exchangeBaseUrl = "exchange.head.taler.net", currency = "KUDOS", paytoUris = emptyList(), scopeInfo = null, + tosStatus = ExchangeTosStatus.Accepted, ), ), ), diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/ReviewExchangeTosFragment.kt b/wallet/src/main/java/net/taler/wallet/withdraw/ReviewExchangeTosFragment.kt @@ -34,17 +34,18 @@ import net.taler.wallet.MainViewModel import net.taler.wallet.R import net.taler.wallet.databinding.FragmentReviewExchangeTosBinding import java.text.ParseException -import net.taler.wallet.withdraw.WithdrawStatus.Status.* class ReviewExchangeTosFragment : Fragment() { private val model: MainViewModel by activityViewModels() - private val withdrawManager by lazy { model.withdrawManager } + private val exchangeManager by lazy { model.exchangeManager } private lateinit var ui: FragmentReviewExchangeTosBinding private val markwon by lazy { Markwon.builder(requireContext()).build() } private val adapter by lazy { TosAdapter(markwon) } + private var tos: TosResponse? = null + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -56,42 +57,42 @@ class ReviewExchangeTosFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + + val exchangeBaseUrl = arguments?.getString("exchangeBaseUrl") + ?: error("no exchangeBaseUrl passed") + ui.acceptTosCheckBox.isChecked = false ui.acceptTosCheckBox.setOnCheckedChangeListener { _, _ -> - withdrawManager.acceptCurrentTos() + tos?.let { + viewLifecycleOwner.lifecycleScope.launch { + if (exchangeManager.acceptCurrentTos( + exchangeBaseUrl = exchangeBaseUrl, + currentEtag = it.currentEtag, + )) { + findNavController().navigateUp() + } + } + } } viewLifecycleOwner.lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { - withdrawManager.withdrawStatus.collect { status -> - when (status.status) { - TosReviewRequired -> { - val tos = status.tosDetails!!.content - val sections = try { - parseTos(markwon, tos) - } catch (e: ParseException) { - onTosError(e.message ?: "Unknown Error") - return@collect - } - - adapter.setSections(sections) - ui.tosList.adapter = adapter - ui.tosList.fadeIn() - - ui.acceptTosCheckBox.fadeIn() - ui.progressBar.fadeOut() - } - - Loading -> { - findNavController().navigate(R.id.action_reviewExchangeTOS_to_promptWithdraw) - } + tos = exchangeManager.getExchangeTos(exchangeBaseUrl) + // FIXME: better null handling! + tos?.let { + val sections = try { + parseTos(markwon, it.content) + } catch (e: ParseException) { + onTosError(e.message ?: "Unknown Error") + return@repeatOnLifecycle + } - InfoReceived -> { - findNavController().navigate(R.id.action_reviewExchangeTOS_to_promptWithdraw) - } + adapter.setSections(sections) + ui.tosList.adapter = adapter + ui.tosList.fadeIn() - else -> {} - } + ui.acceptTosCheckBox.fadeIn() + ui.progressBar.fadeOut() } } } diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt b/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt @@ -39,6 +39,8 @@ import net.taler.wallet.backend.WalletBackendApi import net.taler.wallet.balances.ScopeInfo import net.taler.wallet.exchanges.ExchangeFees import net.taler.wallet.exchanges.ExchangeItem +import net.taler.wallet.exchanges.ExchangeManager +import net.taler.wallet.exchanges.ExchangeTosStatus import net.taler.wallet.transactions.WithdrawalExchangeAccountDetails import net.taler.wallet.withdraw.WithdrawStatus.Status.* @@ -62,7 +64,6 @@ data class WithdrawStatus( val currency: String? = null, val uriInfo: WithdrawalDetailsForUri? = null, val amountInfo: WithdrawalDetailsForAmount? = null, - val tosDetails: TosResponse? = null, // manual transfer val manualTransferResponse: AcceptManualWithdrawalResponse? = null, @@ -207,6 +208,7 @@ data class QrCodeSpec( class WithdrawManager( private val api: WalletBackendApi, private val scope: CoroutineScope, + private val exchangeManager: ExchangeManager, ) { private val _withdrawStatus = MutableStateFlow(WithdrawStatus()) val withdrawStatus: StateFlow<WithdrawStatus> = _withdrawStatus.asStateFlow() @@ -288,20 +290,29 @@ class WithdrawManager( }.onError { error -> handleError("getWithdrawalDetailsForAmount", error) }.onSuccess { details -> - if (details.tosAccepted) { - _withdrawStatus.update { value -> - value.copy( - status = InfoReceived, - exchangeBaseUrl = exchangeBaseUrl2, - uriInfo = uriInfo ?: value.uriInfo, - amountInfo = details, - currency = details.amountRaw.currency, - ) + scope.launch { + val exchange = exchangeManager.findExchangeByUrl(exchangeBaseUrl2) + if (exchange?.tosStatus == ExchangeTosStatus.Accepted) { + _withdrawStatus.update { value -> + value.copy( + status = InfoReceived, + exchangeBaseUrl = exchangeBaseUrl2, + uriInfo = uriInfo ?: value.uriInfo, + amountInfo = details, + currency = details.amountRaw.currency, + ) + } + } else { + _withdrawStatus.update { value -> + value.copy( + status = TosReviewRequired, + amountInfo = details, + currency = details.amountRaw.currency, + exchangeBaseUrl = exchangeBaseUrl, + ) + } } - } else getExchangeTos( - exchangeBaseUrl2, - amountInfo = details, - ) + } } } @@ -319,45 +330,18 @@ class WithdrawManager( return response } - private fun getExchangeTos( - exchangeBaseUrl: String, - amountInfo: WithdrawalDetailsForAmount? = null, - ) = scope.launch { - api.request("getExchangeTos", TosResponse.serializer()) { - put("exchangeBaseUrl", exchangeBaseUrl) - }.onError { - handleError("getExchangeTos", it) - }.onSuccess { tos -> - _withdrawStatus.update { value -> - value.copy( - status = TosReviewRequired, - amountInfo = amountInfo ?: value.amountInfo, - tosDetails = tos, - currency = amountInfo?.amountRaw?.currency, - exchangeBaseUrl = exchangeBaseUrl, - ) - } - } - } - - /** - * Accept the currently displayed terms of service. - */ - fun acceptCurrentTos() = scope.launch { - val exchangeBaseUrl = withdrawStatus.value.exchangeBaseUrl - ?: error("no exchangeBaseUrl") - val tos = withdrawStatus.value.tosDetails - ?: error("no tosDetails") - - api.request<Unit>("setExchangeTosAccepted") { - put("exchangeBaseUrl", exchangeBaseUrl) - put("etag", tos.currentEtag) - }.onError { error -> - handleError("setExchangeTosAccepted", error) - }.onSuccess { - _withdrawStatus.update { value -> - value.copy(status = InfoReceived) + @UiThread + fun refreshTosStatus() = scope.launch { + _withdrawStatus.update { status -> + var newStatus = status + status.exchangeBaseUrl?.let { exchangeBaseUrl -> + exchangeManager.findExchangeByUrl(exchangeBaseUrl)?.let { exchange -> + if (exchange.tosStatus == ExchangeTosStatus.Accepted) { + newStatus = status.copy(status = InfoReceived) + } + } } + newStatus } } diff --git a/wallet/src/main/res/navigation/nav_graph.xml b/wallet/src/main/res/navigation/nav_graph.xml @@ -263,6 +263,9 @@ android:id="@+id/action_promptPushPayment_to_nav_main" app:destination="@id/nav_main" app:popUpTo="@id/nav_main" /> + <action + android:id="@+id/action_promptPushPayment_to_reviewExchangeTOS" + app:destination="@id/reviewExchangeTOS" /> </fragment> <fragment