taler-android

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

commit 0f816b80f13b6061720aa036f5e0e4f9cb3f2589
parent 2e99e3afa708639cf9e11125e39ca62e81a382ba
Author: Iván Ávalos <avalos@disroot.org>
Date:   Mon, 29 Jul 2024 22:46:52 +0200

[wallet] improvements and fixes to ToS review flow

Diffstat:
Mwallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt | 55+++++++++++++++++++++++++++++++++++--------------------
Mwallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt | 42++++++++++++++++++++++++++++++------------
2 files changed, 65 insertions(+), 32 deletions(-)

diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt b/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt @@ -97,6 +97,8 @@ class PromptWithdrawFragment: Fragment() { private val selectExchangeDialog = SelectExchangeDialogFragment() + private var startup: Boolean = true + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -115,9 +117,9 @@ class PromptWithdrawFragment: Fragment() { } when (s.status) { - None, Loading, TosReviewRequired -> LoadingScreen() + None, Loading -> LoadingScreen() - InfoReceived, Updating -> { + InfoReceived, TosReviewRequired, Updating -> { val spec = remember(s) { defaultExchange?.scopeInfo?.let { scopeInfo -> balanceManager.getSpecForScopeInfo(scopeInfo) @@ -138,9 +140,15 @@ class PromptWithdrawFragment: Fragment() { loading = false, ) }, + onTosReview = { + // TODO: rewrite ToS review screen in compose + findNavController().navigate( + R.id.action_promptWithdraw_to_reviewExchangeTOS, + ) + }, onConfirm = { age -> withdrawManager.acceptWithdrawal(age) - } + }, ) } else -> {} @@ -149,8 +157,8 @@ class PromptWithdrawFragment: Fragment() { } LaunchedEffect(Unit) { + val s = status coroutineScope.launch { - val s = status if (s.uriInfo?.amount == null && s.uriInfo?.defaultExchangeBaseUrl != null) { defaultExchange = exchangeManager.findExchangeByUrl(s.uriInfo.defaultExchangeBaseUrl) } @@ -175,10 +183,12 @@ class PromptWithdrawFragment: Fragment() { } when (status.status) { - // TODO: rewrite ToS review screen in compose - TosReviewRequired -> { - findNavController().navigate( - R.id.action_promptWithdraw_to_reviewExchangeTOS, + InfoReceived -> if (startup) { // only fire at startup + startup = false + withdrawManager.getWithdrawalDetails( + amount = status.amountInfo?.amountRaw ?: status.uriInfo?.amount, + exchangeBaseUrl = status.exchangeBaseUrl ?: status.uriInfo?.defaultExchangeBaseUrl, + loading = true, ) } @@ -228,9 +238,10 @@ fun WithdrawalShowInfo( spec: CurrencySpecification?, onSelectAmount: (amount: Amount) -> Unit, onSelectExchange: () -> Unit, + onTosReview: () -> Unit, onConfirm: (age: Int?) -> Unit, ) { - val defaultAmount = status.uriInfo?.amount + val defaultAmount = status.amountInfo?.amountRaw ?: status.uriInfo?.amount val maxAmount = status.uriInfo?.maxAmount val editableAmount = status.uriInfo?.editableAmount ?: false val wireFee = status.uriInfo?.wireFee ?: Amount.zero(currency) @@ -238,16 +249,17 @@ fun WithdrawalShowInfo( val possibleExchanges = status.uriInfo?.possibleExchanges ?: emptyList() val ageRestrictionOptions = status.amountInfo?.ageRestrictionOptions ?: emptyList() + var startup by remember { mutableStateOf(true) } var selectedAmount by remember { mutableStateOf(defaultAmount) } var selectedAge by remember { mutableStateOf<Int?>(null) } var error by remember { mutableStateOf(false) } val scrollState = rememberScrollState() selectedAmount.useDebounce { - it?.let { amount -> - if (editableAmount) { - onSelectAmount(amount) - } + if (startup) { // do not fire at startup + startup = false + } else it?.let { + onSelectAmount(it) } } @@ -374,15 +386,17 @@ fun WithdrawalShowInfo( && status.status != Updating && selectedAmount?.let { !it.isZero() } == true, onClick = { - selectedAmount?.let { onConfirm(selectedAge) } + if (status.status == TosReviewRequired) { + onTosReview() + } else selectedAmount?.let { + onConfirm(selectedAge) + } }, ) { - if (status.status == Updating) { - CircularProgressIndicator( - modifier = Modifier.size(15.dp) - ) - } else { - Text(stringResource(R.string.withdraw_button_confirm)) + when (status.status) { + Updating -> CircularProgressIndicator(modifier = Modifier.size(15.dp)) + TosReviewRequired -> Text(stringResource(R.string.withdraw_button_tos)) + else -> Text(stringResource(R.string.withdraw_button_confirm)) } } } @@ -507,6 +521,7 @@ fun WithdrawalShowInfoPreview() { spec = null, onSelectExchange = {}, onSelectAmount = {}, + onTosReview = {}, onConfirm = {}, ) } diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt b/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt @@ -272,8 +272,16 @@ class WithdrawManager( val status = _withdrawStatus.getAndUpdate { value -> value.copy(status = if (loading) Loading else Updating) } - val exchangeBaseUrl2 = exchangeBaseUrl ?: status.exchangeBaseUrl!! - val amount2 = amount?.toJSONString() ?: status.amountInfo!!.amountRaw.toJSONString() + + val exchangeBaseUrl2 = exchangeBaseUrl + ?: status.exchangeBaseUrl + ?: error("no exchangeBaseUrl") + + val amount2 = amount?.toJSONString() + ?: status.uriInfo?.amount?.toJSONString() + ?: status.amountInfo?.amountRaw?.toJSONString() + ?: error("no amount") + api.request("getWithdrawalDetailsForAmount", WithdrawalDetailsForAmount.serializer()) { put("exchangeBaseUrl", exchangeBaseUrl2) put("amount", amount2) @@ -285,12 +293,15 @@ class WithdrawManager( value.copy( status = InfoReceived, exchangeBaseUrl = exchangeBaseUrl2, - uriInfo = uriInfo, + uriInfo = uriInfo ?: value.uriInfo, amountInfo = details, currency = details.amountRaw.currency, ) } - } else getExchangeTos(exchangeBaseUrl2) + } else getExchangeTos( + exchangeBaseUrl2, + amountInfo = details, + ) } } @@ -310,6 +321,7 @@ class WithdrawManager( private fun getExchangeTos( exchangeBaseUrl: String, + amountInfo: WithdrawalDetailsForAmount? = null, ) = scope.launch { api.request("getExchangeTos", TosResponse.serializer()) { put("exchangeBaseUrl", exchangeBaseUrl) @@ -319,6 +331,7 @@ class WithdrawManager( _withdrawStatus.update { value -> value.copy( status = TosReviewRequired, + amountInfo = amountInfo ?: value.amountInfo, tosDetails = tos, ) } @@ -329,8 +342,11 @@ class WithdrawManager( * Accept the currently displayed terms of service. */ fun acceptCurrentTos() = scope.launch { - val exchangeBaseUrl = withdrawStatus.value.exchangeBaseUrl!! - val tos = withdrawStatus.value.tosDetails!! + 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) @@ -360,9 +376,10 @@ class WithdrawManager( status: WithdrawStatus, restrictAge: Int? = null, ) { - val exchangeBaseUrl = status.exchangeBaseUrl!! - val talerWithdrawUri = status.talerWithdrawUri!! - val amountInfo = status.amountInfo!! + val exchangeBaseUrl = status.exchangeBaseUrl ?: error("no exchangeBaseUrl") + val talerWithdrawUri = status.talerWithdrawUri ?: error("no talerWithdrawUri") + val amountInfo = status.amountInfo ?: error("no amountInfo") + api.request("acceptBankIntegratedWithdrawal", AcceptWithdrawalResponse.serializer()) { restrictAge?.let { put("restrictAge", it) } put("exchangeBaseUrl", exchangeBaseUrl) @@ -384,8 +401,9 @@ class WithdrawManager( status: WithdrawStatus, restrictAge: Int? = null, ) { - val exchangeBaseUrl = status.exchangeBaseUrl!! - val amountInfo = status.amountInfo!! + val exchangeBaseUrl = status.exchangeBaseUrl ?: error("no exchangeBaseUrl") + val amountInfo = status.amountInfo ?: error("no amountInfo") + api.request("acceptManualWithdrawal", AcceptManualWithdrawalResponse.serializer()) { restrictAge?.let { put("restrictAge", it) } put("exchangeBaseUrl", exchangeBaseUrl) @@ -458,7 +476,7 @@ class WithdrawManager( status = ManualTransferRequired, manualTransferResponse = response, withdrawalTransfers = response.withdrawalAccountsList.mapNotNull { - val details = status.amountInfo!! + val details = status.amountInfo ?: error("no amountInfo") val uri = Uri.parse(it.paytoUri.replace("receiver-name=", "receiver_name=")) if ("bitcoin".equals(uri.authority, true)) { val msg = uri.getQueryParameter("message").orEmpty()