incorporate new sdk changes and refactor the way transactions and polling work

This commit is contained in:
Kevin Gorham 2019-07-15 03:15:24 -04:00
parent c3e59f4ed9
commit 9bee43be37
No known key found for this signature in database
GPG Key ID: ABA38E928749A19E
20 changed files with 341 additions and 314 deletions

View File

@ -14,7 +14,6 @@ import cash.z.android.wallet.sample.Servers
import cash.z.android.wallet.sample.WalletConfig
import cash.z.android.wallet.ui.util.Broom
import cash.z.wallet.sdk.block.*
import cash.z.wallet.sdk.dao.ClearedTransaction
import cash.z.wallet.sdk.data.*
import cash.z.wallet.sdk.ext.DEFAULT_BATCH_SIZE
import cash.z.wallet.sdk.ext.DEFAULT_RETRIES
@ -44,7 +43,6 @@ internal object SynchronizerModule {
private val pollingTransactionRepository = PollingTransactionRepository(
ZcashWalletApplication.instance,
walletConfig.dataDbName,
rustBackend,
DEFAULT_TRANSACTION_POLL_FREQUENCY_MILLIS
)
@ -168,25 +166,6 @@ internal object SynchronizerModule {
)
}
@JvmStatic
@Provides
@Singleton
fun provideManager(wallet: Wallet, repository: TransactionRepository, service: LightWalletService): ActiveTransactionManager {
return ActiveTransactionManager(repository, service, wallet)
}
//
// @JvmStatic
// @Provides
// @Singleton
// fun provideSynchronizer(
// processor: CompactBlockProcessor,
// repository: TransactionRepository,
// manager: ActiveTransactionManager,
// wallet: Wallet
// ): Synchronizer {
// return SdkSynchronizer(processor, repository, manager, wallet, DEFAULT_STALE_TOLERANCE)
// }
@JvmStatic
@Provides
@Singleton
@ -204,14 +183,6 @@ internal object SynchronizerModule {
)
}
// @JvmStatic
// @Provides
// @Singleton
// fun provideChipBucket(): ChipBucket {
// return InMemoryChipBucket()
// }
@JvmStatic
@Provides
@Singleton
@ -233,20 +204,20 @@ internal object SynchronizerModule {
@JvmStatic
@Provides
@Singleton
fun provideTransactionEncoder(wallet: Wallet, repository: TransactionRepository): RawTransactionEncoder {
fun provideTransactionEncoder(wallet: Wallet, repository: TransactionRepository): TransactionEncoder {
return WalletTransactionEncoder(wallet, repository)
}
@JvmStatic
@Provides
@Singleton
fun provideDataSynchronizer(
fun provideSynchronizer(
wallet: Wallet,
ledger: PollingTransactionRepository,
sender: TransactionSender,
processor: CompactBlockProcessor,
encoder: RawTransactionEncoder
): DataSynchronizer {
encoder: TransactionEncoder
): Synchronizer {
return StableSynchronizer(wallet, ledger, sender, processor, encoder)
}
}

View File

@ -7,7 +7,7 @@ import cash.z.android.wallet.PokerChipSeedProvider
import cash.z.android.wallet.ZcashWalletApplication
import cash.z.android.wallet.extention.Toaster
import cash.z.android.wallet.extention.tryIgnore
import cash.z.wallet.sdk.data.DataSynchronizer
import cash.z.wallet.sdk.data.Synchronizer
import cash.z.wallet.sdk.data.StableSynchronizer
import cash.z.wallet.sdk.ext.MINERS_FEE_ZATOSHI
import cash.z.wallet.sdk.ext.convertZecToZatoshi
@ -27,7 +27,7 @@ import kotlin.properties.ReadOnlyProperty
class SendReceiver : DaggerBroadcastReceiver() {
@Inject
lateinit var synchronizer: DataSynchronizer
lateinit var synchronizer: Synchronizer
@Inject
lateinit var rustBackend: RustBackendWelding

View File

@ -9,7 +9,7 @@ import kotlinx.coroutines.CoroutineScope
// TODO: reverse this hierarchy so that Polling depends on this class and has all the overhead specific to polling, leaving this class more streamlined and efficient
class StaticTransactionRepository(dataDbName: String, rustBackend: RustBackendWelding, dbCallback: (DerivedDataDb) -> Unit = {}) :
PollingTransactionRepository(ZcashWalletApplication.instance, dataDbName, rustBackend,2000L, dbCallback) {
PollingTransactionRepository(ZcashWalletApplication.instance, dataDbName,2000L) {
// override fun start(parentScope: CoroutineScope) {
// twig("starting repository ignored because this DB does not poll")

View File

@ -40,11 +40,11 @@ import cash.z.android.wallet.ui.util.Analytics.trackAction
import cash.z.android.wallet.ui.util.Analytics.trackCrash
import cash.z.android.wallet.ui.util.Analytics.trackFunnelStep
import cash.z.android.wallet.ui.util.Broom
import cash.z.wallet.sdk.data.DataSynchronizer
import cash.z.wallet.sdk.data.Synchronizer
import cash.z.wallet.sdk.data.StableSynchronizer
import cash.z.wallet.sdk.data.twig
import cash.z.wallet.sdk.db.isMined
import cash.z.wallet.sdk.db.isSubmitted
import cash.z.wallet.sdk.entity.isMined
import cash.z.wallet.sdk.entity.isSubmitted
import cash.z.wallet.sdk.ext.MINERS_FEE_ZATOSHI
import cash.z.wallet.sdk.ext.convertZatoshiToZecString
import cash.z.wallet.sdk.secure.Wallet
@ -59,7 +59,7 @@ class MainActivity : BaseActivity(), Animator.AnimatorListener, ScanFragment.Bar
@Inject
lateinit var synchronizer: DataSynchronizer
lateinit var synchronizer: Synchronizer
@Inject
lateinit var mainPresenter: MainPresenter
@ -101,7 +101,9 @@ class MainActivity : BaseActivity(), Animator.AnimatorListener, ScanFragment.Bar
super.onResume()
// chipBucket.restore()
launch {
synchronizer.onCriticalErrorListener = ::onCriticalError
synchronizer.onCriticalErrorHandler = ::onCriticalError
synchronizer.onProcessorErrorHandler = ::onProcessorError
synchronizer.onSubmissionErrorHandler = ::onCriticalError
synchronizer.start(this)
balancePresenter.start(this, synchronizer.balances())
mainPresenter.start()
@ -125,14 +127,14 @@ class MainActivity : BaseActivity(), Animator.AnimatorListener, ScanFragment.Bar
}
}
private fun onCriticalError(error: Throwable): Boolean {
private fun onCriticalError(error: Throwable?): Boolean {
Handler(Looper.getMainLooper()).post {
//TODO proper error parsing, with strongly typed exceptions
var title: String? = null
var message: String? = null
when {
(error.message?.contains("UNAVAILABLE") == true) -> {
(error?.message?.contains("UNAVAILABLE") == true) -> {
title = "Server Error!"
message = "Unable to reach the server. Either it is down or you are not connected to the internet."
}
@ -150,6 +152,12 @@ class MainActivity : BaseActivity(), Animator.AnimatorListener, ScanFragment.Bar
return true
}
private fun onProcessorError(error: Throwable?): Boolean {
twig("Error while processing blocks: $error")
twig("The processor will keep trying and then throw a critical error after a while so this can be ignored for now.")
return true
}
override fun onPause() {
super.onPause()
balancePresenter.stop()
@ -410,7 +418,7 @@ class MainActivity : BaseActivity(), Animator.AnimatorListener, ScanFragment.Bar
// For now, empty happens when back is pressed
if (value.isEmpty()) return
synchronizer.getPending()?.firstOrNull { it.memo.contains("#${value.hashCode()}") }?.let { existingTransaction ->
synchronizer.lastPending().firstOrNull { it.memo?.contains("#${value.hashCode()}") == true }?.let { existingTransaction ->
if (existingTransaction.isMined()) {
alert(title = "Successfully Redeemed!", message = "We scanned this one already and the funds went to this wallet!")
} else {
@ -534,7 +542,7 @@ class MainActivity : BaseActivity(), Animator.AnimatorListener, ScanFragment.Bar
fun buyProduct(product: Zcon1Store.CartItem) {
trackFunnelStep(PurchasedItem(product))
val balance = (synchronizer as StableSynchronizer).getBalance()
val balance = (synchronizer as StableSynchronizer).lastBalance()
if (balance.available < (product.zatoshiValue + MINERS_FEE_ZATOSHI)) {
val message = if (balance.total >= (product.zatoshiValue + MINERS_FEE_ZATOSHI)) {
"Sorry, some of your funds are still awaiting 10 network confirmations before they are available for spending! Try again after your \"amount syncing\" is zero."
@ -606,8 +614,8 @@ class MainActivity : BaseActivity(), Animator.AnimatorListener, ScanFragment.Bar
}
fun calculatePendingChipBalance(): Long {
return synchronizer.getPending()?.filter {
it.memo.toLowerCase().contains("poker chip") && !it.isMined()
return synchronizer.lastPending()?.filter {
it.memo?.toLowerCase()?.contains("poker chip") == true && !it.isMined()
}?.fold(0L) { acc, item ->
acc + item.value
} ?: 0L

View File

@ -12,8 +12,7 @@ import androidx.recyclerview.widget.RecyclerView
import cash.z.android.wallet.R
import cash.z.android.wallet.extention.toAppColor
import cash.z.android.wallet.extention.toRelativeTimeString
import cash.z.android.wallet.extention.truncate
import cash.z.wallet.sdk.dao.ClearedTransaction
import cash.z.wallet.sdk.entity.SentTransaction
import cash.z.wallet.sdk.ext.MINERS_FEE_ZATOSHI
import cash.z.wallet.sdk.ext.convertZatoshiToZecString
import java.text.SimpleDateFormat
@ -21,7 +20,7 @@ import java.util.*
import kotlin.math.absoluteValue
class TransactionAdapter(@LayoutRes val itemResId: Int = R.layout.item_transaction) : ListAdapter<ClearedTransaction, TransactionViewHolder>(DIFF_CALLBACK) {
class TransactionAdapter(@LayoutRes val itemResId: Int = R.layout.item_transaction) : ListAdapter<TransactionUiModel, TransactionViewHolder>(DIFF_CALLBACK) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransactionViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(itemResId, parent, false)
return TransactionViewHolder(itemView)
@ -29,9 +28,9 @@ class TransactionAdapter(@LayoutRes val itemResId: Int = R.layout.item_transacti
override fun onBindViewHolder(holder: TransactionViewHolder, position: Int) = holder.bind(getItem(position))
}
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<ClearedTransaction>() {
override fun areItemsTheSame(oldItem: ClearedTransaction, newItem: ClearedTransaction) = oldItem.height == newItem.height
override fun areContentsTheSame(oldItem: ClearedTransaction, newItem: ClearedTransaction) = oldItem == newItem
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<TransactionUiModel>() {
override fun areItemsTheSame(oldItem: TransactionUiModel, newItem: TransactionUiModel) = oldItem.minedHeight == newItem.minedHeight
override fun areContentsTheSame(oldItem: TransactionUiModel, newItem: TransactionUiModel): Boolean = oldItem == newItem
}
class TransactionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
@ -43,47 +42,41 @@ class TransactionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
private val memo = itemView.findViewById<TextView>(R.id.text_transaction_memo)
private val formatter = SimpleDateFormat("M/d h:mma", Locale.getDefault())
fun bind(tx: ClearedTransaction) {
val isChip = tx.isPokerChip()
val isSwag = tx.isSend && (tx.memo ?: "").toLowerCase().contains("swag")
val useSend = tx.isSend && !isChip
fun bind(tx: TransactionUiModel) {
val useSend = (tx.isSend) && !tx.isPokerChip
val isHistory = icon != null
val sign = if (useSend) "- " else "+ "
val amountColor = if (useSend) R.color.colorAccent else R.color.zcashPurple_accent
val defaultColor = R.color.text_light_dimmed
val actionColor = if (tx.action == null) amountColor else defaultColor
val transactionColor = if (useSend) R.color.send_associated else R.color.receive_associated
val transactionIcon = if (useSend || (isChip && tx.address != "Redeemed")) R.drawable.ic_sent_transaction else R.drawable.ic_received_transaction
val zecAbsoluteValue = tx.value.absoluteValue + if(tx.isSend) MINERS_FEE_ZATOSHI else 0
val toOrFrom = if (useSend) "to" else "from"
val srcOrDestination = tx.address?.truncate() ?: "shielded address"
val transactionIcon = if (useSend || (tx.isPokerChip && (tx as? SentTransaction)?.toAddress != "Redeemed")) R.drawable.ic_sent_transaction else R.drawable.ic_received_transaction
val zecAbsoluteValue = tx.zatoshiValue.absoluteValue + if(tx.isSend) MINERS_FEE_ZATOSHI else 0
fun createActionText(): Pair<String, Int> {
return when {
tx.isSend && isChip -> "Scan" to R.color.text_light_dimmed
tx.isMined && !(tx.isSend && tx.noteId <= 0) -> "$sign${zecAbsoluteValue.convertZatoshiToZecString(2)}" to amountColor
isSwag -> "Purchase" to R.color.text_light_dimmed
else -> (if (tx.isSend) "Transfer" else "Pending") to R.color.text_light_dimmed
}
}
val actionText = createActionText()
timestamp.text = if (tx.timeInSeconds == 0L) "Pending"
else (if (isHistory) formatter.format(tx.timeInSeconds * 1000) else (tx.timeInSeconds * 1000L).toRelativeTimeString())
amount.text = actionText.first
amount.setTextColor(actionText.second.toAppColor())
timestamp.text = if (tx.timestampMillis == 0L) "Pending"
else (if (isHistory) formatter.format(tx.timestampMillis) else (tx.timestampMillis).toRelativeTimeString())
amount.text = tx.action ?: "$sign${zecAbsoluteValue.convertZatoshiToZecString(2)}"
amount.setTextColor(actionColor.toAppColor())
// maybes - and if this gets to be too much, then pass in a custom holder when constructing the adapter, instead
status?.setBackgroundColor(transactionColor.toAppColor())
address?.text = if (tx.isSend) {
if (tx.isMined) {
if (isSwag && tx.isMined && tx.status == null) "Purchase accepted."
else "Transfer complete."
} else tx.status
} else "$toOrFrom $srcOrDestination"
memo?.text = tx.memo
address?.text = tx.status
memo?.text = tx?.memo ?: ""
icon?.setImageResource(transactionIcon)
}
}
private fun ClearedTransaction.isPokerChip(): Boolean {
return memo?.contains("Poker Chip") == true
}
data class TransactionUiModel(
val action: String? = null,
val status: String? = null,
val isSend: Boolean = false,
val isPending: Boolean = false,
val isPokerChip: Boolean = false,
val isSwag: Boolean = false,
val timestampMillis: Long = 0L,
val zatoshiValue: Long = 0L,
val minedHeight: Int = 0,
val memo: String? = null
)
val TransactionUiModel.isMined get() = this.minedHeight > 0

View File

@ -11,10 +11,11 @@ import cash.z.android.wallet.R
import cash.z.android.wallet.databinding.FragmentHistoryBinding
import cash.z.android.wallet.di.annotation.FragmentScope
import cash.z.android.wallet.ui.adapter.TransactionAdapter
import cash.z.android.wallet.ui.adapter.TransactionUiModel
import cash.z.android.wallet.ui.presenter.HistoryPresenter
import cash.z.android.wallet.ui.presenter.HistoryPresenterModule
import cash.z.android.wallet.ui.util.AlternatingRowColorDecoration
import cash.z.wallet.sdk.dao.ClearedTransaction
import cash.z.wallet.sdk.entity.ClearedTransaction
import dagger.Module
import dagger.android.ContributesAndroidInjector
import kotlinx.coroutines.launch
@ -58,7 +59,7 @@ class HistoryFragment : BaseFragment(), HistoryPresenter.HistoryView {
historyPresenter.stop()
}
override fun setTransactions(transactions: List<ClearedTransaction>) {
override fun setTransactions(transactions: List<TransactionUiModel>) {
mainActivity?.supportActionBar?.title = resources.getQuantityString(R.plurals.history_transaction_count_title,
transactions.size, transactions.size)
with (binding.recyclerTransactionsHistory) {

View File

@ -27,16 +27,14 @@ import cash.z.android.wallet.di.annotation.FragmentScope
import cash.z.android.wallet.extention.*
import cash.z.android.wallet.sample.SampleProperties
import cash.z.android.wallet.ui.adapter.TransactionAdapter
import cash.z.android.wallet.ui.adapter.TransactionUiModel
import cash.z.android.wallet.ui.presenter.HomePresenter
import cash.z.android.wallet.ui.presenter.HomePresenterModule
import cash.z.android.wallet.ui.util.AlternatingRowColorDecoration
import cash.z.android.wallet.ui.util.LottieLooper
import cash.z.android.wallet.ui.util.TopAlignedSpan
import cash.z.wallet.sdk.dao.ClearedTransaction
import cash.z.wallet.sdk.data.ActiveSendTransaction
import cash.z.wallet.sdk.data.ActiveTransaction
import cash.z.wallet.sdk.data.TransactionState
import cash.z.wallet.sdk.data.twig
import cash.z.wallet.sdk.entity.ClearedTransaction
import cash.z.wallet.sdk.ext.*
import com.google.android.material.snackbar.Snackbar
import com.leinardi.android.speeddial.SpeedDialActionItem
@ -136,7 +134,7 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
// SendView Implementation
//
override fun setTransactions(transactions: List<ClearedTransaction>) {
override fun setTransactions(transactions: List<TransactionUiModel>) {
val recent = if(transactions.size > maxTransactionsShown) transactions.subList(0, maxTransactionsShown) else transactions
with (binding.includeContent.recyclerTransactions) {
(adapter as TransactionAdapter).submitList(recent)
@ -160,21 +158,21 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
onContentRefreshComplete(new <= 0)
}
override fun setActiveTransactions(activeTransactionMap: Map<ActiveTransaction, TransactionState>) {
if (activeTransactionMap.isEmpty()) {
twig("A.T.: setActiveTransactionsShown(false) because map is empty")
setActiveTransactionsShown(false)
return
}
val transactions = activeTransactionMap.entries.toTypedArray()
// primary is the last one that was inserted
val primaryEntry = transactions[transactions.size - 1]
updatePrimaryTransaction(primaryEntry.key, primaryEntry.value)
onContentRefreshComplete(false)
}
//
// override fun setActiveTransactions(activeTransactionMap: Map<ActiveTransaction, TransactionState>) {
// if (activeTransactionMap.isEmpty()) {
// twig("A.T.: setActiveTransactionsShown(false) because map is empty")
// setActiveTransactionsShown(false)
// return
// }
//
// val transactions = activeTransactionMap.entries.toTypedArray()
// // primary is the last one that was inserted
// val primaryEntry = transactions[transactions.size - 1]
// updatePrimaryTransaction(primaryEntry.key, primaryEntry.value)
//
// onContentRefreshComplete(false)
// }
override fun onCancelledTooLate() {
snackbar = snackbar.showOk(view!!, "Oops! It was too late to cancel!")
@ -236,82 +234,82 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
setRefreshAnimationPlaying(false).also { twig("refresh false from onInitialLoadComplete") }
}
private fun updatePrimaryTransaction(transaction: ActiveTransaction, transactionState: TransactionState) {
twig("setting transaction state to ${transactionState::class.simpleName}")
var title = binding.includeContent.textActiveTransactionTitle.text?.toString() ?: ""
var subtitle: CharSequence = binding.includeContent.textActiveTransactionSubtitle.text?.toString() ?: ""
var isShown = binding.includeContent.textActiveTransactionHeader.visibility == View.VISIBLE
var isShownDelay = 10L
when (transactionState) {
TransactionState.Creating -> {
binding.includeContent.headerActiveTransaction.visibility = View.VISIBLE
title = "Preparing ${transaction.value.convertZatoshiToZecString(3)} ZEC"
subtitle = "to ${(transaction as ActiveSendTransaction).toAddress.truncate()}"
setTransactionActive(transaction, true)
isShown = true
}
TransactionState.SendingToNetwork -> {
title = "Sending Transaction"
subtitle = "to ${(transaction as ActiveSendTransaction).toAddress.truncate()}"
binding.includeContent.textActiveTransactionValue.text = "${transaction.value.convertZatoshiToZecString(3)}"
binding.includeContent.textActiveTransactionValue.visibility = View.VISIBLE
binding.includeContent.buttonActiveTransactionCancel.visibility = View.GONE
setTransactionActive(transaction, true)
isShown = true
}
is TransactionState.Failure -> {
binding.includeContent.lottieActiveTransaction.setAnimation(R.raw.lottie_send_failure)
binding.includeContent.lottieActiveTransaction.playAnimation()
title = "Failed"
subtitle = when(transactionState.failedStep) {
TransactionState.Creating -> "Failed to create transaction"
TransactionState.SendingToNetwork -> "Failed to submit transaction to the network"
else -> "Unrecoginzed error"
}
binding.includeContent.buttonActiveTransactionCancel.visibility = View.GONE
binding.includeContent.textActiveTransactionValue.visibility = View.GONE
setTransactionActive(transaction, false)
isShown = false
isShownDelay = 10_000L
}
is TransactionState.AwaitingConfirmations -> {
if (transactionState.confirmationCount < 1) {
binding.includeContent.lottieActiveTransaction.setAnimation(R.raw.lottie_send_success)
binding.includeContent.lottieActiveTransaction.playAnimation()
title = "ZEC Sent"
subtitle = "Waiting to be mined..."
binding.includeContent.textActiveTransactionValue.text = transaction.value.convertZatoshiToZecString(3)
binding.includeContent.textActiveTransactionValue.visibility = View.VISIBLE
binding.includeContent.buttonActiveTransactionCancel.visibility = View.GONE
isShown = true
} else if (transactionState.confirmationCount > 1) {
isShown = false
} else {
title = "Confirmation Received"
subtitle = transactionState.timestamp.toRelativeTimeString()
isShown = false
isShownDelay = 5_000L
// take it out of the list in a bit and skip counting confirmation animation for now (i.e. one is enough)
}
}
is TransactionState.Cancelled -> {
title = binding.includeContent.textActiveTransactionTitle.text.toString()
subtitle = binding.includeContent.textActiveTransactionSubtitle.text.toString()
setTransactionActive(transaction, false)
isShown = false
isShownDelay = 10_000L
}
else -> {
Log.e(javaClass.simpleName, "Warning: unrecognized transaction state $transactionState is being ignored")
return
}
}
binding.includeContent.textActiveTransactionTitle.text = title
binding.includeContent.textActiveTransactionSubtitle.text = subtitle
twig("A.T.: setActiveTransactionsShown($isShown, $isShownDelay) because ${transactionState}")
setActiveTransactionsShown(isShown, isShownDelay)
}
// private fun updatePrimaryTransaction(transaction: ActiveTransaction, transactionState: TransactionState) {
//
// twig("setting transaction state to ${transactionState::class.simpleName}")
// var title = binding.includeContent.textActiveTransactionTitle.text?.toString() ?: ""
// var subtitle: CharSequence = binding.includeContent.textActiveTransactionSubtitle.text?.toString() ?: ""
// var isShown = binding.includeContent.textActiveTransactionHeader.visibility == View.VISIBLE
// var isShownDelay = 10L
// when (transactionState) {
// TransactionState.Creating -> {
// binding.includeContent.headerActiveTransaction.visibility = View.VISIBLE
// title = "Preparing ${transaction.value.convertZatoshiToZecString(3)} ZEC"
// subtitle = "to ${(transaction as ActiveSendTransaction).toAddress.truncate()}"
// setTransactionActive(transaction, true)
// isShown = true
// }
// TransactionState.SendingToNetwork -> {
// title = "Sending Transaction"
// subtitle = "to ${(transaction as ActiveSendTransaction).toAddress.truncate()}"
// binding.includeContent.textActiveTransactionValue.text = "${transaction.value.convertZatoshiToZecString(3)}"
// binding.includeContent.textActiveTransactionValue.visibility = View.VISIBLE
// binding.includeContent.buttonActiveTransactionCancel.visibility = View.GONE
// setTransactionActive(transaction, true)
// isShown = true
// }
// is TransactionState.Failure -> {
// binding.includeContent.lottieActiveTransaction.setAnimation(R.raw.lottie_send_failure)
// binding.includeContent.lottieActiveTransaction.playAnimation()
// title = "Failed"
// subtitle = when(transactionState.failedStep) {
// TransactionState.Creating -> "Failed to create transaction"
// TransactionState.SendingToNetwork -> "Failed to submit transaction to the network"
// else -> "Unrecoginzed error"
// }
// binding.includeContent.buttonActiveTransactionCancel.visibility = View.GONE
// binding.includeContent.textActiveTransactionValue.visibility = View.GONE
// setTransactionActive(transaction, false)
// isShown = false
// isShownDelay = 10_000L
// }
// is TransactionState.AwaitingConfirmations -> {
// if (transactionState.confirmationCount < 1) {
// binding.includeContent.lottieActiveTransaction.setAnimation(R.raw.lottie_send_success)
// binding.includeContent.lottieActiveTransaction.playAnimation()
// title = "ZEC Sent"
// subtitle = "Waiting to be mined..."
// binding.includeContent.textActiveTransactionValue.text = transaction.value.convertZatoshiToZecString(3)
// binding.includeContent.textActiveTransactionValue.visibility = View.VISIBLE
// binding.includeContent.buttonActiveTransactionCancel.visibility = View.GONE
// isShown = true
// } else if (transactionState.confirmationCount > 1) {
// isShown = false
// } else {
// title = "Confirmation Received"
// subtitle = transactionState.timestamp.toRelativeTimeString()
// isShown = false
// isShownDelay = 5_000L
// // take it out of the list in a bit and skip counting confirmation animation for now (i.e. one is enough)
// }
// }
// is TransactionState.Cancelled -> {
// title = binding.includeContent.textActiveTransactionTitle.text.toString()
// subtitle = binding.includeContent.textActiveTransactionSubtitle.text.toString()
// setTransactionActive(transaction, false)
// isShown = false
// isShownDelay = 10_000L
// }
// else -> {
// Log.e(javaClass.simpleName, "Warning: unrecognized transaction state $transactionState is being ignored")
// return
// }
// }
// binding.includeContent.textActiveTransactionTitle.text = title
// binding.includeContent.textActiveTransactionSubtitle.text = subtitle
// twig("A.T.: setActiveTransactionsShown($isShown, $isShownDelay) because ${transactionState}")
// setActiveTransactionsShown(isShown, isShownDelay)
// }
//
@ -335,12 +333,12 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
private fun init() {
zcashLogoAnimation = LottieLooper(binding.lottieZcashBadge, 20..47, 69)
binding.includeContent.buttonActiveTransactionCancel.setOnClickListener {
val transaction = it.tag as? ActiveSendTransaction
if (transaction != null) {
homePresenter.onCancelActiveTransaction(transaction)
} else {
// val transaction = it.tag as? ActiveSendTransaction
// if (transaction != null) {
// homePresenter.onCancelActiveTransaction(transaction)
// } else {
Toaster.short("Error: unable to find transaction to cancel!")
}
// }
}
binding.lottieZcashBadge.setOnClickListener {
binding.lottieZcashBadge.playAnimation()
@ -471,29 +469,29 @@ class HomeFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener, HomeP
}
}
private fun setTransactionActive(transaction: ActiveTransaction, isActive: Boolean) {
// TODO: get view for transaction, mostly likely keep a sparse array of these or something
if (isActive) {
binding.includeContent.buttonActiveTransactionCancel.setText(R.string.cancel)
binding.includeContent.buttonActiveTransactionCancel.isEnabled = true
binding.includeContent.buttonActiveTransactionCancel.tag = transaction
binding.includeContent.headerActiveTransaction.animate().apply {
translationZ(10f)
duration = 200L
interpolator = DecelerateInterpolator()
}
} else {
binding.includeContent.buttonActiveTransactionCancel.setText(R.string.cancelled)
binding.includeContent.buttonActiveTransactionCancel.isEnabled = false
binding.includeContent.buttonActiveTransactionCancel.tag = null
binding.includeContent.headerActiveTransaction.animate().apply {
translationZ(2f)
duration = 300L
interpolator = AccelerateInterpolator()
}
binding.includeContent.lottieActiveTransaction.cancelAnimation()
}
}
// private fun setTransactionActive(transaction: ActiveTransaction, isActive: Boolean) {
// // TODO: get view for transaction, mostly likely keep a sparse array of these or something
// if (isActive) {
// binding.includeContent.buttonActiveTransactionCancel.setText(R.string.cancel)
// binding.includeContent.buttonActiveTransactionCancel.isEnabled = true
// binding.includeContent.buttonActiveTransactionCancel.tag = transaction
// binding.includeContent.headerActiveTransaction.animate().apply {
// translationZ(10f)
// duration = 200L
// interpolator = DecelerateInterpolator()
// }
// } else {
// binding.includeContent.buttonActiveTransactionCancel.setText(R.string.cancelled)
// binding.includeContent.buttonActiveTransactionCancel.isEnabled = false
// binding.includeContent.buttonActiveTransactionCancel.tag = null
// binding.includeContent.headerActiveTransaction.animate().apply {
// translationZ(2f)
// duration = 300L
// interpolator = AccelerateInterpolator()
// }
// binding.includeContent.lottieActiveTransaction.cancelAnimation()
// }
// }
private inner class Ticker : Runnable {
override fun run() {

View File

@ -5,7 +5,7 @@ import android.view.View
import android.widget.ProgressBar
import androidx.annotation.IdRes
import cash.z.android.wallet.ui.presenter.ProgressPresenter
import cash.z.wallet.sdk.data.DataSynchronizer
import cash.z.wallet.sdk.data.Synchronizer
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -15,7 +15,7 @@ abstract class ProgressFragment(
ProgressPresenter.ProgressView {
@Inject
protected lateinit var synchronizer: DataSynchronizer
protected lateinit var synchronizer: Synchronizer
protected lateinit var progressPresenter: ProgressPresenter
private lateinit var progressBar: ProgressBar

View File

@ -12,7 +12,7 @@ import cash.z.android.qrecycler.QRecycler
import cash.z.android.wallet.R
import cash.z.android.wallet.di.annotation.FragmentScope
import cash.z.android.wallet.ui.util.AddressPartNumberSpan
import cash.z.wallet.sdk.data.DataSynchronizer
import cash.z.wallet.sdk.data.Synchronizer
import dagger.Module
import dagger.android.ContributesAndroidInjector
import kotlinx.android.synthetic.main.fragment_receive.*
@ -28,7 +28,7 @@ class ReceiveFragment : BaseFragment() {
lateinit var qrecycler: QRecycler
@Inject
lateinit var synchronizer: DataSynchronizer
lateinit var synchronizer: Synchronizer
lateinit var addressParts: Array<TextView>

View File

@ -6,20 +6,19 @@ import android.view.View
import android.view.ViewGroup
import androidx.annotation.ColorRes
import androidx.databinding.DataBindingUtil
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import cash.z.android.wallet.*
import cash.z.android.wallet.R
import cash.z.android.wallet.ZcashWalletApplication
import cash.z.android.wallet.databinding.FragmentZcon1HomeBinding
import cash.z.android.wallet.di.annotation.FragmentScope
import cash.z.android.wallet.extention.Toaster
import cash.z.android.wallet.ui.adapter.TransactionAdapter
import cash.z.android.wallet.ui.adapter.TransactionUiModel
import cash.z.android.wallet.ui.presenter.BalancePresenter
import cash.z.android.wallet.ui.presenter.TransactionPresenter
import cash.z.android.wallet.ui.presenter.TransactionPresenterModule
import cash.z.wallet.sdk.dao.ClearedTransaction
import cash.z.wallet.sdk.data.twig
import cash.z.wallet.sdk.ext.MINERS_FEE_ZATOSHI
import cash.z.wallet.sdk.entity.ClearedTransaction
import cash.z.wallet.sdk.ext.convertZatoshiToZecString
import cash.z.wallet.sdk.secure.Wallet
import dagger.Module
@ -45,7 +44,7 @@ class Zcon1HomeFragment : BaseFragment(), BalancePresenter.BalanceView, Transact
private val balanceInfo: Wallet.WalletBalance get() = mainActivity?.balancePresenter?.lastBalance!!
private var transactions: List<ClearedTransaction> = emptyList()
private var transactions: List<TransactionUiModel> = emptyList()
@ -167,7 +166,7 @@ class Zcon1HomeFragment : BaseFragment(), BalancePresenter.BalanceView, Transact
// TransactionView Implementation
//
override fun setTransactions(transactions: List<ClearedTransaction>) {
override fun setTransactions(transactions: List<TransactionUiModel>) {
this.transactions = transactions
refreshTransactions()
}
@ -196,16 +195,16 @@ class Zcon1HomeFragment : BaseFragment(), BalancePresenter.BalanceView, Transact
// }
}
private fun PokerChip.toClearedTransaction(): ClearedTransaction {
return ClearedTransaction(
value = zatoshiValue - MINERS_FEE_ZATOSHI,
isSend = true,
timeInSeconds = created/1000L,
status = "Verifying funds...",
address = "PokerChip",
memo = toMemo()
)
}
//private fun PokerChip.toClearedTransaction(): ClearedTransaction {
// return ClearedTransaction(
// value = zatoshiValue - MINERS_FEE_ZATOSHI,
// isSend = true,
// timeInSeconds = created/1000L,
// status = "Verifying funds...",
// address = "PokerChip",
// memo = toMemo()
// )
//}
@Module

View File

@ -1,9 +1,9 @@
package cash.z.android.wallet.ui.presenter
import cash.z.android.wallet.ui.adapter.TransactionUiModel
import cash.z.android.wallet.ui.fragment.HistoryFragment
import cash.z.android.wallet.ui.presenter.Presenter.PresenterView
import cash.z.wallet.sdk.dao.ClearedTransaction
import cash.z.wallet.sdk.data.DataSynchronizer
import cash.z.wallet.sdk.data.Synchronizer
import cash.z.wallet.sdk.data.twig
import dagger.Binds
import dagger.Module
@ -16,13 +16,13 @@ import javax.inject.Singleton
class HistoryPresenter @Inject constructor(
private val view: HistoryFragment,
private var synchronizer: DataSynchronizer
private var synchronizer: Synchronizer
) : Presenter {
private var job: Job? = null
interface HistoryView : PresenterView {
fun setTransactions(transactions: List<ClearedTransaction>)
fun setTransactions(transactions: List<TransactionUiModel>)
}
override suspend fun start() {
@ -37,7 +37,7 @@ class HistoryPresenter @Inject constructor(
job?.cancel()?.also { job = null }
}
private fun CoroutineScope.launchTransactionBinder(channel: ReceiveChannel<List<ClearedTransaction>>) = launch {
private fun CoroutineScope.launchTransactionBinder(channel: ReceiveChannel<List<TransactionUiModel>>) = launch {
twig("transaction binder starting!")
for (clearedTransactionList in channel) {
twig("received ${clearedTransactionList.size} transactions for presenting")
@ -51,11 +51,9 @@ class HistoryPresenter @Inject constructor(
// View Callbacks on Main Thread
//
private fun bind(transactions: List<ClearedTransaction>) {
private fun bind(transactions: List<TransactionUiModel>) {
twig("binding ${transactions.size} clearedTransactions")
view.setTransactions(transactions.sortedByDescending {
if (!it.isMined && it.isSend) Long.MAX_VALUE else it.timeInSeconds
})
view.setTransactions(transactions)
}
}

View File

@ -1,9 +1,11 @@
package cash.z.android.wallet.ui.presenter
import cash.z.android.wallet.ui.adapter.TransactionUiModel
import cash.z.android.wallet.ui.fragment.HomeFragment
import cash.z.android.wallet.ui.presenter.Presenter.PresenterView
import cash.z.wallet.sdk.dao.ClearedTransaction
import cash.z.wallet.sdk.data.*
import cash.z.wallet.sdk.data.Synchronizer
import cash.z.wallet.sdk.data.twig
import cash.z.wallet.sdk.entity.ClearedTransaction
import cash.z.wallet.sdk.secure.Wallet
import dagger.Binds
import dagger.Module
@ -16,15 +18,15 @@ import javax.inject.Singleton
class HomePresenter @Inject constructor(
private val view: HomeFragment,
private val synchronizer: DataSynchronizer
private val synchronizer: Synchronizer
) : Presenter {
private var job: Job? = null
interface HomeView : PresenterView {
fun setTransactions(transactions: List<ClearedTransaction>)
fun setTransactions(transactions: List<TransactionUiModel>)
fun updateBalance(old: Long, new: Long)
fun setActiveTransactions(activeTransactionMap: Map<ActiveTransaction, TransactionState>)
// fun setActiveTransactions(activeTransactionMap: Map<ActiveTransaction, TransactionState>)
fun onCancelledTooLate()
fun onSynchronizerError(error: Throwable?): Boolean
}
@ -56,7 +58,7 @@ class HomePresenter @Inject constructor(
twig("balance binder exiting!")
}
private fun CoroutineScope.launchTransactionBinder(channel: ReceiveChannel<List<ClearedTransaction>>) = launch {
private fun CoroutineScope.launchTransactionBinder(channel: ReceiveChannel<List<TransactionUiModel>>) = launch {
twig("transaction binder starting!")
for (clearedTransactionList in channel) {
twig("received ${clearedTransactionList.size} transactions for presenting")
@ -65,13 +67,13 @@ class HomePresenter @Inject constructor(
twig("transaction binder exiting!")
}
private fun CoroutineScope.launchActiveTransactionMonitor(channel: ReceiveChannel<Map<ActiveTransaction, TransactionState>>) = launch {
twig("active transaction monitor starting!")
for (i in channel) {
bind(i)
}
twig("active transaction monitor exiting!")
}
// private fun CoroutineScope.launchActiveTransactionMonitor(channel: ReceiveChannel<Map<ActiveTransaction, TransactionState>>) = launch {
// twig("active transaction monitor starting!")
// for (i in channel) {
// bind(i)
// }
// twig("active transaction monitor exiting!")
// }
//
@ -84,25 +86,23 @@ class HomePresenter @Inject constructor(
}
private fun bind(transactions: List<ClearedTransaction>) {
private fun bind(transactions: List<TransactionUiModel>) {
twig("binding ${transactions.size} clearedTransactions")
view.setTransactions(transactions.sortedByDescending {
if (!it.isMined && it.isSend) Long.MAX_VALUE else it.timeInSeconds
})
view.setTransactions(transactions)
}
private fun bind(activeTransactionMap: Map<ActiveTransaction, TransactionState>) {
twig("binding a.t. map of size ${activeTransactionMap.size}")
if (activeTransactionMap.isNotEmpty()) view.setActiveTransactions(activeTransactionMap)
}
// private fun bind(activeTransactionMap: Map<ActiveTransaction, TransactionState>) {
// twig("binding a.t. map of size ${activeTransactionMap.size}")
// if (activeTransactionMap.isNotEmpty()) view.setActiveTransactions(activeTransactionMap)
// }
fun onCancelActiveTransaction(transaction: ActiveSendTransaction) {
// fun onCancelActiveTransaction(transaction: ActiveSendTransaction) {
// twig("requesting to cancel send for transaction ${transaction.internalId}")
// val isTooLate = !synchronizer.cancelSend(transaction)
// if (isTooLate) {
// view.onCancelledTooLate()
// }
}
// }
}

View File

@ -1,13 +1,14 @@
package cash.z.android.wallet.ui.presenter
import cash.z.android.wallet.Zcon1Store
import cash.z.android.wallet.Zcon1Store.address
import cash.z.android.wallet.ui.activity.MainActivity
import cash.z.android.wallet.ui.presenter.Presenter.PresenterView
import cash.z.wallet.sdk.data.DataSynchronizer
import cash.z.wallet.sdk.data.Synchronizer
import cash.z.wallet.sdk.data.Twig
import cash.z.wallet.sdk.data.twig
import cash.z.wallet.sdk.db.PendingTransaction
import cash.z.wallet.sdk.db.isFailure
import cash.z.wallet.sdk.entity.PendingTransaction
import cash.z.wallet.sdk.entity.isFailure
import dagger.Binds
import dagger.Module
import kotlinx.coroutines.CoroutineScope
@ -18,7 +19,7 @@ import javax.inject.Inject
class MainPresenter @Inject constructor(
private val view: MainActivity,
private val synchronizer: DataSynchronizer
private val synchronizer: Synchronizer
) : Presenter {
interface MainView : PresenterView {
@ -80,7 +81,7 @@ class MainPresenter @Inject constructor(
}
private fun PendingTransaction.isSwag(): Boolean {
return address == Zcon1Store.address && memo.toLowerCase().contains("swag")
return address == Zcon1Store.address && (memo?.toLowerCase()?.contains("swag") == true)
}

View File

@ -1,7 +1,7 @@
package cash.z.android.wallet.ui.presenter
import cash.z.android.wallet.ui.presenter.Presenter.PresenterView
import cash.z.wallet.sdk.data.DataSynchronizer
import cash.z.wallet.sdk.data.Synchronizer
import cash.z.wallet.sdk.data.Twig
import cash.z.wallet.sdk.data.twig
import kotlinx.coroutines.CoroutineScope
@ -12,7 +12,7 @@ import javax.inject.Inject
class ProgressPresenter @Inject constructor(
private val view: ProgressView,
private var synchronizer: DataSynchronizer
private var synchronizer: Synchronizer
) : Presenter {
private var job: Job? = null

View File

@ -6,7 +6,7 @@ import cash.z.android.wallet.extention.toAppString
import cash.z.android.wallet.sample.SampleProperties
import cash.z.android.wallet.ui.fragment.SendFragment
import cash.z.android.wallet.ui.presenter.Presenter.PresenterView
import cash.z.wallet.sdk.data.DataSynchronizer
import cash.z.wallet.sdk.data.Synchronizer
import cash.z.wallet.sdk.data.Twig
import cash.z.wallet.sdk.data.twig
import cash.z.wallet.sdk.ext.*
@ -22,7 +22,7 @@ import javax.inject.Inject
class SendPresenter @Inject constructor(
private val view: SendFragment,
private val synchronizer: DataSynchronizer
private val synchronizer: Synchronizer
) : Presenter {
interface SendView : PresenterView {

View File

@ -1,13 +1,13 @@
package cash.z.android.wallet.ui.presenter
import cash.z.android.wallet.extention.truncate
import cash.z.android.wallet.ui.adapter.TransactionUiModel
import cash.z.android.wallet.ui.fragment.Zcon1HomeFragment
import cash.z.android.wallet.ui.presenter.Presenter.PresenterView
import cash.z.wallet.sdk.dao.ClearedTransaction
import cash.z.wallet.sdk.data.DataSynchronizer
import cash.z.wallet.sdk.data.TransactionState
import cash.z.wallet.sdk.data.Synchronizer
import cash.z.wallet.sdk.data.Twig
import cash.z.wallet.sdk.data.twig
import cash.z.wallet.sdk.db.*
import cash.z.wallet.sdk.entity.*
import dagger.Binds
import dagger.Module
import kotlinx.coroutines.CoroutineScope
@ -17,18 +17,18 @@ import javax.inject.Inject
class TransactionPresenter @Inject constructor(
private val view: Zcon1HomeFragment,
private val synchronizer: DataSynchronizer
private val synchronizer: Synchronizer
) : Presenter {
interface TransactionView : PresenterView {
fun setTransactions(transactions: List<ClearedTransaction>)
fun setTransactions(transactions: List<TransactionUiModel>)
}
private var pendingJob: Job? = null
private var clearedJob: Job? = null
private var latestPending: List<PendingTransaction> = listOf()
private var latestCleared: List<ClearedTransaction> = listOf()
private var latestPending: List<TransactionUiModel> = listOf()
private var latestCleared: List<TransactionUiModel> = listOf()
//
// LifeCycle
@ -57,7 +57,7 @@ class TransactionPresenter @Inject constructor(
twig("pending transaction binder starting")
for (new in channel) {
twig("pending transactions have been modified... binding to the view")
latestPending = new
latestPending = new.map { it.toTransactionUiModel() }
bind()
}
twig("pending transaction binder exiting!")
@ -68,7 +68,13 @@ class TransactionPresenter @Inject constructor(
twig("cleared transaction binder starting")
for (new in channel) {
twig("cleared transactions have been modified... binding to the view")
latestCleared = new
latestCleared = new.map {
when(it) {
is SentTransaction -> it.toTransactionUiModel()
is ReceivedTransaction -> it.toTransactionUiModel()
else -> throw UnsupportedTransactionException(it)
}
}
bind()
}
twig("cleared transaction binder exiting!")
@ -82,11 +88,12 @@ class TransactionPresenter @Inject constructor(
private fun bind() {
twig("binding ${latestPending.size} pending transactions and ${latestCleared.size} cleared transactions")
// merge transactions
val mergedTransactions = mutableListOf<ClearedTransaction>()
latestPending.forEach { mergedTransactions.add(it.toClearedTransaction()) }
val mergedTransactions = mutableListOf<TransactionUiModel>()
mergedTransactions.addAll(latestCleared)
mergedTransactions.addAll(latestPending)
mergedTransactions.sortByDescending {
it.timeInSeconds
if (it.timestampMillis > 0) it.timestampMillis else it.minedHeight.toLong()
}
view.setTransactions(mergedTransactions)
// twig("MERGED_TX---------vvvvvv")
@ -97,36 +104,87 @@ class TransactionPresenter @Inject constructor(
}
sealed class PurchaseResult {
data class Processing(val state: TransactionState = TransactionState.Creating) : PurchaseResult()
data class Failure(val reason: String = "") : PurchaseResult()
}
// sealed class PurchaseResult {
// data class Processing(val state: TransactionState = TransactionState.Creating) : PurchaseResult()
// data class Failure(val reason: String = "") : PurchaseResult()
// }
}
private fun PendingTransaction.toClearedTransaction(): ClearedTransaction {
var description = when {
isFailedEncoding() -> "Failed to create! Aborted."
isFailedSubmit() -> "Failed to send...Retrying!"
isCreating() -> if (memo.toLowerCase().contains("poker chip")) "Redeeming..." else "Creating transaction..."
isSubmitted() && !isMined() -> "Submitted, awaiting response."
isSubmitted() && isMined() -> "Successfully mined!"
else -> "Pending..."
}
if (!isSubmitted() && (submitAttempts > 2 || encodeAttempts > 2)) {
description += " aborting in ${ttl() / 60L}m${ttl().rem(60)}s"
}
return ClearedTransaction(
value = value,
private fun SentTransaction.toTransactionUiModel(): TransactionUiModel {
return TransactionUiModel(
status = "to ${toAddress.truncate()}",
isSend = true,
isMined = isMined(),
height = minedHeight,
timeInSeconds = createTime / 1000L,
address = address,
status = description,
memo = memo
minedHeight = minedHeight,
zatoshiValue = value,
memo = memo,
isSwag = memo?.toLowerCase()?.contains("swag") ?: false,
timestampMillis = blockTimeInSeconds * 1000L,
isPokerChip = false
)
}
private fun ReceivedTransaction.toTransactionUiModel(): TransactionUiModel {
return TransactionUiModel(
status = "from shielded address",
minedHeight = minedHeight,
zatoshiValue = value,
timestampMillis = blockTimeInSeconds * 1000L,
isPokerChip = false
)
}
private fun PendingTransaction.toTransactionUiModel(): TransactionUiModel {
val isPokerChip = memo?.toLowerCase()?.contains("poker chip") == true
val isSwag = memo?.toLowerCase()?.contains("swag") ?: false
var description = when {
isSwag && isMined() -> "Purchase accepted."
isFailedEncoding() -> "Failed to create! Aborted."
isFailedSubmit() -> "Failed to send...Retrying!"
isCreating() -> if (isPokerChip) "Redeeming..." else "Creating transaction..."
isSwag && !isMined() -> "Purchase pending"
isSubmitted() && !isMined() -> "Submitted, awaiting response."
isSubmitted() && isMined() -> if (isPokerChip) "Successfully mined!" else "to ${toAddress.truncate()}"
else -> "Pending..."
}
return TransactionUiModel(
action = if (isPokerChip) "Scan" else null,
status = description,
isPending = true,
isSend = true,
isSwag = isSwag,
minedHeight = minedHeight,
zatoshiValue = value,
memo = memo,
timestampMillis = createTime,
isPokerChip = isPokerChip
)
}
class UnsupportedTransactionException(tx: ClearedTransaction) :
RuntimeException("Unsupported transaction type. " +
"Expected either SentTransaction or ReceivedTransaction but was ${tx.javaClass.canonicalName}")
//private fun PendingTransaction.toClearedTransaction(): ClearedTransaction {
// var description = when {
// isFailedEncoding() -> "Failed to create! Aborted."
// isFailedSubmit() -> "Failed to send...Retrying!"
// isCreating() -> if (memo?.toLowerCase()?.contains("poker chip") == true) "Redeeming..." else "Creating transaction..."
// isSubmitted() && !isMined() -> "Submitted, awaiting response."
// isSubmitted() && isMined() -> "Successfully mined!"
// else -> "Pending..."
// }
//// if (!isSubmitted() && (submitAttempts > 2 || encodeAttempts > 2)) {
//// description += " aborting in ${ttl() / 60L}m${ttl().rem(60)}s"
//// }
// return SentTransaction(
// value = value,
// minedHeight = minedHeight,
// blockTimeInSeconds = createTime / 1000L,
// toAddress = toAddress,
// status = description,
// memo = memo
// )
//}
@Module
abstract class TransactionPresenterModule {
@Binds

View File

@ -8,7 +8,7 @@ import cash.z.android.wallet.ZcashWalletApplication
import cash.z.android.wallet.Zcon1Store
import cash.z.android.wallet.extention.toAppString
import cash.z.wallet.sdk.data.twig
import cash.z.wallet.sdk.db.PendingTransaction
import cash.z.wallet.sdk.entity.PendingTransaction
import cash.z.wallet.sdk.ext.convertZatoshiToZecString
import com.mixpanel.android.mpmetrics.MixpanelAPI
import java.io.PrintWriter

View File

@ -8,7 +8,7 @@ import cash.z.wallet.sdk.data.TransactionSender
import cash.z.wallet.sdk.data.Twig
import cash.z.wallet.sdk.data.WalletTransactionEncoder
import cash.z.wallet.sdk.data.twig
import cash.z.wallet.sdk.db.PendingTransaction
import cash.z.wallet.sdk.entity.PendingTransaction
import cash.z.wallet.sdk.ext.MINERS_FEE_ZATOSHI
import cash.z.wallet.sdk.ext.convertZatoshiToZecString
import cash.z.wallet.sdk.jni.RustBackendWelding

View File

@ -60,7 +60,7 @@ buildscript {
'speeddial': 'com.leinardi.android:speed-dial:2.0.0',
'stetho': 'com.facebook.stetho:stetho:1.5.1',
'zcash': [
'walletSdk': "cash.z.android.wallet:zcash-android-testnet:1.9.0-alpha@aar"
'walletSdk': "cash.z.android.wallet:zcash-android-testnet:1.9.1-alpha@aar"
]
]
repositories {