summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
authorTorsten Grote <t@grobox.de>2020-01-22 09:20:25 -0300
committerTorsten Grote <t@grobox.de>2020-01-22 09:37:50 -0300
commit2371ad0e4492e31648c0451b0b3fa799f7a99a42 (patch)
treeee379941923c159c2788fc994d6761c69b69b61b /app/src
parent1ec5fbd1d342f42975dc8820efe697f332ea3c15 (diff)
downloadwallet-android-2371ad0e4492e31648c0451b0b3fa799f7a99a42.tar.gz
wallet-android-2371ad0e4492e31648c0451b0b3fa799f7a99a42.tar.bz2
wallet-android-2371ad0e4492e31648c0451b0b3fa799f7a99a42.zip
Show history event JSON in debug builds
Diffstat (limited to 'app/src')
-rw-r--r--app/src/main/java/net/taler/wallet/WalletViewModel.kt9
-rw-r--r--app/src/main/java/net/taler/wallet/history/HistoryEvent.kt5
-rw-r--r--app/src/main/java/net/taler/wallet/history/JsonDialogFragment.kt50
-rw-r--r--app/src/main/java/net/taler/wallet/history/WalletHistory.kt13
-rw-r--r--app/src/main/java/net/taler/wallet/history/WalletHistoryAdapter.kt254
-rw-r--r--app/src/main/res/layout/fragment_json.xml25
-rw-r--r--app/src/main/res/layout/history_payment.xml1
-rw-r--r--app/src/main/res/layout/history_receive.xml1
-rw-r--r--app/src/main/res/layout/history_row.xml1
-rw-r--r--app/src/test/java/net/taler/wallet/history/HistoryEventTest.kt29
10 files changed, 232 insertions, 156 deletions
diff --git a/app/src/main/java/net/taler/wallet/WalletViewModel.kt b/app/src/main/java/net/taler/wallet/WalletViewModel.kt
index f932cff..bc8c7e2 100644
--- a/app/src/main/java/net/taler/wallet/WalletViewModel.kt
+++ b/app/src/main/java/net/taler/wallet/WalletViewModel.kt
@@ -29,6 +29,7 @@ import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onStart
import net.taler.wallet.backend.WalletBackendApi
import net.taler.wallet.history.History
+import net.taler.wallet.history.HistoryEvent
import org.json.JSONObject
const val TAG = "taler-wallet"
@@ -214,7 +215,13 @@ class WalletViewModel(val app: Application) : AndroidViewModel(app) {
close()
return@sendRequest
}
- val history: History = mapper.readValue(result.getString("history"))
+ 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
mHistoryProgress.postValue(false)
offer(if (showAll) history else history.filter { it.showToUser } as History)
diff --git a/app/src/main/java/net/taler/wallet/history/HistoryEvent.kt b/app/src/main/java/net/taler/wallet/history/HistoryEvent.kt
index d866c72..e2a7c7e 100644
--- a/app/src/main/java/net/taler/wallet/history/HistoryEvent.kt
+++ b/app/src/main/java/net/taler/wallet/history/HistoryEvent.kt
@@ -26,6 +26,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo.As.PROPERTY
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME
import net.taler.wallet.ParsedAmount.Companion.parseAmount
import net.taler.wallet.R
+import org.json.JSONObject
enum class ReserveType {
/**
@@ -130,7 +131,9 @@ abstract class HistoryEvent(
@get:DrawableRes
open val icon: Int = R.drawable.ic_account_balance,
open val showToUser: Boolean = false
-)
+) {
+ open lateinit var json: JSONObject
+}
@JsonTypeName("exchange-added")
diff --git a/app/src/main/java/net/taler/wallet/history/JsonDialogFragment.kt b/app/src/main/java/net/taler/wallet/history/JsonDialogFragment.kt
new file mode 100644
index 0000000..a9ed514
--- /dev/null
+++ b/app/src/main/java/net/taler/wallet/history/JsonDialogFragment.kt
@@ -0,0 +1,50 @@
+/*
+ This file is part of GNU Taler
+ (C) 2019 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.ViewGroup
+import androidx.fragment.app.DialogFragment
+import kotlinx.android.synthetic.main.fragment_json.*
+import net.taler.wallet.R
+
+class JsonDialogFragment : DialogFragment() {
+
+ companion object {
+ fun new(json: String): JsonDialogFragment {
+ return JsonDialogFragment().apply {
+ arguments = Bundle().apply { putString("json", json) }
+ }
+ }
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ return inflater.inflate(R.layout.fragment_json, container, false)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ val json = arguments!!.getString("json")
+ jsonView.text = json
+ }
+
+}
diff --git a/app/src/main/java/net/taler/wallet/history/WalletHistory.kt b/app/src/main/java/net/taler/wallet/history/WalletHistory.kt
index 0b2e214..5652e66 100644
--- a/app/src/main/java/net/taler/wallet/history/WalletHistory.kt
+++ b/app/src/main/java/net/taler/wallet/history/WalletHistory.kt
@@ -30,15 +30,19 @@ import kotlinx.android.synthetic.main.fragment_show_history.*
import net.taler.wallet.R
import net.taler.wallet.WalletViewModel
+interface OnEventClickListener {
+ fun onEventClicked(event: HistoryEvent)
+}
+
/**
* Wallet history.
*
*/
-class WalletHistory : Fragment() {
+class WalletHistory : Fragment(), OnEventClickListener {
lateinit var model: WalletViewModel
private lateinit var showAllItem: MenuItem
- private val historyAdapter = WalletHistoryAdapter()
+ private val historyAdapter = WalletHistoryAdapter(this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -98,6 +102,11 @@ class WalletHistory : Fragment() {
if (savedInstanceState == null) model.historyShowAll.value = false
}
+ override fun onEventClicked(event: HistoryEvent) {
+ JsonDialogFragment.new(event.json.toString(4))
+ .show(parentFragmentManager, null)
+ }
+
companion object {
const val TAG = "taler-wallet"
}
diff --git a/app/src/main/java/net/taler/wallet/history/WalletHistoryAdapter.kt b/app/src/main/java/net/taler/wallet/history/WalletHistoryAdapter.kt
index 7bb8b33..7df8d0c 100644
--- a/app/src/main/java/net/taler/wallet/history/WalletHistoryAdapter.kt
+++ b/app/src/main/java/net/taler/wallet/history/WalletHistoryAdapter.kt
@@ -29,13 +29,16 @@ import androidx.annotation.CallSuper
import androidx.core.net.toUri
import androidx.recyclerview.widget.RecyclerView.Adapter
import androidx.recyclerview.widget.RecyclerView.ViewHolder
+import net.taler.wallet.BuildConfig
import net.taler.wallet.ParsedAmount
import net.taler.wallet.ParsedAmount.Companion.parseAmount
import net.taler.wallet.R
-internal class WalletHistoryAdapter(private var history: History = History()) :
- Adapter<HistoryEventViewHolder>() {
+internal class WalletHistoryAdapter(
+ private val listener: OnEventClickListener,
+ private var history: History = History()
+) : Adapter<WalletHistoryAdapter.HistoryEventViewHolder>() {
init {
setHasStableIds(false)
@@ -64,158 +67,163 @@ internal class WalletHistoryAdapter(private var history: History = History()) :
this.notifyDataSetChanged()
}
-}
-
-internal abstract class HistoryEventViewHolder(protected val v: View) : ViewHolder(v) {
+ internal abstract inner class HistoryEventViewHolder(protected val v: View) : ViewHolder(v) {
- 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 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)
- @CallSuper
- open fun bind(event: HistoryEvent) {
- icon.setImageResource(event.icon)
- if (event.title == 0) title.text = event::class.java.simpleName
- else title.setText(event.title)
- time.text = getRelativeTime(event.timestamp.ms)
- }
+ @CallSuper
+ open fun bind(event: HistoryEvent) {
+ if (BuildConfig.DEBUG) { // doesn't produce recycling issues, no need to cover all cases
+ v.setOnClickListener { listener.onEventClicked(event) }
+ } else {
+ v.background = null
+ }
+ icon.setImageResource(event.icon)
+ if (event.title == 0) title.text = event::class.java.simpleName
+ else title.setText(event.title)
+ time.text = getRelativeTime(event.timestamp.ms)
+ }
- private fun getRelativeTime(timestamp: Long): CharSequence {
- val now = System.currentTimeMillis()
- return if (now - timestamp > DAY_IN_MILLIS * 2) {
- formatDateTime(
- v.context,
- timestamp,
- FORMAT_SHOW_TIME or FORMAT_SHOW_DATE or FORMAT_ABBREV_MONTH or FORMAT_NO_YEAR
- )
- } else {
- getRelativeTimeSpanString(timestamp, now, MINUTE_IN_MILLIS, FORMAT_ABBREV_RELATIVE)
+ private fun getRelativeTime(timestamp: Long): CharSequence {
+ val now = System.currentTimeMillis()
+ return if (now - timestamp > DAY_IN_MILLIS * 2) {
+ formatDateTime(
+ v.context,
+ timestamp,
+ FORMAT_SHOW_TIME or FORMAT_SHOW_DATE or FORMAT_ABBREV_MONTH or FORMAT_NO_YEAR
+ )
+ } else {
+ getRelativeTimeSpanString(timestamp, now, MINUTE_IN_MILLIS, FORMAT_ABBREV_RELATIVE)
+ }
}
+
}
-}
+ internal inner class GenericHistoryEventViewHolder(v: View) : HistoryEventViewHolder(v) {
-internal class GenericHistoryEventViewHolder(v: View) : HistoryEventViewHolder(v) {
-
- private val info: TextView = v.findViewById(R.id.info)
-
- override fun bind(event: HistoryEvent) {
- super.bind(event)
- info.text = when (event) {
- is ExchangeAddedEvent -> event.exchangeBaseUrl
- is ExchangeUpdatedEvent -> event.exchangeBaseUrl
- is ReserveBalanceUpdatedEvent -> parseAmount(event.amountReserveBalance).toString()
- is HistoryPaymentSentEvent -> event.orderShortInfo.summary
- is HistoryOrderAcceptedEvent -> event.orderShortInfo.summary
- is HistoryOrderRefusedEvent -> event.orderShortInfo.summary
- is HistoryOrderRedirectedEvent -> event.newOrderShortInfo.summary
- else -> ""
+ private val info: TextView = v.findViewById(R.id.info)
+
+ override fun bind(event: HistoryEvent) {
+ super.bind(event)
+ info.text = when (event) {
+ is ExchangeAddedEvent -> event.exchangeBaseUrl
+ is ExchangeUpdatedEvent -> event.exchangeBaseUrl
+ is ReserveBalanceUpdatedEvent -> parseAmount(event.amountReserveBalance).toString()
+ is HistoryPaymentSentEvent -> event.orderShortInfo.summary
+ is HistoryOrderAcceptedEvent -> event.orderShortInfo.summary
+ is HistoryOrderRefusedEvent -> event.orderShortInfo.summary
+ is HistoryOrderRedirectedEvent -> event.newOrderShortInfo.summary
+ else -> ""
+ }
}
- }
-}
+ }
-internal class HistoryReceiveViewHolder(v: View) : HistoryEventViewHolder(v) {
+ internal inner class HistoryReceiveViewHolder(v: View) : HistoryEventViewHolder(v) {
- private val summary: TextView = v.findViewById(R.id.summary)
- private val amountWithdrawn: TextView = v.findViewById(R.id.amountWithdrawn)
- private val feeLabel: TextView = v.findViewById(R.id.feeLabel)
- private val fee: TextView = v.findViewById(R.id.fee)
+ private val summary: TextView = v.findViewById(R.id.summary)
+ private val amountWithdrawn: TextView = v.findViewById(R.id.amountWithdrawn)
+ private val feeLabel: TextView = v.findViewById(R.id.feeLabel)
+ private val fee: TextView = v.findViewById(R.id.fee)
- override fun bind(event: HistoryEvent) {
- super.bind(event)
- when(event) {
- is HistoryWithdrawnEvent -> bind(event)
- is HistoryRefundedEvent -> bind(event)
- is HistoryTipAcceptedEvent -> bind(event)
- is HistoryTipDeclinedEvent -> bind(event)
+ override fun bind(event: HistoryEvent) {
+ super.bind(event)
+ when (event) {
+ is HistoryWithdrawnEvent -> bind(event)
+ is HistoryRefundedEvent -> bind(event)
+ is HistoryTipAcceptedEvent -> bind(event)
+ is HistoryTipDeclinedEvent -> bind(event)
+ }
}
- }
- private fun bind(event: HistoryWithdrawnEvent) {
- title.text = getHostname(event.exchangeBaseUrl)
- summary.setText(event.title)
+ private fun bind(event: HistoryWithdrawnEvent) {
+ title.text = getHostname(event.exchangeBaseUrl)
+ summary.setText(event.title)
- val parsedEffective = parseAmount(event.amountWithdrawnEffective)
- val parsedRaw = parseAmount(event.amountWithdrawnRaw)
- showAmounts(parsedEffective, parsedRaw)
- }
+ val parsedEffective = parseAmount(event.amountWithdrawnEffective)
+ val parsedRaw = parseAmount(event.amountWithdrawnRaw)
+ showAmounts(parsedEffective, parsedRaw)
+ }
- private fun bind(event: HistoryRefundedEvent) {
- title.text = event.orderShortInfo.summary
- summary.setText(event.title)
+ private fun bind(event: HistoryRefundedEvent) {
+ title.text = event.orderShortInfo.summary
+ summary.setText(event.title)
- val parsedEffective = parseAmount(event.amountRefundedEffective)
- val parsedRaw = parseAmount(event.amountRefundedRaw)
- showAmounts(parsedEffective, parsedRaw)
- }
+ val parsedEffective = parseAmount(event.amountRefundedEffective)
+ val parsedRaw = parseAmount(event.amountRefundedRaw)
+ showAmounts(parsedEffective, parsedRaw)
+ }
- private fun bind(event: HistoryTipAcceptedEvent) {
- title.setText(event.title)
- summary.text = null
- val amount = parseAmount(event.tipRaw)
- showAmounts(amount, amount)
- }
+ private fun bind(event: HistoryTipAcceptedEvent) {
+ title.setText(event.title)
+ summary.text = null
+ val amount = parseAmount(event.tipRaw)
+ showAmounts(amount, amount)
+ }
- private fun bind(event: HistoryTipDeclinedEvent) {
- title.setText(event.title)
- summary.text = null
- val amount = parseAmount(event.tipAmount)
- showAmounts(amount, amount)
- amountWithdrawn.paintFlags = amountWithdrawn.paintFlags or STRIKE_THRU_TEXT_FLAG
- }
+ private fun bind(event: HistoryTipDeclinedEvent) {
+ title.setText(event.title)
+ summary.text = null
+ val amount = parseAmount(event.tipAmount)
+ showAmounts(amount, amount)
+ amountWithdrawn.paintFlags = amountWithdrawn.paintFlags or STRIKE_THRU_TEXT_FLAG
+ }
- private fun showAmounts(effective: ParsedAmount, raw: ParsedAmount) {
- amountWithdrawn.text = "+$raw"
- val calculatedFee = raw - effective
- if (calculatedFee.isZero()) {
- fee.visibility = GONE
- feeLabel.visibility = GONE
- } else {
- fee.text = "-$calculatedFee"
- fee.visibility = VISIBLE
- feeLabel.visibility = VISIBLE
+ private fun showAmounts(effective: ParsedAmount, raw: ParsedAmount) {
+ amountWithdrawn.text = "+$raw"
+ val calculatedFee = raw - effective
+ if (calculatedFee.isZero()) {
+ fee.visibility = GONE
+ feeLabel.visibility = GONE
+ } else {
+ fee.text = "-$calculatedFee"
+ fee.visibility = VISIBLE
+ feeLabel.visibility = VISIBLE
+ }
+ amountWithdrawn.paintFlags = fee.paintFlags
+ }
+
+ private fun getHostname(url: String): String {
+ return url.toUri().host!!
}
- amountWithdrawn.paintFlags = fee.paintFlags
- }
- private fun getHostname(url: String): String {
- return url.toUri().host!!
}
-}
+ internal inner class HistoryPaymentViewHolder(v: View) : HistoryEventViewHolder(v) {
-internal class HistoryPaymentViewHolder(v: View) : HistoryEventViewHolder(v) {
+ private val summary: TextView = v.findViewById(R.id.summary)
+ private val amountPaidWithFees: TextView = v.findViewById(R.id.amountPaidWithFees)
- private val summary: TextView = v.findViewById(R.id.summary)
- private val amountPaidWithFees: TextView = v.findViewById(R.id.amountPaidWithFees)
+ override fun bind(event: HistoryEvent) {
+ super.bind(event)
+ summary.setText(event.title)
+ when (event) {
+ is HistoryPaymentSentEvent -> bind(event)
+ is HistoryPaymentAbortedEvent -> bind(event)
+ is HistoryRefreshedEvent -> bind(event)
+ }
+ }
- override fun bind(event: HistoryEvent) {
- super.bind(event)
- summary.setText(event.title)
- when(event) {
- is HistoryPaymentSentEvent -> bind(event)
- is HistoryPaymentAbortedEvent -> bind(event)
- is HistoryRefreshedEvent -> bind(event)
+ private fun bind(event: HistoryPaymentSentEvent) {
+ title.text = event.orderShortInfo.summary
+ amountPaidWithFees.text = "-${parseAmount(event.amountPaidWithFees)}"
}
- }
- private fun bind(event: HistoryPaymentSentEvent) {
- title.text = event.orderShortInfo.summary
- amountPaidWithFees.text = "-${parseAmount(event.amountPaidWithFees)}"
- }
+ private fun bind(event: HistoryPaymentAbortedEvent) {
+ title.text = event.orderShortInfo.summary
+ amountPaidWithFees.text = "-${parseAmount(event.amountLost)}"
+ }
- private fun bind(event: HistoryPaymentAbortedEvent) {
- title.text = event.orderShortInfo.summary
- amountPaidWithFees.text = "-${parseAmount(event.amountLost)}"
- }
+ private fun bind(event: HistoryRefreshedEvent) {
+ title.text = ""
+ val fee =
+ parseAmount(event.amountRefreshedRaw) - parseAmount(event.amountRefreshedEffective)
+ if (fee.isZero()) amountPaidWithFees.text = null
+ else amountPaidWithFees.text = "-$fee"
+ }
- private fun bind(event: HistoryRefreshedEvent) {
- title.text = ""
- val fee =
- parseAmount(event.amountRefreshedRaw) - parseAmount(event.amountRefreshedEffective)
- if (fee.isZero()) amountPaidWithFees.text = null
- else amountPaidWithFees.text = "-$fee"
}
}
diff --git a/app/src/main/res/layout/fragment_json.xml b/app/src/main/res/layout/fragment_json.xml
new file mode 100644
index 0000000..fe40156
--- /dev/null
+++ b/app/src/main/res/layout/fragment_json.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/jsonView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_marginTop="8dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginBottom="8dp"
+ android:fontFamily="monospace"
+ android:textSize="12sp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:text="[JSON]" />
+
+</ScrollView>
diff --git a/app/src/main/res/layout/history_payment.xml b/app/src/main/res/layout/history_payment.xml
index cf03998..62bfa79 100644
--- a/app/src/main/res/layout/history_payment.xml
+++ b/app/src/main/res/layout/history_payment.xml
@@ -7,6 +7,7 @@
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
+ android:background="?android:selectableItemBackground"
android:layout_marginBottom="8dp">
<ImageView
diff --git a/app/src/main/res/layout/history_receive.xml b/app/src/main/res/layout/history_receive.xml
index 5b86e6a..adb6ecd 100644
--- a/app/src/main/res/layout/history_receive.xml
+++ b/app/src/main/res/layout/history_receive.xml
@@ -6,6 +6,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
+ android:background="?android:selectableItemBackground"
android:layout_marginEnd="16dp"
android:layout_marginBottom="8dp">
diff --git a/app/src/main/res/layout/history_row.xml b/app/src/main/res/layout/history_row.xml
index 394ad79..51647ee 100644
--- a/app/src/main/res/layout/history_row.xml
+++ b/app/src/main/res/layout/history_row.xml
@@ -4,6 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:background="?android:selectableItemBackground"
android:layout_margin="15dp">
<ImageView
diff --git a/app/src/test/java/net/taler/wallet/history/HistoryEventTest.kt b/app/src/test/java/net/taler/wallet/history/HistoryEventTest.kt
index f3d1a5e..9355161 100644
--- a/app/src/test/java/net/taler/wallet/history/HistoryEventTest.kt
+++ b/app/src/test/java/net/taler/wallet/history/HistoryEventTest.kt
@@ -441,33 +441,4 @@ class HistoryEventTest {
assertEquals(timestamp, event.timestamp.ms)
}
- @Test
- fun `test list of events as History`() {
- val builtIn = Random.nextBoolean()
- val json = """[
- {
- "type": "exchange-updated",
- "eventId": "exchange-updated;https%3A%2F%2Fexchange.test.taler.net%2F",
- "exchangeBaseUrl": "https:\/\/exchange.test.taler.net\/",
- "timestamp": {
- "t_ms": $timestamp
- }
- },
- {
- "type": "exchange-added",
- "builtIn": $builtIn,
- "eventId": "exchange-added;https%3A%2F%2Fexchange.test.taler.net%2F",
- "exchangeBaseUrl": "https:\/\/exchange.test.taler.net\/",
- "timestamp": {
- "t_ms": $timestamp
- }
- }
- ]""".trimIndent()
- val history: History = mapper.readValue(json)
-
- assertEquals(2, history.size)
- assertTrue(history[0] is ExchangeUpdatedEvent)
- assertTrue(history[1] is ExchangeAddedEvent)
- }
-
}