diff options
author | Torsten Grote <t@grobox.de> | 2020-05-12 15:26:44 -0300 |
---|---|---|
committer | Torsten Grote <t@grobox.de> | 2020-05-15 14:26:41 -0300 |
commit | e74f39ee86f32b4e0324405af1f0c7be061fb372 (patch) | |
tree | 484a9e2f6ad3d8d6c9662ff5f41c6f254d218b30 /wallet/src | |
parent | 4a6630d1d147ae35358272dc5222964831c234ab (diff) | |
download | taler-android-e74f39ee86f32b4e0324405af1f0c7be061fb372.tar.gz taler-android-e74f39ee86f32b4e0324405af1f0c7be061fb372.tar.bz2 taler-android-e74f39ee86f32b4e0324405af1f0c7be061fb372.zip |
[wallet] separate history and transactions UI
The history with its JSON payload is only shown in dev mode
while the transactions are prepared to move to the new API.
Diffstat (limited to 'wallet/src')
20 files changed, 539 insertions, 153 deletions
diff --git a/wallet/src/main/java/net/taler/wallet/MainActivity.kt b/wallet/src/main/java/net/taler/wallet/MainActivity.kt index b6e9a7a..a6385a9 100644 --- a/wallet/src/main/java/net/taler/wallet/MainActivity.kt +++ b/wallet/src/main/java/net/taler/wallet/MainActivity.kt @@ -75,7 +75,7 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener { setSupportActionBar(toolbar) val appBarConfiguration = AppBarConfiguration( - setOf(R.id.nav_main, R.id.nav_settings, R.id.nav_pending_operations), + setOf(R.id.nav_main, R.id.nav_settings, R.id.nav_pending_operations, R.id.nav_history), drawer_layout ) toolbar.setupWithNavController(nav, appBarConfiguration) @@ -86,7 +86,7 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener { val versionView: TextView = nav_view.getHeaderView(0).findViewById(R.id.versionView) model.devMode.observe(this, Observer { enabled -> - nav_view.menu.findItem(R.id.nav_pending_operations).isVisible = enabled + nav_view.menu.findItem(R.id.nav_dev).isVisible = enabled if (enabled) { @SuppressLint("SetTextI18n") versionView.text = "$VERSION_NAME ($VERSION_CODE)" @@ -116,6 +116,7 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener { R.id.nav_home -> nav.navigate(R.id.nav_main) R.id.nav_settings -> nav.navigate(R.id.nav_settings) R.id.nav_pending_operations -> nav.navigate(R.id.nav_pending_operations) + R.id.nav_history -> nav.navigate(R.id.nav_history) } drawer_layout.closeDrawer(START) return true diff --git a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt index 230c310..b880036 100644 --- a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt +++ b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt @@ -23,11 +23,13 @@ import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.distinctUntilChanged +import androidx.lifecycle.viewModelScope import com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.KotlinModule import net.taler.common.Amount import net.taler.wallet.backend.WalletBackendApi +import net.taler.wallet.history.DevHistoryManager import net.taler.wallet.payment.PaymentManager import net.taler.wallet.pending.PendingOperationsManager import net.taler.wallet.refund.RefundManager @@ -70,13 +72,13 @@ class MainViewModel(val app: Application) : AndroidViewModel(app) { loadBalances() if (payload.optString("type") in transactionNotifications) { // update transaction list - // TODO do this in a better way - transactionManager.showAll.value?.let { - transactionManager.showAll.postValue(it) - } + transactionManager.loadTransactions() + } + // refresh pending ops and history with each notification + if (devMode.value == true) { + pendingOperationsManager.getPending() + historyManager.loadHistory() } - // refresh pending ops with each notification - if (devMode.value == true) pendingOperationsManager.getPending() } } @@ -88,7 +90,10 @@ class MainViewModel(val app: Application) : AndroidViewModel(app) { val paymentManager = PaymentManager(walletBackendApi, mapper) val pendingOperationsManager: PendingOperationsManager = PendingOperationsManager(walletBackendApi) - val transactionManager: TransactionManager = TransactionManager(walletBackendApi, mapper) + val historyManager: DevHistoryManager = + DevHistoryManager(walletBackendApi, viewModelScope, mapper) + val transactionManager: TransactionManager = + TransactionManager(walletBackendApi, viewModelScope, mapper) val refundManager = RefundManager(walletBackendApi) override fun onCleared() { diff --git a/wallet/src/main/java/net/taler/wallet/history/DevHistoryAdapter.kt b/wallet/src/main/java/net/taler/wallet/history/DevHistoryAdapter.kt new file mode 100644 index 0000000..88db90c --- /dev/null +++ b/wallet/src/main/java/net/taler/wallet/history/DevHistoryAdapter.kt @@ -0,0 +1,133 @@ +/* + * 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 <http://www.gnu.org/licenses/> + */ + +package net.taler.wallet.history + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView.Adapter +import androidx.recyclerview.widget.RecyclerView.ViewHolder +import net.taler.common.exhaustive +import net.taler.common.toRelativeTime +import net.taler.wallet.R +import net.taler.wallet.history.DevHistoryAdapter.HistoryViewHolder + +@Deprecated("Replaced by TransactionAdapter") +internal class DevHistoryAdapter( + private val listener: OnEventClickListener, + private var history: History = History() +) : Adapter<HistoryViewHolder>() { + + init { + setHasStableIds(false) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HistoryViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.list_item_history, parent, false) + return HistoryViewHolder(view) + } + + override fun getItemCount(): Int = history.size + + override fun onBindViewHolder(holder: HistoryViewHolder, position: Int) { + val transaction = history[position] + holder.bind(transaction) + } + + fun update(updatedHistory: History) { + this.history = updatedHistory + this.notifyDataSetChanged() + } + + internal open inner class HistoryViewHolder(private val v: View) : ViewHolder(v) { + + protected val context: Context = v.context + + private val icon: ImageView = v.findViewById(R.id.icon) + protected val title: TextView = v.findViewById(R.id.title) + private val time: TextView = v.findViewById(R.id.time) + private val amount: TextView = v.findViewById(R.id.amount) + + private val amountColor = amount.currentTextColor + + open fun bind(historyEvent: HistoryEvent) { + v.setOnClickListener { listener.onTransactionClicked(historyEvent) } + icon.setImageResource(historyEvent.icon) + + title.text = if (historyEvent.title == null) { + when (historyEvent) { + is RefreshHistoryEvent -> getRefreshTitle(historyEvent) + is OrderAcceptedHistoryEvent -> context.getString(R.string.transaction_order_accepted) + is OrderRefusedHistoryEvent -> context.getString(R.string.transaction_order_refused) + is TipAcceptedHistoryEvent -> context.getString(R.string.transaction_tip_accepted) + is TipDeclinedHistoryEvent -> context.getString(R.string.transaction_tip_declined) + is ReserveBalanceUpdatedHistoryEvent -> context.getString(R.string.transaction_reserve_balance_updated) + else -> historyEvent::class.java.simpleName + } + } else historyEvent.title + + time.text = historyEvent.timestamp.ms.toRelativeTime(context) + bindAmount(historyEvent.displayAmount) + } + + private fun bindAmount(displayAmount: DisplayAmount?) { + if (displayAmount == null) { + amount.visibility = GONE + } else { + amount.visibility = VISIBLE + when (displayAmount.type) { + AmountType.Positive -> { + amount.text = context.getString( + R.string.amount_positive, displayAmount.amount.amountStr + ) + amount.setTextColor(context.getColor(R.color.green)) + } + AmountType.Negative -> { + amount.text = context.getString( + R.string.amount_negative, displayAmount.amount.amountStr + ) + amount.setTextColor(context.getColor(R.color.red)) + } + AmountType.Neutral -> { + amount.text = displayAmount.amount.amountStr + amount.setTextColor(amountColor) + } + }.exhaustive + } + } + + private fun getRefreshTitle(transaction: RefreshHistoryEvent): String { + val res = when (transaction.refreshReason) { + RefreshReason.MANUAL -> R.string.transaction_refresh_reason_manual + RefreshReason.PAY -> R.string.transaction_refresh_reason_pay + RefreshReason.REFUND -> R.string.transaction_refresh_reason_refund + RefreshReason.ABORT_PAY -> R.string.transaction_refresh_reason_abort_pay + RefreshReason.RECOUP -> R.string.transaction_refresh_reason_recoup + RefreshReason.BACKUP_RESTORED -> R.string.transaction_refresh_reason_backup_restored + } + return context.getString(R.string.transaction_refresh) + " " + context.getString(res) + } + + } + +} diff --git a/wallet/src/main/java/net/taler/wallet/history/DevHistoryFragment.kt b/wallet/src/main/java/net/taler/wallet/history/DevHistoryFragment.kt new file mode 100644 index 0000000..c3c07a3 --- /dev/null +++ b/wallet/src/main/java/net/taler/wallet/history/DevHistoryFragment.kt @@ -0,0 +1,87 @@ +/* + * 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 <http://www.gnu.org/licenses/> + */ + +package net.taler.wallet.history + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.View.INVISIBLE +import android.view.View.VISIBLE +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.Observer +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.LinearLayoutManager.VERTICAL +import kotlinx.android.synthetic.main.fragment_transactions.* +import net.taler.common.fadeIn +import net.taler.common.fadeOut +import net.taler.wallet.MainViewModel +import net.taler.wallet.R + +internal interface OnEventClickListener { + fun onTransactionClicked(historyEvent: HistoryEvent) +} + +class DevHistoryFragment : Fragment(), + OnEventClickListener { + + private val model: MainViewModel by activityViewModels() + private val historyManager by lazy { model.historyManager } + private val historyAdapter by lazy { DevHistoryAdapter(this) } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_transactions, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + if (savedInstanceState == null) historyManager.loadHistory() + + list.apply { + adapter = historyAdapter + addItemDecoration(DividerItemDecoration(context, VERTICAL)) + } + historyManager.progress.observe(viewLifecycleOwner, Observer { show -> + progressBar.visibility = if (show) VISIBLE else INVISIBLE + }) + historyManager.history.observe(viewLifecycleOwner, Observer { result -> + onHistoryResult(result) + }) + } + + override fun onTransactionClicked(historyEvent: HistoryEvent) { + JsonDialogFragment.new(historyEvent.json.toString(2)) + .show(parentFragmentManager, null) + } + + private fun onHistoryResult(result: HistoryResult) = when (result) { + HistoryResult.Error -> { + list.fadeOut() + emptyState.text = getString(R.string.transactions_error) + emptyState.fadeIn() + } + is HistoryResult.Success -> { + emptyState.visibility = if (result.history.isEmpty()) VISIBLE else INVISIBLE + historyAdapter.update(result.history) + list.fadeIn() + } + } + +} diff --git a/wallet/src/main/java/net/taler/wallet/history/DevHistoryManager.kt b/wallet/src/main/java/net/taler/wallet/history/DevHistoryManager.kt new file mode 100644 index 0000000..72967b2 --- /dev/null +++ b/wallet/src/main/java/net/taler/wallet/history/DevHistoryManager.kt @@ -0,0 +1,78 @@ +/* + * 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 <http://www.gnu.org/licenses/> + */ + +package net.taler.wallet.history + +import androidx.annotation.UiThread +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import net.taler.wallet.backend.WalletBackendApi +import org.json.JSONObject + +sealed class HistoryResult { + object Error : HistoryResult() + class Success(val history: History) : HistoryResult() +} + +class DevHistoryManager( + private val walletBackendApi: WalletBackendApi, + private val scope: CoroutineScope, + private val mapper: ObjectMapper +) { + + private val mProgress = MutableLiveData<Boolean>() + val progress: LiveData<Boolean> = mProgress + + private val mHistory = MutableLiveData<HistoryResult>() + val history: LiveData<HistoryResult> = mHistory + + @UiThread + internal fun loadHistory() { + mProgress.value = true + walletBackendApi.sendRequest("getHistory", null) { isError, result -> + scope.launch(Dispatchers.Default) { + onEventsLoaded(isError, result) + } + } + } + + private fun onEventsLoaded(isError: Boolean, result: JSONObject) { + if (isError) { + mHistory.postValue(HistoryResult.Error) + return + } + val history = History() + val json = result.getJSONArray("history") + for (i in 0 until json.length()) { + val event: HistoryEvent = mapper.readValue(json.getString(i)) + event.json = json.getJSONObject(i) + history.add(event) + } + history.reverse() // show latest first + mProgress.postValue(false) + mHistory.postValue( + HistoryResult.Success( + history + ) + ) + } + +} diff --git a/wallet/src/main/java/net/taler/wallet/transactions/Transaction.kt b/wallet/src/main/java/net/taler/wallet/history/HistoryEvent.kt index 34942d0..acca679 100644 --- a/wallet/src/main/java/net/taler/wallet/transactions/Transaction.kt +++ b/wallet/src/main/java/net/taler/wallet/history/HistoryEvent.kt @@ -14,7 +14,7 @@ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -package net.taler.wallet.transactions +package net.taler.wallet.history import androidx.annotation.DrawableRes import androidx.annotation.LayoutRes @@ -103,13 +103,13 @@ class DisplayAmount( val type: AmountType ) -typealias Transactions = ArrayList<Transaction> +typealias History = ArrayList<HistoryEvent> @JsonTypeInfo( use = NAME, include = PROPERTY, property = "type", - defaultImpl = UnknownTransaction::class + defaultImpl = UnknownHistoryEvent::class ) /** missing: AuditorComplaintSent = "auditor-complained-sent", @@ -127,19 +127,19 @@ ReserveCreated = "reserve-created", @JsonSubTypes( Type(value = ExchangeAddedEvent::class, name = "exchange-added"), Type(value = ExchangeUpdatedEvent::class, name = "exchange-updated"), - Type(value = ReserveBalanceUpdatedTransaction::class, name = "reserve-balance-updated"), - Type(value = WithdrawTransaction::class, name = "withdrawn"), - Type(value = OrderAcceptedTransaction::class, name = "order-accepted"), - Type(value = OrderRefusedTransaction::class, name = "order-refused"), - Type(value = OrderRedirectedTransaction::class, name = "order-redirected"), - Type(value = PaymentTransaction::class, name = "payment-sent"), - Type(value = PaymentAbortedTransaction::class, name = "payment-aborted"), - Type(value = TipAcceptedTransaction::class, name = "tip-accepted"), - Type(value = TipDeclinedTransaction::class, name = "tip-declined"), - Type(value = RefundTransaction::class, name = "refund"), - Type(value = RefreshTransaction::class, name = "refreshed") + Type(value = ReserveBalanceUpdatedHistoryEvent::class, name = "reserve-balance-updated"), + Type(value = WithdrawHistoryEvent::class, name = "withdrawn"), + Type(value = OrderAcceptedHistoryEvent::class, name = "order-accepted"), + Type(value = OrderRefusedHistoryEvent::class, name = "order-refused"), + Type(value = OrderRedirectedHistoryEvent::class, name = "order-redirected"), + Type(value = PaymentHistoryEvent::class, name = "payment-sent"), + Type(value = PaymentAbortedHistoryEvent::class, name = "payment-aborted"), + Type(value = TipAcceptedHistoryEvent::class, name = "tip-accepted"), + Type(value = TipDeclinedHistoryEvent::class, name = "tip-declined"), + Type(value = RefundHistoryEvent::class, name = "refund"), + Type(value = RefreshHistoryEvent::class, name = "refreshed") ) -abstract class Transaction( +abstract class HistoryEvent( val timestamp: Timestamp, val eventId: String, @get:LayoutRes @@ -155,7 +155,7 @@ abstract class Transaction( } -class UnknownTransaction(timestamp: Timestamp, eventId: String) : Transaction(timestamp, eventId) { +class UnknownHistoryEvent(timestamp: Timestamp, eventId: String) : HistoryEvent(timestamp, eventId) { override val title: String? = null } @@ -165,7 +165,7 @@ class ExchangeAddedEvent( eventId: String, val exchangeBaseUrl: String, val builtIn: Boolean -) : Transaction(timestamp, eventId) { +) : HistoryEvent(timestamp, eventId) { override val title = cleanExchange(exchangeBaseUrl) } @@ -174,13 +174,13 @@ class ExchangeUpdatedEvent( timestamp: Timestamp, eventId: String, val exchangeBaseUrl: String -) : Transaction(timestamp, eventId) { +) : HistoryEvent(timestamp, eventId) { override val title = cleanExchange(exchangeBaseUrl) } @JsonTypeName("reserve-balance-updated") -class ReserveBalanceUpdatedTransaction( +class ReserveBalanceUpdatedHistoryEvent( timestamp: Timestamp, eventId: String, /** @@ -200,14 +200,17 @@ class ReserveBalanceUpdatedTransaction( * Amount that hasn't been withdrawn yet. */ val reserveUnclaimedAmount: Amount -) : Transaction(timestamp, eventId) { +) : HistoryEvent(timestamp, eventId) { override val title: String? = null - override val displayAmount = DisplayAmount(reserveBalance, AmountType.Neutral) + override val displayAmount = DisplayAmount( + reserveBalance, + AmountType.Neutral + ) override fun isCurrency(currency: String) = reserveBalance.currency == currency } @JsonTypeName("withdrawn") -class WithdrawTransaction( +class WithdrawHistoryEvent( timestamp: Timestamp, eventId: String, /** @@ -229,45 +232,48 @@ class WithdrawTransaction( * Amount that actually was added to the wallet's balance. */ val amountWithdrawnEffective: Amount -) : Transaction(timestamp, eventId) { +) : HistoryEvent(timestamp, eventId) { override val detailPageLayout = R.layout.fragment_event_withdraw override val title = cleanExchange(exchangeBaseUrl) override val icon = R.drawable.transaction_withdrawal override val showToUser = true - override val displayAmount = DisplayAmount(amountWithdrawnEffective, AmountType.Positive) + override val displayAmount = DisplayAmount( + amountWithdrawnEffective, + AmountType.Positive + ) override fun isCurrency(currency: String) = amountWithdrawnRaw.currency == currency } @JsonTypeName("order-accepted") -class OrderAcceptedTransaction( +class OrderAcceptedHistoryEvent( timestamp: Timestamp, eventId: String, /** * Condensed info about the order. */ val orderShortInfo: OrderShortInfo -) : Transaction(timestamp, eventId) { +) : HistoryEvent(timestamp, eventId) { override val icon = R.drawable.ic_add_circle override val title: String? = null override fun isCurrency(currency: String) = orderShortInfo.amount.currency == currency } @JsonTypeName("order-refused") -class OrderRefusedTransaction( +class OrderRefusedHistoryEvent( timestamp: Timestamp, eventId: String, /** * Condensed info about the order. */ val orderShortInfo: OrderShortInfo -) : Transaction(timestamp, eventId) { +) : HistoryEvent(timestamp, eventId) { override val icon = R.drawable.ic_cancel override val title: String? = null override fun isCurrency(currency: String) = orderShortInfo.amount.currency == currency } @JsonTypeName("payment-sent") -class PaymentTransaction( +class PaymentHistoryEvent( timestamp: Timestamp, eventId: String, /** @@ -291,17 +297,20 @@ class PaymentTransaction( * Session ID that the payment was (re-)submitted under. */ val sessionId: String? -) : Transaction(timestamp, eventId) { +) : HistoryEvent(timestamp, eventId) { override val detailPageLayout = R.layout.fragment_event_paid override val title = orderShortInfo.summary override val icon = R.drawable.ic_cash_usd_outline override val showToUser = true - override val displayAmount = DisplayAmount(amountPaidWithFees, AmountType.Negative) + override val displayAmount = DisplayAmount( + amountPaidWithFees, + AmountType.Negative + ) override fun isCurrency(currency: String) = orderShortInfo.amount.currency == currency } @JsonTypeName("payment-aborted") -class PaymentAbortedTransaction( +class PaymentAbortedHistoryEvent( timestamp: Timestamp, eventId: String, /** @@ -312,16 +321,19 @@ class PaymentAbortedTransaction( * Amount that was lost due to refund and refreshing fees. */ val amountLost: Amount -) : Transaction(timestamp, eventId) { +) : HistoryEvent(timestamp, eventId) { override val title = orderShortInfo.summary override val icon = R.drawable.transaction_payment_aborted override val showToUser = true - override val displayAmount = DisplayAmount(amountLost, AmountType.Negative) + override val displayAmount = DisplayAmount( + amountLost, + AmountType.Negative + ) override fun isCurrency(currency: String) = orderShortInfo.amount.currency == currency } @JsonTypeName("refreshed") -class RefreshTransaction( +class RefreshHistoryEvent( timestamp: Timestamp, eventId: String, /** @@ -345,7 +357,7 @@ class RefreshTransaction( * more refresh session IDs. */ val refreshGroupId: String -) : Transaction(timestamp, eventId) { +) : HistoryEvent(timestamp, eventId) { override val icon = R.drawable.transaction_refresh override val title: String? = null override val showToUser = !(amountRefreshedRaw - amountRefreshedEffective).isZero() @@ -362,7 +374,7 @@ class RefreshTransaction( } @JsonTypeName("order-redirected") -class OrderRedirectedTransaction( +class OrderRedirectedHistoryEvent( timestamp: Timestamp, eventId: String, /** @@ -374,14 +386,14 @@ class OrderRedirectedTransaction( * Condensed info about the order that we already paid for. */ val alreadyPaidOrderShortInfo: OrderShortInfo -) : Transaction(timestamp, eventId) { +) : HistoryEvent(timestamp, eventId) { override val icon = R.drawable.ic_directions override val title = newOrderShortInfo.summary override fun isCurrency(currency: String) = newOrderShortInfo.amount.currency == currency } @JsonTypeName("tip-accepted") -class TipAcceptedTransaction( +class TipAcceptedHistoryEvent( timestamp: Timestamp, eventId: String, /** @@ -392,16 +404,19 @@ class TipAcceptedTransaction( * Raw amount of the tip, without extra fees that apply. */ val tipRaw: Amount -) : Transaction(timestamp, eventId) { +) : HistoryEvent(timestamp, eventId) { override val icon = R.drawable.transaction_tip_accepted override val title: String? = null override val showToUser = true - override val displayAmount = DisplayAmount(tipRaw, AmountType.Positive) + override val displayAmount = DisplayAmount( + tipRaw, + AmountType.Positive + ) override fun isCurrency(currency: String) = tipRaw.currency == currency } @JsonTypeName("tip-declined") -class TipDeclinedTransaction( +class TipDeclinedHistoryEvent( timestamp: Timestamp, eventId: String, /** @@ -412,16 +427,19 @@ class TipDeclinedTransaction( * Raw amount of the tip, without extra fees that apply. */ val tipAmount: Amount -) : Transaction(timestamp, eventId) { +) : HistoryEvent(timestamp, eventId) { override val icon = R.drawable.transaction_tip_declined override val title: String? = null override val showToUser = true - override val displayAmount = DisplayAmount(tipAmount, AmountType.Neutral) + override val displayAmount = DisplayAmount( + tipAmount, + AmountType.Neutral + ) override fun isCurrency(currency: String) = tipAmount.currency == currency } @JsonTypeName("refund") -class RefundTransaction( +class RefundHistoryEvent( timestamp: Timestamp, eventId: String, val orderShortInfo: OrderShortInfo, @@ -443,12 +461,15 @@ class RefundTransaction( * Amount will be added to the wallet's balance after fees and refreshing. */ val amountRefundedEffective: Amount -) : Transaction(timestamp, eventId) { +) : HistoryEvent(timestamp, eventId) { override val icon = R.drawable.transaction_refund override val title = orderShortInfo.summary override val detailPageLayout = R.layout.fragment_event_paid override val showToUser = true - override val displayAmount = DisplayAmount(amountRefundedEffective, AmountType.Positive) + override val displayAmount = DisplayAmount( + amountRefundedEffective, + AmountType.Positive + ) override fun isCurrency(currency: String) = amountRefundedRaw.currency == currency } diff --git a/wallet/src/main/java/net/taler/wallet/transactions/JsonDialogFragment.kt b/wallet/src/main/java/net/taler/wallet/history/JsonDialogFragment.kt index 2337059..31c2b93 100644 --- a/wallet/src/main/java/net/taler/wallet/transactions/JsonDialogFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/history/JsonDialogFragment.kt @@ -14,7 +14,7 @@ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -package net.taler.wallet.transactions +package net.taler.wallet.history import android.os.Bundle import android.view.LayoutInflater diff --git a/wallet/src/main/java/net/taler/wallet/transactions/ReserveTransaction.kt b/wallet/src/main/java/net/taler/wallet/history/ReserveTransaction.kt index e497e9a..6c8fdaa 100644 --- a/wallet/src/main/java/net/taler/wallet/transactions/ReserveTransaction.kt +++ b/wallet/src/main/java/net/taler/wallet/history/ReserveTransaction.kt @@ -14,7 +14,7 @@ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -package net.taler.wallet.transactions +package net.taler.wallet.history import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonSubTypes diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt index 440d07f..5aca896 100644 --- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt +++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt @@ -34,13 +34,23 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder import net.taler.common.exhaustive import net.taler.common.toRelativeTime import net.taler.wallet.R +import net.taler.wallet.history.AmountType +import net.taler.wallet.history.DisplayAmount +import net.taler.wallet.history.History +import net.taler.wallet.history.HistoryEvent +import net.taler.wallet.history.OrderAcceptedHistoryEvent +import net.taler.wallet.history.OrderRefusedHistoryEvent +import net.taler.wallet.history.RefreshHistoryEvent +import net.taler.wallet.history.RefreshReason +import net.taler.wallet.history.ReserveBalanceUpdatedHistoryEvent +import net.taler.wallet.history.TipAcceptedHistoryEvent +import net.taler.wallet.history.TipDeclinedHistoryEvent import net.taler.wallet.transactions.TransactionAdapter.TransactionViewHolder internal class TransactionAdapter( - private val devMode: Boolean, - private val listener: OnEventClickListener, - private var transactions: Transactions = Transactions() + private val listener: OnTransactionClickListener, + private var transactions: History = History() ) : Adapter<TransactionViewHolder>() { lateinit var tracker: SelectionTracker<String> @@ -52,7 +62,7 @@ internal class TransactionAdapter( override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransactionViewHolder { val view = LayoutInflater.from(parent.context) - .inflate(R.layout.list_item_transaction, parent, false) + .inflate(R.layout.list_item_history, parent, false) return TransactionViewHolder(view) } @@ -63,7 +73,7 @@ internal class TransactionAdapter( holder.bind(transaction, tracker.isSelected(transaction.eventId)) } - fun update(updatedTransactions: Transactions) { + fun update(updatedTransactions: History) { this.transactions = updatedTransactions this.notifyDataSetChanged() } @@ -84,8 +94,8 @@ internal class TransactionAdapter( private val selectableForeground = v.foreground private val amountColor = amount.currentTextColor - open fun bind(transaction: Transaction, selected: Boolean) { - if (devMode || transaction.detailPageLayout != 0) { + open fun bind(transaction: HistoryEvent, selected: Boolean) { + if (transaction.detailPageLayout != 0) { v.foreground = selectableForeground v.setOnClickListener { listener.onTransactionClicked(transaction) } } else { @@ -97,12 +107,12 @@ internal class TransactionAdapter( title.text = if (transaction.title == null) { when (transaction) { - is RefreshTransaction -> getRefreshTitle(transaction) - is OrderAcceptedTransaction -> context.getString(R.string.transaction_order_accepted) - is OrderRefusedTransaction -> context.getString(R.string.transaction_order_refused) - is TipAcceptedTransaction -> context.getString(R.string.transaction_tip_accepted) - is TipDeclinedTransaction -> context.getString(R.string.transaction_tip_declined) - is ReserveBalanceUpdatedTransaction -> context.getString(R.string.transaction_reserve_balance_updated) + is RefreshHistoryEvent -> getRefreshTitle(transaction) + is OrderAcceptedHistoryEvent -> context.getString(R.string.transaction_order_accepted) + is OrderRefusedHistoryEvent -> context.getString(R.string.transaction_order_refused) + is TipAcceptedHistoryEvent -> context.getString(R.string.transaction_tip_accepted) + is TipDeclinedHistoryEvent -> context.getString(R.string.transaction_tip_declined) + is ReserveBalanceUpdatedHistoryEvent -> context.getString(R.string.transaction_reserve_balance_updated) else -> transaction::class.java.simpleName } } else transaction.title @@ -137,7 +147,7 @@ internal class TransactionAdapter( } } - private fun getRefreshTitle(transaction: RefreshTransaction): String { + private fun getRefreshTitle(transaction: RefreshHistoryEvent): String { val res = when (transaction.refreshReason) { RefreshReason.MANUAL -> R.string.transaction_refresh_reason_manual RefreshReason.PAY -> R.string.transaction_refresh_reason_pay diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt index 909a7bf..bb70b5c 100644 --- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt @@ -37,6 +37,11 @@ import net.taler.common.toAbsoluteTime import net.taler.wallet.MainViewModel import net.taler.wallet.R import net.taler.wallet.cleanExchange +import net.taler.wallet.history.JsonDialogFragment +import net.taler.wallet.history.OrderShortInfo +import net.taler.wallet.history.PaymentHistoryEvent +import net.taler.wallet.history.RefundHistoryEvent +import net.taler.wallet.history.WithdrawHistoryEvent class TransactionDetailFragment : Fragment() { @@ -65,9 +70,9 @@ class TransactionDetailFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { timeView.text = event.timestamp.ms.toAbsoluteTime(requireContext()) when (val e = event) { - is WithdrawTransaction -> bind(e) - is PaymentTransaction -> bind(e) - is RefundTransaction -> bind(e) + is WithdrawHistoryEvent -> bind(e) + is PaymentHistoryEvent -> bind(e) + is RefundHistoryEvent -> bind(e) else -> Toast.makeText( requireContext(), "event ${e.javaClass} not implement", @@ -90,7 +95,7 @@ class TransactionDetailFragment : Fragment() { } } - private fun bind(event: WithdrawTransaction) { + private fun bind(event: WithdrawHistoryEvent) { effectiveAmountLabel.text = getString(R.string.withdraw_total) effectiveAmountView.text = event.amountWithdrawnEffective.toString() chosenAmountLabel.text = getString(R.string.amount_chosen) @@ -101,13 +106,13 @@ class TransactionDetailFragment : Fragment() { exchangeView.text = cleanExchange(event.exchangeBaseUrl) } - private fun bind(event: PaymentTransaction) { + private fun bind(event: PaymentHistoryEvent) { amountPaidWithFeesView.text = event.amountPaidWithFees.toString() val fee = event.amountPaidWithFees - event.orderShortInfo.amount bindOrderAndFee(event.orderShortInfo, fee) } - private fun bind(event: RefundTransaction) { + private fun bind(event: RefundHistoryEvent) { amountPaidWithFeesLabel.text = getString(R.string.transaction_refund) amountPaidWithFeesView.setTextColor(getColor(requireContext(), R.color.green)) amountPaidWithFeesView.text = diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt index 549b2a8..850a3bb 100644 --- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt +++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt @@ -18,70 +18,62 @@ package net.taler.wallet.transactions import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.asLiveData -import androidx.lifecycle.switchMap import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.readValue +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.channels.awaitClose -import kotlinx.coroutines.flow.callbackFlow -import kotlinx.coroutines.flow.onCompletion -import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.launch import net.taler.wallet.backend.WalletBackendApi +import net.taler.wallet.history.History +import net.taler.wallet.history.HistoryEvent +import org.json.JSONObject sealed class TransactionsResult { object Error : TransactionsResult() - class Success(val transactions: Transactions) : TransactionsResult() + class Success(val transactions: History) : TransactionsResult() } -@Suppress("EXPERIMENTAL_API_USAGE") class TransactionManager( private val walletBackendApi: WalletBackendApi, + private val scope: CoroutineScope, private val mapper: ObjectMapper ) { private val mProgress = MutableLiveData<Boolean>() val progress: LiveData<Boolean> = mProgress - val showAll = MutableLiveData<Boolean>() - var selectedCurrency: String? = null - var selectedEvent: Transaction? = null + var selectedEvent: HistoryEvent? = null - val transactions: LiveData<TransactionsResult> = showAll.switchMap { showAll -> - loadTransactions(showAll) - .onStart { mProgress.postValue(true) } - .onCompletion { mProgress.postValue(false) } - .asLiveData(Dispatchers.IO) - } + private val mTransactions = MutableLiveData<TransactionsResult>() + val transactions: LiveData<TransactionsResult> = mTransactions - private fun loadTransactions(showAll: Boolean) = callbackFlow { + fun loadTransactions() { + mProgress.postValue(true) walletBackendApi.sendRequest("getHistory", null) { isError, result -> - launch(Dispatchers.Default) { - if (isError) { - offer(TransactionsResult.Error) - close() - return@launch - } - val transactions = Transactions() - val json = result.getJSONArray("history") - val currency = selectedCurrency - for (i in 0 until json.length()) { - val event: Transaction = mapper.readValue(json.getString(i)) - event.json = json.getJSONObject(i) - if (currency == null || event.isCurrency(currency)) { - transactions.add(event) - } - } - transactions.reverse() // show latest first - val filtered = - if (showAll) transactions else transactions.filter { it.showToUser } as Transactions - offer(TransactionsResult.Success(filtered)) - close() + scope.launch(Dispatchers.Default) { + onTransactionsLoaded(isError, result) + } + } + } + + private fun onTransactionsLoaded(isError: Boolean, result: JSONObject) { + if (isError) { + mTransactions.postValue(TransactionsResult.Error) + return + } + val transactions = History() + val json = result.getJSONArray("history") + val currency = selectedCurrency + for (i in 0 until json.length()) { + val event: HistoryEvent = mapper.readValue(json.getString(i)) + if (event.showToUser && (currency == null || event.isCurrency(currency))) { + transactions.add(event) } } - awaitClose() + transactions.reverse() // show latest first + mProgress.postValue(false) + mTransactions.postValue(TransactionsResult.Success(transactions)) } } diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt index e7adaf1..2b5337f 100644 --- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt @@ -42,17 +42,18 @@ import net.taler.common.fadeIn import net.taler.common.fadeOut import net.taler.wallet.MainViewModel import net.taler.wallet.R +import net.taler.wallet.history.HistoryEvent -interface OnEventClickListener { - fun onTransactionClicked(transaction: Transaction) +interface OnTransactionClickListener { + fun onTransactionClicked(transaction: HistoryEvent) } -class TransactionsFragment : Fragment(), OnEventClickListener, ActionMode.Callback { +class TransactionsFragment : Fragment(), OnTransactionClickListener, ActionMode.Callback { private val model: MainViewModel by activityViewModels() private val transactionManager by lazy { model.transactionManager } - private val transactionAdapter by lazy { TransactionAdapter(model.devMode.value == true, this) } + private val transactionAdapter by lazy { TransactionAdapter(this) } private val currency by lazy { transactionManager.selectedCurrency!! } private var tracker: SelectionTracker<String>? = null private var actionMode: ActionMode? = null @@ -109,7 +110,7 @@ class TransactionsFragment : Fragment(), OnEventClickListener, ActionMode.Callba }) // kicks off initial load, needs to be adapted if showAll state is ever saved - if (savedInstanceState == null) transactionManager.showAll.value = model.devMode.value + if (savedInstanceState == null) transactionManager.loadTransactions() } override fun onActivityCreated(savedInstanceState: Bundle?) { @@ -137,14 +138,11 @@ class TransactionsFragment : Fragment(), OnEventClickListener, ActionMode.Callba } } - override fun onTransactionClicked(transaction: Transaction) { + override fun onTransactionClicked(transaction: HistoryEvent) { if (actionMode != null) return // don't react on clicks while in action mode if (transaction.detailPageLayout != 0) { transactionManager.selectedEvent = transaction findNavController().navigate(R.id.action_nav_transaction_detail) - } else if (model.devMode.value == true) { - JsonDialogFragment.new(transaction.json.toString(2)) - .show(parentFragmentManager, null) } } diff --git a/wallet/src/main/res/drawable/ic_history.xml b/wallet/src/main/res/drawable/ic_history.xml new file mode 100644 index 0000000..d9f75ea --- /dev/null +++ b/wallet/src/main/res/drawable/ic_history.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z" /> +</vector> diff --git a/wallet/src/main/res/layout/fragment_transactions.xml b/wallet/src/main/res/layout/fragment_transactions.xml index aaf638c..547da24 100644 --- a/wallet/src/main/res/layout/fragment_transactions.xml +++ b/wallet/src/main/res/layout/fragment_transactions.xml @@ -27,7 +27,7 @@ android:scrollbars="vertical" android:visibility="invisible" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" - tools:listitem="@layout/list_item_transaction" + tools:listitem="@layout/list_item_history" tools:visibility="visible" /> <TextView diff --git a/wallet/src/main/res/layout/list_item_transaction.xml b/wallet/src/main/res/layout/list_item_history.xml index 2fabe1d..2fabe1d 100644 --- a/wallet/src/main/res/layout/list_item_transaction.xml +++ b/wallet/src/main/res/layout/list_item_history.xml diff --git a/wallet/src/main/res/menu/activity_main_drawer.xml b/wallet/src/main/res/menu/activity_main_drawer.xml index 896ff69..62abc32 100644 --- a/wallet/src/main/res/menu/activity_main_drawer.xml +++ b/wallet/src/main/res/menu/activity_main_drawer.xml @@ -18,7 +18,9 @@ xmlns:tools="http://schemas.android.com/tools" tools:showIn="@layout/activity_main"> - <group android:checkableBehavior="single"> + <group + android:id="@+id/nav_group_main" + android:checkableBehavior="single"> <item android:id="@+id/nav_home" android:icon="@drawable/ic_account_balance_wallet" @@ -28,10 +30,25 @@ android:id="@+id/nav_settings" android:icon="@drawable/ic_settings" android:title="@string/menu_settings" /> - <item - android:id="@+id/nav_pending_operations" - android:icon="@drawable/ic_sync" - android:title="@string/pending_operations_title" /> </group> + <item + android:id="@+id/nav_dev" + android:title="@string/settings_dev_mode"> + <menu> + <group + android:id="@+id/nav_group_dev" + android:checkableBehavior="single"> + <item + android:id="@+id/nav_pending_operations" + android:icon="@drawable/ic_sync" + android:title="@string/pending_operations_title" /> + <item + android:id="@+id/nav_history" + android:icon="@drawable/ic_history" + android:title="@string/nav_history" /> + </group> + </menu> + </item> + </menu> diff --git a/wallet/src/main/res/navigation/nav_graph.xml b/wallet/src/main/res/navigation/nav_graph.xml index f8d515e..8e717c1 100644 --- a/wallet/src/main/res/navigation/nav_graph.xml +++ b/wallet/src/main/res/navigation/nav_graph.xml @@ -129,6 +129,12 @@ tools:layout="@layout/fragment_pending_operations" /> <fragment + android:id="@+id/nav_history" + android:name="net.taler.wallet.history.DevHistoryFragment" + android:label="@string/nav_history" + tools:layout="@layout/fragment_transactions" /> + + <fragment android:id="@+id/errorFragment" android:name="net.taler.wallet.withdraw.ErrorFragment" android:label="@string/nav_error" @@ -143,6 +149,10 @@ app:destination="@id/nav_pending_operations" /> <action + android:id="@+id/action_global_history" + app:destination="@id/nav_history" /> + + <action android:id="@+id/action_nav_transaction_detail" app:destination="@id/nav_transactions_detail" /> diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml index a28545f..56ff2ef 100644 --- a/wallet/src/main/res/values/strings.xml +++ b/wallet/src/main/res/values/strings.xml @@ -40,6 +40,7 @@ GNU Taler is immune against many types of fraud, such as phishing of credit card <string name="nav_prompt_withdraw">Withdraw Digital Cash</string> <string name="nav_exchange_tos">Exchange\'s Terms of Service</string> <string name="nav_exchange_fees">Exchange Fees</string> + <string name="nav_history">Event History</string> <string name="nav_error">Error</string> <string name="button_back">Go Back</string> diff --git a/wallet/src/test/java/net/taler/wallet/transactions/TransactionTest.kt b/wallet/src/test/java/net/taler/wallet/history/HistoryEventTest.kt index 6549434..109b8dc 100644 --- a/wallet/src/test/java/net/taler/wallet/transactions/TransactionTest.kt +++ b/wallet/src/test/java/net/taler/wallet/history/HistoryEventTest.kt @@ -14,20 +14,38 @@ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -package net.taler.wallet.transactions +package net.taler.wallet.history import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.KotlinModule import com.fasterxml.jackson.module.kotlin.readValue import net.taler.common.Amount -import net.taler.wallet.transactions.RefreshReason.PAY -import net.taler.wallet.transactions.ReserveType.MANUAL +import net.taler.wallet.history.ExchangeAddedEvent +import net.taler.wallet.history.ExchangeUpdatedEvent +import net.taler.wallet.history.HistoryEvent +import net.taler.wallet.history.OrderAcceptedHistoryEvent +import net.taler.wallet.history.OrderRedirectedHistoryEvent +import net.taler.wallet.history.OrderRefusedHistoryEvent +import net.taler.wallet.history.OrderShortInfo +import net.taler.wallet.history.PaymentAbortedHistoryEvent +import net.taler.wallet.history.PaymentHistoryEvent +import net.taler.wallet.history.RefreshHistoryEvent +import net.taler.wallet.history.RefreshReason.PAY +import net.taler.wallet.history.RefundHistoryEvent +import net.taler.wallet.history.ReserveBalanceUpdatedHistoryEvent +import net.taler.wallet.history.ReserveShortInfo +import net.taler.wallet.history.ReserveType.MANUAL +import net.taler.wallet.history.TipAcceptedHistoryEvent +import net.taler.wallet.history.TipDeclinedHistoryEvent +import net.taler.wallet.history.UnknownHistoryEvent +import net.taler.wallet.history.WithdrawHistoryEvent +import net.taler.wallet.history.WithdrawalSourceReserve import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Test import kotlin.random.Random -class TransactionTest { +class HistoryEventTest { private val mapper = ObjectMapper().registerModule(KotlinModule()) @@ -111,7 +129,7 @@ class TransactionTest { "reservePub": "BRT2P0YMQSD5F48V9XHVNH73ZTS6EZC0KCQCPGPZQWTSQB77615G" } }""".trimIndent() - val transaction: ReserveBalanceUpdatedTransaction = mapper.readValue(json) + val transaction: ReserveBalanceUpdatedHistoryEvent = mapper.readValue(json) assertEquals(timestamp, transaction.timestamp.ms) assertEquals("TESTKUDOS:23", transaction.reserveAwaitedAmount.toJSONString()) @@ -137,7 +155,7 @@ class TransactionTest { "reservePub": "BRT2P0YMQSD5F48V9XHVNH73ZTS6EZC0KCQCPGPZQWTSQB77615G" } }""".trimIndent() - val event: WithdrawTransaction = mapper.readValue(json) + val event: WithdrawHistoryEvent = mapper.readValue(json) assertEquals( "974FT7JDNR20EQKNR21G1HV9PB6T5AZHYHX9NHR51Q30ZK3T10S0", @@ -186,7 +204,7 @@ class TransactionTest { "t_ms": $timestamp } }""".trimIndent() - val transaction: OrderAcceptedTransaction = mapper.readValue(json) + val transaction: OrderAcceptedHistoryEvent = mapper.readValue(json) assertEquals(orderShortInfo, transaction.orderShortInfo) assertEquals(timestamp, transaction.timestamp.ms) @@ -208,7 +226,7 @@ class TransactionTest { "t_ms": $timestamp } }""".trimIndent() - val transaction: OrderRefusedTransaction = mapper.readValue(json) + val transaction: OrderRefusedHistoryEvent = mapper.readValue(json) assertEquals(orderShortInfo, transaction.orderShortInfo) assertEquals(timestamp, transaction.timestamp.ms) @@ -234,7 +252,7 @@ class TransactionTest { "numCoins": 6, "amountPaidWithFees": "KUDOS:0.6" }""".trimIndent() - val event: PaymentTransaction = mapper.readValue(json) + val event: PaymentHistoryEvent = mapper.readValue(json) assertEquals(orderShortInfo, event.orderShortInfo) assertEquals(false, event.replay) @@ -263,7 +281,7 @@ class TransactionTest { "numCoins": 6, "amountPaidWithFees": "KUDOS:0.6" }""".trimIndent() - val event: PaymentTransaction = mapper.readValue(json) + val event: PaymentHistoryEvent = mapper.readValue(json) assertEquals(orderShortInfo, event.orderShortInfo) assertEquals(true, event.replay) @@ -290,7 +308,7 @@ class TransactionTest { }, "amountLost": "KUDOS:0.1" }""".trimIndent() - val transaction: PaymentAbortedTransaction = mapper.readValue(json) + val transaction: PaymentAbortedHistoryEvent = mapper.readValue(json) assertEquals(orderShortInfo, transaction.orderShortInfo) assertEquals("KUDOS:0.1", transaction.amountLost.toJSONString()) @@ -308,7 +326,7 @@ class TransactionTest { "tipId": "tip-accepted;898724XGQ1GGMZB4WY3KND582NSP74FZ60BX0Y87FF81H0FJ8XD0", "tipRaw": "KUDOS:4" }""".trimIndent() - val transaction: TipAcceptedTransaction = mapper.readValue(json) + val transaction: TipAcceptedHistoryEvent = mapper.readValue(json) assertEquals( "tip-accepted;898724XGQ1GGMZB4WY3KND582NSP74FZ60BX0Y87FF81H0FJ8XD0", @@ -329,7 +347,7 @@ class TransactionTest { "tipId": "tip-accepted;998724XGQ1GGMZB4WY3KND582NSP74FZ60BX0Y87FF81H0FJ8XD0", "tipAmount": "KUDOS:4" }""".trimIndent() - val transaction: TipDeclinedTransaction = mapper.readValue(json) + val transaction: TipDeclinedHistoryEvent = mapper.readValue(json) assertEquals( "tip-accepted;998724XGQ1GGMZB4WY3KND582NSP74FZ60BX0Y87FF81H0FJ8XD0", @@ -359,7 +377,7 @@ class TransactionTest { "amountRefundedInvalid": "KUDOS:0.5", "amountRefundedEffective": "KUDOS:0.4" }""".trimIndent() - val event: RefundTransaction = mapper.readValue(json) + val event: RefundHistoryEvent = mapper.readValue(json) assertEquals("refund;998724", event.refundGroupId) assertEquals("KUDOS:1", event.amountRefundedRaw.toJSONString()) @@ -385,7 +403,7 @@ class TransactionTest { "numOutputCoins": 0, "numRefreshedInputCoins": 1 }""".trimIndent() - val event: RefreshTransaction = mapper.readValue(json) + val event: RefreshHistoryEvent = mapper.readValue(json) assertEquals("KUDOS:0", event.amountRefreshedEffective.toJSONString()) assertEquals("KUDOS:1", event.amountRefreshedRaw.toJSONString()) @@ -420,7 +438,7 @@ class TransactionTest { "t_ms": $timestamp } }""".trimIndent() - val transaction: OrderRedirectedTransaction = mapper.readValue(json) + val transaction: OrderRedirectedHistoryEvent = mapper.readValue(json) assertEquals( "898724XGQ1GGMZB4WY3KND582NSP74FZ60BX0Y87FF81H0FJ8XD0", @@ -461,9 +479,9 @@ class TransactionTest { }, "eventId": "does-not-exist;898724XGQ1GGMZB4WY3KND582NSP74FZ60BX0Y87FF81H0FJ8XD0" }""".trimIndent() - val event: Transaction = mapper.readValue(json) + val event: HistoryEvent = mapper.readValue(json) - assertEquals(UnknownTransaction::class.java, event.javaClass) + assertEquals(UnknownHistoryEvent::class.java, event.javaClass) assertEquals(timestamp, event.timestamp.ms) } diff --git a/wallet/src/test/java/net/taler/wallet/transactions/ReserveTransactionTest.kt b/wallet/src/test/java/net/taler/wallet/history/ReserveHistoryEventTest.kt index 4a3c75b..f09d7b6 100644 --- a/wallet/src/test/java/net/taler/wallet/transactions/ReserveTransactionTest.kt +++ b/wallet/src/test/java/net/taler/wallet/history/ReserveHistoryEventTest.kt @@ -14,16 +14,17 @@ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -package net.taler.wallet.transactions +package net.taler.wallet.history import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.KotlinModule import com.fasterxml.jackson.module.kotlin.readValue +import net.taler.wallet.history.ReserveDepositTransaction import org.junit.Assert.assertEquals import org.junit.Test import kotlin.random.Random -class ReserveTransactionTest { +class ReserveHistoryEventTest { private val mapper = ObjectMapper().registerModule(KotlinModule()) |