From 6e99207a7b525536240f32f04f1bb5457f969025 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Wed, 22 Feb 2023 13:33:18 -0300 Subject: [wallet] show fees for peer push debit --- .../java/net/taler/wallet/SendFundsFragment.kt | 10 ++--- .../wallet/peer/OutgoingPullResultComposable.kt | 4 +- .../net/taler/wallet/peer/OutgoingPushFragment.kt | 21 +++++---- .../wallet/peer/OutgoingPushIntroComposable.kt | 51 +++++++++++++--------- .../wallet/peer/OutgoingPushResultComposable.kt | 4 +- .../java/net/taler/wallet/peer/OutgoingState.kt | 12 +++++ .../main/java/net/taler/wallet/peer/PeerManager.kt | 17 ++++++++ 7 files changed, 81 insertions(+), 38 deletions(-) diff --git a/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt b/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt index 7ae7773..c2680d5 100644 --- a/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt @@ -82,18 +82,14 @@ class SendFundsFragment : Fragment() { activity?.setTitle(R.string.transactions_send_funds) } - override fun onDestroy() { - super.onDestroy() - if (!requireActivity().isChangingConfigurations) peerManager.resetPushPayment() - } - - fun onDeposit(amount: Amount) { + private fun onDeposit(amount: Amount) { val bundle = bundleOf("amount" to amount.toJSONString()) findNavController().navigate(R.id.action_sendFunds_to_nav_deposit, bundle) } - fun onPeerPush(amount: Amount) { + private fun onPeerPush(amount: Amount) { val bundle = bundleOf("amount" to amount.toJSONString()) + peerManager.checkPeerPushDebit(amount) findNavController().navigate(R.id.action_sendFunds_to_nav_peer_push, bundle) } } diff --git a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullResultComposable.kt b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullResultComposable.kt index e6d9ec9..de62cda 100644 --- a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullResultComposable.kt +++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullResultComposable.kt @@ -58,7 +58,9 @@ fun OutgoingPullResultComposable(state: OutgoingState, onClose: () -> Unit) { text = stringResource(id = R.string.receive_peer_invoice_instruction), ) when (state) { - OutgoingIntro -> error("Result composable with PullPaymentIntro") + OutgoingIntro, OutgoingChecking, is OutgoingChecked -> { + error("Result composable with ${state::class.simpleName}") + } is OutgoingCreating -> PeerPullCreatingComposable() is OutgoingResponse -> PeerPullResponseComposable(state) is OutgoingError -> PeerPullErrorComposable(state) diff --git a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushFragment.kt b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushFragment.kt index b7a510c..a844b85 100644 --- a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushFragment.kt @@ -45,15 +45,18 @@ class OutgoingPushFragment : Fragment() { return ComposeView(requireContext()).apply { setContent { TalerSurface { - val state = peerManager.pushState.collectAsStateLifecycleAware() - if (state.value is OutgoingIntro) { - OutgoingPushIntroComposable( - amount = amount, - onSend = this@OutgoingPushFragment::onSend, - ) - } else { - OutgoingPushResultComposable(state.value) { - findNavController().popBackStack() + when (val state = peerManager.pushState.collectAsStateLifecycleAware().value) { + is OutgoingIntro, OutgoingChecking, is OutgoingChecked -> { + OutgoingPushIntroComposable( + state = state, + amount = amount, + onSend = this@OutgoingPushFragment::onSend, + ) + } + OutgoingCreating, is OutgoingResponse, is OutgoingError -> { + OutgoingPushResultComposable(state) { + findNavController().popBackStack() + } } } } diff --git a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushIntroComposable.kt b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushIntroComposable.kt index 7fd7c8b..7d109c7 100644 --- a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushIntroComposable.kt +++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushIntroComposable.kt @@ -16,8 +16,8 @@ package net.taler.wallet.peer +import androidx.compose.foundation.layout.Arrangement.spacedBy import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState @@ -33,7 +33,6 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment.Companion.CenterHorizontally import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -43,10 +42,12 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import net.taler.common.Amount import net.taler.wallet.R +import kotlin.random.Random @OptIn(ExperimentalMaterial3Api::class) @Composable fun OutgoingPushIntroComposable( + state: OutgoingState, amount: Amount, onSend: (amount: Amount, summary: String) -> Unit, ) { @@ -54,27 +55,28 @@ fun OutgoingPushIntroComposable( Column( modifier = Modifier .fillMaxWidth() + .padding(16.dp) .verticalScroll(scrollState), horizontalAlignment = CenterHorizontally, + verticalArrangement = spacedBy(16.dp), ) { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .padding(16.dp), - ) { + Text( + text = amount.toString(), + softWrap = false, + style = MaterialTheme.typography.titleLarge, + ) + if (state is OutgoingChecked) { + val fee = state.amountEffective - state.amountRaw Text( - modifier = Modifier, - text = amount.toString(), + text = stringResource(id = R.string.payment_fee, fee), softWrap = false, - style = MaterialTheme.typography.titleLarge, + color = MaterialTheme.colorScheme.error, ) } var subject by rememberSaveable { mutableStateOf("") } OutlinedTextField( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp), + modifier = Modifier.fillMaxWidth(), singleLine = true, value = subject, onValueChange = { input -> @@ -93,19 +95,16 @@ fun OutgoingPushIntroComposable( ) Text( modifier = Modifier - .fillMaxWidth() - .padding(top = 5.dp, end = 16.dp), + .fillMaxWidth(), color = if (subject.isBlank()) MaterialTheme.colorScheme.error else Color.Unspecified, text = stringResource(R.string.char_count, subject.length, MAX_LENGTH_SUBJECT), textAlign = TextAlign.End, ) Text( - modifier = Modifier.padding(top = 16.dp, start = 16.dp, end = 16.dp), text = stringResource(R.string.send_peer_warning), ) Button( - modifier = Modifier.padding(16.dp), - enabled = subject.isNotBlank(), + enabled = state is OutgoingChecked && subject.isNotBlank(), onClick = { onSend(amount, subject) }, @@ -117,8 +116,20 @@ fun OutgoingPushIntroComposable( @Preview @Composable -fun PeerPushIntroComposablePreview() { +fun PeerPushIntroComposableCheckingPreview() { + Surface { + val state = if (Random.nextBoolean()) OutgoingIntro else OutgoingChecking + OutgoingPushIntroComposable(state, Amount.fromDouble("TESTKUDOS", 42.23)) { _, _ -> } + } +} + +@Preview +@Composable +fun PeerPushIntroComposableCheckedPreview() { Surface { - OutgoingPushIntroComposable(Amount.fromDouble("TESTKUDOS", 42.23)) { _, _ -> } + val amountEffective = Amount.fromDouble("TESTKUDOS", 42.23) + val amountRaw = Amount.fromDouble("TESTKUDOS", 42.42) + val state = OutgoingChecked(amountEffective, amountRaw) + OutgoingPushIntroComposable(state, amountEffective) { _, _ -> } } } diff --git a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushResultComposable.kt b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushResultComposable.kt index 0fb3f2c..0a4ee70 100644 --- a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushResultComposable.kt +++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushResultComposable.kt @@ -58,7 +58,9 @@ fun OutgoingPushResultComposable(state: OutgoingState, onClose: () -> Unit) { text = stringResource(id = R.string.send_peer_payment_instruction), ) when (state) { - OutgoingIntro -> error("Result composable with PullPaymentIntro") + OutgoingIntro, OutgoingChecking, is OutgoingChecked -> { + error("Result composable with ${state::class.simpleName}") + } is OutgoingCreating -> PeerPushCreatingComposable() is OutgoingResponse -> PeerPushResponseComposable(state) is OutgoingError -> PeerPushErrorComposable(state) diff --git a/wallet/src/main/java/net/taler/wallet/peer/OutgoingState.kt b/wallet/src/main/java/net/taler/wallet/peer/OutgoingState.kt index e40ddb8..b0a31d2 100644 --- a/wallet/src/main/java/net/taler/wallet/peer/OutgoingState.kt +++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingState.kt @@ -18,10 +18,16 @@ package net.taler.wallet.peer import android.graphics.Bitmap import kotlinx.serialization.Serializable +import net.taler.common.Amount import net.taler.wallet.backend.TalerErrorInfo sealed class OutgoingState object OutgoingIntro : OutgoingState() +object OutgoingChecking : OutgoingState() +data class OutgoingChecked( + val amountRaw: Amount, + val amountEffective: Amount, +) : OutgoingState() object OutgoingCreating : OutgoingState() data class OutgoingResponse( val talerUri: String, @@ -40,6 +46,12 @@ data class InitiatePeerPullPaymentResponse( val talerUri: String, ) +@Serializable +data class CheckPeerPushDebitResponse( + val amountRaw: Amount, + val amountEffective: Amount, +) + @Serializable data class InitiatePeerPullCreditResponse( val exchangeBaseUrl: String, diff --git a/wallet/src/main/java/net/taler/wallet/peer/PeerManager.kt b/wallet/src/main/java/net/taler/wallet/peer/PeerManager.kt index 5b38e2f..7875c6f 100644 --- a/wallet/src/main/java/net/taler/wallet/peer/PeerManager.kt +++ b/wallet/src/main/java/net/taler/wallet/peer/PeerManager.kt @@ -77,6 +77,23 @@ class PeerManager( _outgoingPullState.value = OutgoingIntro } + fun checkPeerPushDebit(amount: Amount) { + _outgoingPushState.value = OutgoingChecking + scope.launch(Dispatchers.IO) { + api.request("checkPeerPushDebit", CheckPeerPushDebitResponse.serializer()) { + put("amount", amount.toJSONString()) + }.onSuccess { response -> + _outgoingPushState.value = OutgoingChecked( + amountRaw = response.amountRaw, + amountEffective = response.amountEffective, + ) + }.onError { error -> + Log.e(TAG, "got checkPeerPushDebit error result $error") + _outgoingPushState.value = OutgoingError(error) + } + } + } + fun initiatePeerPushDebit(amount: Amount, summary: String) { _outgoingPushState.value = OutgoingCreating scope.launch(Dispatchers.IO) { -- cgit v1.2.3