commit 761aa68df2fe364d9351085f3a3081a99e60068f
parent 93aa3afcd9c2985f1442221be7f4794e5743e05d
Author: Iván Ávalos <avalos@disroot.org>
Date: Tue, 29 Apr 2025 17:51:40 +0200
[wallet] show dialog from history for payments in dialog state
bug 0009787
Diffstat:
5 files changed, 98 insertions(+), 6 deletions(-)
diff --git a/taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt b/taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt
@@ -38,7 +38,7 @@ data class ContractTerms(
val fulfillmentMessage: String? = null,
@SerialName("fulfillment_message_i18n")
val fulfillmentMessageI18n: Map <String, String>? = null,
- val products: List<ContractProduct>,
+ val products: List<ContractProduct>? = null,
@SerialName("wire_transfer_deadline")
val wireTransferDeadline: Timestamp? = null,
@SerialName("refund_deadline")
diff --git a/wallet/src/main/java/net/taler/wallet/MainFragment.kt b/wallet/src/main/java/net/taler/wallet/MainFragment.kt
@@ -89,6 +89,10 @@ import net.taler.wallet.compose.GridMenuItem
import net.taler.wallet.compose.TalerSurface
import net.taler.wallet.compose.collectAsStateLifecycleAware
import net.taler.wallet.settings.SettingsFragment
+import net.taler.wallet.transactions.Transaction
+import net.taler.wallet.transactions.TransactionMajorState
+import net.taler.wallet.transactions.TransactionPayment
+import net.taler.wallet.transactions.TransactionState
import net.taler.wallet.transactions.TransactionStateFilter.Nonfinal
import kotlin.math.roundToInt
@@ -194,10 +198,7 @@ class MainFragment: Fragment() {
model.showTransactions(it.scopeInfo, Nonfinal)
},
onTransactionClicked = { tx ->
- if (tx.detailPageNav != 0) {
- model.transactionManager.selectTransaction(tx)
- findNavController().navigate(tx.detailPageNav)
- }
+ onTransactionClicked(tx)
},
onTransactionsDelete = { txIds ->
model.transactionManager.deleteTransactions(txIds) { error ->
@@ -237,7 +238,29 @@ class MainFragment: Fragment() {
}
}
+ private fun onTransactionClicked(tx: Transaction) {
+ val showTxDetails = {
+ if (tx.detailPageNav != 0) {
+ model.transactionManager.selectTransaction(tx)
+ findNavController().navigate(tx.detailPageNav)
+ }
+ }
+
+ when (tx.txState) {
+ // unfinished transactions (dialog)
+ TransactionState(TransactionMajorState.Dialog) -> when (tx) {
+ is TransactionPayment -> {
+ model.paymentManager.preparePay(tx) {
+ findNavController().navigate(R.id.action_global_promptPayment)
+ }
+ }
+
+ else -> showTxDetails()
+ }
+ else -> showTxDetails()
+ }
+ }
override fun onStart() {
super.onStart()
diff --git a/wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt b/wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt
@@ -22,8 +22,11 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
+import kotlinx.serialization.ExperimentalSerializationApi
+import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
+import kotlinx.serialization.json.JsonClassDiscriminator
import net.taler.common.Amount
import net.taler.common.ContractTerms
import net.taler.wallet.TAG
@@ -35,6 +38,7 @@ import net.taler.wallet.payment.PayStatus.InsufficientBalance
import net.taler.wallet.payment.PreparePayResponse.AlreadyConfirmedResponse
import net.taler.wallet.payment.PreparePayResponse.InsufficientBalanceResponse
import net.taler.wallet.payment.PreparePayResponse.PaymentPossibleResponse
+import net.taler.wallet.transactions.TransactionPayment
import org.json.JSONObject
sealed class PayStatus {
@@ -78,6 +82,31 @@ data class CheckPayTemplateResponse(
val supportedCurrencies: List<String>,
)
+@Serializable
+data class GetChoicesForPaymentResponse(
+ val choices: List<ChoiceSelectionDetail>,
+ val contractData: ContractTerms,
+) {
+ @Serializable
+ @OptIn(ExperimentalSerializationApi::class)
+ @JsonClassDiscriminator("status")
+ sealed class ChoiceSelectionDetail {
+ @Serializable
+ @SerialName("payment-possible")
+ data class PaymentPossible(
+ val amountRaw: Amount,
+ val amountEffective: Amount,
+ ) : ChoiceSelectionDetail()
+
+ @Serializable
+ @SerialName("insufficient-balance")
+ data class InsufficientBalance(
+ val amountRaw: Amount,
+ val balanceDetails: PaymentInsufficientBalanceDetails? = null,
+ ) : ChoiceSelectionDetail()
+ }
+}
+
class PaymentManager(
private val api: WalletBackendApi,
private val scope: CoroutineScope,
@@ -108,6 +137,44 @@ class PaymentManager(
}
}
+ @UiThread
+ fun preparePay(
+ tx: TransactionPayment,
+ onSuccess: () -> Unit,
+ ) = scope.launch {
+ api.request("getChoicesForPayment", GetChoicesForPaymentResponse.serializer()) {
+ put("transactionId", tx.transactionId)
+ }.onSuccess { res ->
+ // TODO: this is a terrible v0-only hack!
+ if (res.choices.size == 1) {
+ when (val choice = res.choices[0]) {
+ is GetChoicesForPaymentResponse.ChoiceSelectionDetail.PaymentPossible -> {
+ mPayStatus.value = PayStatus.Prepared(
+ transactionId = tx.transactionId,
+ amountRaw = choice.amountRaw,
+ amountEffective = choice.amountEffective,
+ contractTerms = res.contractData,
+ )
+ onSuccess()
+ }
+
+ is GetChoicesForPaymentResponse.ChoiceSelectionDetail.InsufficientBalance -> {
+ if (choice.balanceDetails != null) {
+ mPayStatus.value = InsufficientBalance(
+ amountRaw = choice.amountRaw,
+ contractTerms = res.contractData,
+ balanceDetails = choice.balanceDetails,
+ )
+ onSuccess()
+ }
+ }
+ }
+ }
+ }.onError { error ->
+ handleError("getChoicesForPayment", error)
+ }
+ }
+
fun confirmPay(transactionId: String, currency: String) = scope.launch {
api.request("confirmPay", ConfirmPayResult.serializer()) {
put("transactionId", transactionId)
diff --git a/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt b/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt
@@ -180,7 +180,7 @@ class PromptPaymentFragment : Fragment(), ProductImageClickListener {
totalFees: Amount? = null,
) {
ui.details.orderView.text = contractTerms.summary
- adapter.update(contractTerms.products)
+ adapter.update(contractTerms.products ?: emptyList())
ui.details.productsList.fadeIn()
ui.bottom.totalView.text = amount.toString()
diff --git a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
@@ -37,6 +37,7 @@ import kotlinx.serialization.json.JsonElement
import net.taler.common.Amount
import net.taler.common.ContractMerchant
import net.taler.common.ContractProduct
+import net.taler.common.ContractTerms
import net.taler.common.Timestamp
import net.taler.wallet.R
import net.taler.wallet.TAG
@@ -309,6 +310,7 @@ class TransactionPayment(
override val txState: TransactionState,
override val txActions: List<TransactionAction>,
val info: TransactionInfo,
+ val contractTerms: ContractTerms? = null,
override val error: TalerErrorInfo? = null,
override val amountRaw: Amount,
override val amountEffective: Amount,