From 49b9fb9306addd4dc5540dfd08f0ced6d56050ec Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Fri, 15 May 2020 12:57:42 -0300 Subject: [wallet] show currency's transaction list after successful operations --- .../src/main/java/net/taler/common/Event.kt | 51 ++++++++++++++++++++++ .../src/main/java/net/taler/wallet/MainActivity.kt | 1 + .../src/main/java/net/taler/wallet/MainFragment.kt | 25 ++++++++--- .../main/java/net/taler/wallet/MainViewModel.kt | 13 ++++++ .../net/taler/wallet/balances/BalancesFragment.kt | 4 +- .../taler/wallet/payment/PromptPaymentFragment.kt | 3 +- .../wallet/withdraw/PromptWithdrawFragment.kt | 3 +- 7 files changed, 87 insertions(+), 13 deletions(-) create mode 100644 taler-kotlin-common/src/main/java/net/taler/common/Event.kt diff --git a/taler-kotlin-common/src/main/java/net/taler/common/Event.kt b/taler-kotlin-common/src/main/java/net/taler/common/Event.kt new file mode 100644 index 0000000..779247f --- /dev/null +++ b/taler-kotlin-common/src/main/java/net/taler/common/Event.kt @@ -0,0 +1,51 @@ +/* + * This file is part of GNU Taler + * (C) 2020 Taler Systems S.A. + * + * GNU Taler is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3, or (at your option) any later version. + * + * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * GNU Taler; see the file COPYING. If not, see + */ + +package net.taler.common + +import androidx.lifecycle.LiveData +import androidx.lifecycle.Observer +import java.util.concurrent.atomic.AtomicBoolean + +/** + * Used as a wrapper for data that is exposed via a [LiveData] that represents an one-time event. + */ +open class Event(private val content: T) { + + private val isConsumed = AtomicBoolean(false) + + /** + * Returns the content and prevents its use again. + */ + fun getIfNotConsumed(): T? { + return if (isConsumed.compareAndSet(false, true)) content else null + } + +} + +fun T.toEvent() = Event(this) + +/** + * An [Observer] for [Event]s, simplifying the pattern of checking if the [Event]'s content has + * already been consumed. + * + * [onEvent] is *only* called if the [Event]'s contents has not been consumed. + */ +class EventObserver(private val onEvent: (T) -> Unit) : Observer> { + override fun onChanged(event: Event?) { + event?.getIfNotConsumed()?.let { onEvent(it) } + } +} diff --git a/wallet/src/main/java/net/taler/wallet/MainActivity.kt b/wallet/src/main/java/net/taler/wallet/MainActivity.kt index a6385a9..f626e4f 100644 --- a/wallet/src/main/java/net/taler/wallet/MainActivity.kt +++ b/wallet/src/main/java/net/taler/wallet/MainActivity.kt @@ -157,6 +157,7 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener { model.showProgressBar.value = false val res = when (status) { is RefundStatus.Error -> R.string.refund_error + // TODO once wallet-core exposes currency, navigate to its transaction list is RefundStatus.Success -> R.string.refund_success } Snackbar.make(nav_view, res, LENGTH_LONG).show() diff --git a/wallet/src/main/java/net/taler/wallet/MainFragment.kt b/wallet/src/main/java/net/taler/wallet/MainFragment.kt index 2905238..328d7a2 100644 --- a/wallet/src/main/java/net/taler/wallet/MainFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/MainFragment.kt @@ -23,14 +23,20 @@ import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.Observer +import androidx.navigation.fragment.findNavController import kotlinx.android.synthetic.main.fragment_main.* +import net.taler.common.EventObserver +import net.taler.wallet.CurrencyMode.MULTI +import net.taler.wallet.CurrencyMode.SINGLE import net.taler.wallet.balances.BalancesFragment import net.taler.wallet.transactions.TransactionsFragment +enum class CurrencyMode { SINGLE, MULTI } + class MainFragment : Fragment() { private val model: MainViewModel by activityViewModels() - private var currentTag: String? = null + private var currencyMode: CurrencyMode? = null override fun onCreateView( inflater: LayoutInflater, @@ -44,6 +50,13 @@ class MainFragment : Fragment() { model.balances.observe(viewLifecycleOwner, Observer { onBalancesChanged(it.values.toList()) }) + model.transactionsEvent.observe(viewLifecycleOwner, EventObserver { currency -> + // we only need to navigate to a dedicated list, when in multi-currency mode + if (currencyMode == MULTI) { + model.transactionManager.selectedCurrency = currency + findNavController().navigate(R.id.action_nav_main_to_nav_transactions) + } + }) mainFab.setOnClickListener { scanQrCode(requireActivity()) @@ -56,17 +69,17 @@ class MainFragment : Fragment() { } private fun onBalancesChanged(balances: List) { - val tag = if (balances.size == 1) "single" else "multi" - if (currentTag != tag) { - val f = if (tag == "single") { + val mode = if (balances.size == 1) SINGLE else MULTI + if (currencyMode != mode) { + val f = if (mode == SINGLE) { model.transactionManager.selectedCurrency = balances[0].available.currency TransactionsFragment() } else { BalancesFragment() } - currentTag = tag + currencyMode = mode childFragmentManager.beginTransaction() - .replace(R.id.mainFragmentContainer, f, tag) + .replace(R.id.mainFragmentContainer, f, mode.name) .commitNow() } } diff --git a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt index f10666e..6a1d6aa 100644 --- a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt +++ b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt @@ -28,7 +28,9 @@ import com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PRO import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.KotlinModule import net.taler.common.Amount +import net.taler.common.Event import net.taler.common.assertUiThread +import net.taler.common.toEvent import net.taler.wallet.backend.WalletBackendApi import net.taler.wallet.history.DevHistoryManager import net.taler.wallet.payment.PaymentManager @@ -99,6 +101,9 @@ class MainViewModel(val app: Application) : AndroidViewModel(app) { TransactionManager(walletBackendApi, viewModelScope, mapper) val refundManager = RefundManager(walletBackendApi) + private val mTransactionsEvent = MutableLiveData>() + val transactionsEvent: LiveData> = mTransactionsEvent + override fun onCleared() { walletBackendApi.destroy() super.onCleared() @@ -129,6 +134,14 @@ class MainViewModel(val app: Application) : AndroidViewModel(app) { } } + /** + * Navigates to the given currency's transaction list, when [MainFragment] is shown. + */ + @UiThread + fun showTransactions(currency: String) { + mTransactionsEvent.value = currency.toEvent() + } + @UiThread fun dangerouslyReset() { walletBackendApi.sendRequest("reset", null) diff --git a/wallet/src/main/java/net/taler/wallet/balances/BalancesFragment.kt b/wallet/src/main/java/net/taler/wallet/balances/BalancesFragment.kt index 0a2b29a..ab4077a 100644 --- a/wallet/src/main/java/net/taler/wallet/balances/BalancesFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/balances/BalancesFragment.kt @@ -27,7 +27,6 @@ import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.Observer -import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager.VERTICAL import kotlinx.android.synthetic.main.fragment_balances.* @@ -79,8 +78,7 @@ class BalancesFragment : Fragment(), } override fun onBalanceClick(currency: String) { - model.transactionManager.selectedCurrency = currency - findNavController().navigate(R.id.action_nav_main_to_nav_transactions) + model.showTransactions(currency) } } diff --git a/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt b/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt index ab109bc..6f806b7 100644 --- a/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt @@ -116,9 +116,8 @@ class PromptPaymentFragment : Fragment(), ProductImageClickListener { is PayStatus.Success -> { showLoading(false) paymentManager.resetPayStatus() - // TODO bring the user to the currency's transaction page, if there's more than one currency - model.transactionManager.selectedCurrency = payStatus.currency findNavController().navigate(R.id.action_promptPayment_to_nav_main) + model.showTransactions(payStatus.currency) Snackbar.make(requireView(), R.string.payment_initiated, LENGTH_LONG).show() } is PayStatus.AlreadyPaid -> { diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt b/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt index e700f67..331554b 100644 --- a/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt @@ -77,9 +77,8 @@ class PromptWithdrawFragment : Fragment() { is WithdrawStatus.Success -> { model.showProgressBar.value = false withdrawManager.withdrawStatus.value = null - // TODO bring the user to the currency's transaction page, if there's more than one currency - model.transactionManager.selectedCurrency = status.currency findNavController().navigate(R.id.action_promptWithdraw_to_nav_main) + model.showTransactions(status.currency) Snackbar.make(requireView(), R.string.withdraw_initiated, LENGTH_LONG).show() } is Loading -> { -- cgit v1.2.3