[#853] Adopt latest SDK changes

Co-authored-by: Honza <rychnovsky.honza@gmail.com>
This commit is contained in:
Carter Jernigan 2023-05-12 07:14:31 -04:00 committed by GitHub
parent e20221f117
commit b6226c55eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 45 additions and 178 deletions

View File

@ -153,7 +153,7 @@ ZCASH_ANDROID_WALLET_PLUGINS_VERSION=1.0.0
ZCASH_BIP39_VERSION=1.0.4 ZCASH_BIP39_VERSION=1.0.4
# TODO [#279]: Revert to stable SDK before app release # TODO [#279]: Revert to stable SDK before app release
# TODO [#279]: https://github.com/zcash/secant-android-wallet/issues/279 # TODO [#279]: https://github.com/zcash/secant-android-wallet/issues/279
ZCASH_SDK_VERSION=1.14.0-beta01-SNAPSHOT ZCASH_SDK_VERSION=1.17.0-beta01-SNAPSHOT
ZXING_VERSION=3.5.1 ZXING_VERSION=3.5.1

View File

@ -1,57 +0,0 @@
package cash.z.ecc.sdk.model
import cash.z.ecc.android.sdk.block.CompactBlockProcessor
import cash.z.ecc.android.sdk.model.PercentDecimal
fun CompactBlockProcessor.ProcessorInfo.downloadProgress(): PercentDecimal {
val lastDownloadRangeSnapshot = lastDownloadRange
val lastDownloadedHeightSnapshot = lastDownloadedHeight
return if (lastDownloadRangeSnapshot?.isEmpty() != false || lastDownloadedHeightSnapshot == null) {
PercentDecimal.ONE_HUNDRED_PERCENT
} else {
val numerator = (lastDownloadedHeightSnapshot.value - lastDownloadRangeSnapshot.start.value + 1)
.toFloat()
.coerceAtLeast(PercentDecimal.MIN)
val denominator = (lastDownloadRangeSnapshot.endInclusive.value - lastDownloadRangeSnapshot.start.value + 1)
.toFloat()
val progress = (numerator / denominator).coerceAtMost(PercentDecimal.MAX)
PercentDecimal(progress)
}
}
fun CompactBlockProcessor.ProcessorInfo.scanProgress(): PercentDecimal {
val lastScanRangeSnapshot = lastScanRange
val lastScannedHeightSnapshot = lastScannedHeight
return if (lastScanRangeSnapshot?.isEmpty() != false || lastScannedHeightSnapshot == null) {
PercentDecimal.ONE_HUNDRED_PERCENT
} else {
val numerator = (lastScannedHeightSnapshot.value - lastScanRangeSnapshot.start.value + 1)
.toFloat()
.coerceAtLeast(PercentDecimal.MIN)
val demonimator = (lastScanRangeSnapshot.endInclusive.value - lastScanRangeSnapshot.start.value + 1)
.toFloat()
val progress = (numerator / demonimator).coerceAtMost(PercentDecimal.MAX)
PercentDecimal(progress)
}
}
// These are estimates
@Suppress("MagicNumber")
private val DOWNLOAD_WEIGHT = PercentDecimal(0.4f)
private val SCAN_WEIGHT = PercentDecimal(PercentDecimal.MAX - DOWNLOAD_WEIGHT.decimal)
fun CompactBlockProcessor.ProcessorInfo.totalProgress(): PercentDecimal {
val downloadWeighted = DOWNLOAD_WEIGHT.decimal * (downloadProgress().decimal).coerceAtMost(PercentDecimal.MAX)
val scanWeighted = SCAN_WEIGHT.decimal * (scanProgress().decimal).coerceAtMost(PercentDecimal.MAX)
return PercentDecimal(
downloadWeighted.coerceAtLeast(PercentDecimal.MIN) +
scanWeighted.coerceAtLeast(PercentDecimal.MIN)
)
}

View File

@ -332,6 +332,7 @@ if (zcashSdkIncludedBuildPath.isNotEmpty()) {
includeBuild(zcashSdkIncludedBuildPath) { includeBuild(zcashSdkIncludedBuildPath) {
dependencySubstitution { dependencySubstitution {
substitute(module("cash.z.ecc.android:zcash-android-sdk")).using(project(":sdk-lib")) substitute(module("cash.z.ecc.android:zcash-android-sdk")).using(project(":sdk-lib"))
substitute(module("cash.z.ecc.android:zcash-android-sdk-incubator")).using(project(":sdk-incubator-lib"))
} }
} }
} }

View File

@ -5,8 +5,7 @@ import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.block.CompactBlockProcessor import cash.z.ecc.android.sdk.block.CompactBlockProcessor
import cash.z.ecc.android.sdk.model.Account import cash.z.ecc.android.sdk.model.Account
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.PendingTransaction import cash.z.ecc.android.sdk.model.PercentDecimal
import cash.z.ecc.android.sdk.model.Transaction
import cash.z.ecc.android.sdk.model.TransactionOverview import cash.z.ecc.android.sdk.model.TransactionOverview
import cash.z.ecc.android.sdk.model.TransactionRecipient import cash.z.ecc.android.sdk.model.TransactionRecipient
import cash.z.ecc.android.sdk.model.UnifiedSpendingKey import cash.z.ecc.android.sdk.model.UnifiedSpendingKey
@ -17,7 +16,6 @@ import cash.z.ecc.android.sdk.type.AddressType
import cash.z.ecc.android.sdk.type.ConsensusMatchType import cash.z.ecc.android.sdk.type.ConsensusMatchType
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.emptyFlow
/** /**
* Mocked Synchronizer that can be used instead of the production SdkSynchronizer e.g. for tests. * Mocked Synchronizer that can be used instead of the production SdkSynchronizer e.g. for tests.
@ -25,9 +23,6 @@ import kotlinx.coroutines.flow.emptyFlow
@Suppress("TooManyFunctions", "UNUSED_PARAMETER") @Suppress("TooManyFunctions", "UNUSED_PARAMETER")
internal class MockSynchronizer : CloseableSynchronizer { internal class MockSynchronizer : CloseableSynchronizer {
override val clearedTransactions: Flow<List<TransactionOverview>>
get() = error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.")
override val latestBirthdayHeight: BlockHeight override val latestBirthdayHeight: BlockHeight
get() = error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.") get() = error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.")
@ -63,26 +58,18 @@ internal class MockSynchronizer : CloseableSynchronizer {
override val orchardBalances: StateFlow<WalletBalance?> override val orchardBalances: StateFlow<WalletBalance?>
get() = error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.") get() = error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.")
override val pendingTransactions: Flow<List<PendingTransaction>>
get() = error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.")
override val processorInfo: Flow<CompactBlockProcessor.ProcessorInfo> override val processorInfo: Flow<CompactBlockProcessor.ProcessorInfo>
get() = error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.") get() = error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.")
override val progress: Flow<PercentDecimal>
override val progress: Flow<Int> get() = TODO("Not yet implemented")
get() = error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.")
override val receivedTransactions: Flow<List<Transaction.Received>>
get() = error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.")
override val saplingBalances: StateFlow<WalletBalance?> override val saplingBalances: StateFlow<WalletBalance?>
get() = error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.") get() = error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.")
override val sentTransactions: Flow<List<Transaction.Sent>>
get() = error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.")
override val status: Flow<Synchronizer.Status> override val status: Flow<Synchronizer.Status>
get() = error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.") get() = error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.")
override val transactions: Flow<List<TransactionOverview>>
get() = TODO("Not yet implemented")
override val transparentBalances: StateFlow<WalletBalance?> override val transparentBalances: StateFlow<WalletBalance?>
get() = error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.") get() = error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.")
@ -143,14 +130,11 @@ internal class MockSynchronizer : CloseableSynchronizer {
error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.") error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.")
} }
/** override suspend fun sendToAddress(usk: UnifiedSpendingKey, amount: Zatoshi, toAddress: String, memo: String): Long {
* This method intentionally returns empty flow, as the PendingTransaction is only an SDK internal class. return 1
*/
override fun sendToAddress(usk: UnifiedSpendingKey, amount: Zatoshi, toAddress: String, memo: String): Flow<PendingTransaction> {
return emptyFlow()
} }
override fun shieldFunds(usk: UnifiedSpendingKey, memo: String): Flow<PendingTransaction> { override suspend fun shieldFunds(usk: UnifiedSpendingKey, memo: String): Long {
error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.") error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.")
} }

View File

@ -34,7 +34,7 @@ class HomeActivityTest : UiTestPrerequisites() {
@MediumTest @MediumTest
fun open_close_drawer_menu_test() { fun open_close_drawer_menu_test() {
val walletSnapshot = WalletSnapshotFixture.new( val walletSnapshot = WalletSnapshotFixture.new(
status = Synchronizer.Status.DOWNLOADING, status = Synchronizer.Status.SYNCING,
progress = PercentDecimal(0.5f) progress = PercentDecimal(0.5f)
) )
val testSetup = newTestSetup(walletSnapshot) val testSetup = newTestSetup(walletSnapshot)

View File

@ -37,7 +37,7 @@ class HomeViewIntegrationTest : UiTestPrerequisites() {
fun wallet_snapshot_restoration() { fun wallet_snapshot_restoration() {
val restorationTester = StateRestorationTester(composeTestRule) val restorationTester = StateRestorationTester(composeTestRule)
val walletSnapshot = WalletSnapshotFixture.new( val walletSnapshot = WalletSnapshotFixture.new(
status = Synchronizer.Status.DOWNLOADING, status = Synchronizer.Status.SYNCING,
progress = PercentDecimal(0.5f) progress = PercentDecimal(0.5f)
) )
val testSetup = newTestSetup(walletSnapshot) val testSetup = newTestSetup(walletSnapshot)
@ -47,7 +47,7 @@ class HomeViewIntegrationTest : UiTestPrerequisites() {
} }
assertNotEquals(WalletSnapshotFixture.STATUS, testSetup.getWalletSnapshot().status) assertNotEquals(WalletSnapshotFixture.STATUS, testSetup.getWalletSnapshot().status)
assertEquals(Synchronizer.Status.DOWNLOADING, testSetup.getWalletSnapshot().status) assertEquals(Synchronizer.Status.SYNCING, testSetup.getWalletSnapshot().status)
assertNotEquals(WalletSnapshotFixture.PROGRESS, testSetup.getWalletSnapshot().progress) assertNotEquals(WalletSnapshotFixture.PROGRESS, testSetup.getWalletSnapshot().progress)
assertEquals(0.5f, testSetup.getWalletSnapshot().progress.decimal) assertEquals(0.5f, testSetup.getWalletSnapshot().progress.decimal)
@ -55,7 +55,7 @@ class HomeViewIntegrationTest : UiTestPrerequisites() {
restorationTester.emulateSavedInstanceStateRestore() restorationTester.emulateSavedInstanceStateRestore()
assertNotEquals(WalletSnapshotFixture.STATUS, testSetup.getWalletSnapshot().status) assertNotEquals(WalletSnapshotFixture.STATUS, testSetup.getWalletSnapshot().status)
assertEquals(Synchronizer.Status.DOWNLOADING, testSetup.getWalletSnapshot().status) assertEquals(Synchronizer.Status.SYNCING, testSetup.getWalletSnapshot().status)
assertNotEquals(WalletSnapshotFixture.PROGRESS, testSetup.getWalletSnapshot().progress) assertNotEquals(WalletSnapshotFixture.PROGRESS, testSetup.getWalletSnapshot().progress)
assertEquals(0.5f, testSetup.getWalletSnapshot().progress.decimal) assertEquals(0.5f, testSetup.getWalletSnapshot().progress.decimal)

View File

@ -12,6 +12,7 @@ import co.electriccoin.zcash.ui.test.getStringResource
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class WalletDisplayValuesTest { class WalletDisplayValuesTest {
@ -20,7 +21,7 @@ class WalletDisplayValuesTest {
fun download_running_test() { fun download_running_test() {
val walletSnapshot = WalletSnapshotFixture.new( val walletSnapshot = WalletSnapshotFixture.new(
progress = PercentDecimal.ONE_HUNDRED_PERCENT, progress = PercentDecimal.ONE_HUNDRED_PERCENT,
status = Synchronizer.Status.SCANNING, status = Synchronizer.Status.SYNCING,
orchardBalance = WalletSnapshotFixture.ORCHARD_BALANCE, orchardBalance = WalletSnapshotFixture.ORCHARD_BALANCE,
saplingBalance = WalletSnapshotFixture.SAPLING_BALANCE, saplingBalance = WalletSnapshotFixture.SAPLING_BALANCE,
transparentBalance = WalletSnapshotFixture.TRANSPARENT_BALANCE transparentBalance = WalletSnapshotFixture.TRANSPARENT_BALANCE
@ -34,7 +35,7 @@ class WalletDisplayValuesTest {
assertNotNull(values) assertNotNull(values)
assertEquals(1f, values.progress.decimal) assertEquals(1f, values.progress.decimal)
assertEquals(walletSnapshot.totalBalance().toZecString(), values.zecAmountText) assertEquals(walletSnapshot.totalBalance().toZecString(), values.zecAmountText)
assertEquals(getStringResource(R.string.home_status_syncing_catchup), values.statusText) assertTrue(values.statusText.startsWith(getStringResource(R.string.home_status_syncing_catchup)))
// TODO [#578] https://github.com/zcash/zcash-android-wallet-sdk/issues/578 // TODO [#578] https://github.com/zcash/zcash-android-wallet-sdk/issues/578
assertEquals(FiatCurrencyConversionRateState.Unavailable, values.fiatCurrencyAmountState) assertEquals(FiatCurrencyConversionRateState.Unavailable, values.fiatCurrencyAmountState)
assertEquals(getStringResource(R.string.fiat_currency_conversion_rate_unavailable), values.fiatCurrencyAmountText) assertEquals(getStringResource(R.string.fiat_currency_conversion_rate_unavailable), values.fiatCurrencyAmountText)

View File

@ -70,7 +70,7 @@ class HomeViewTest : UiTestPrerequisites() {
newTestSetup( newTestSetup(
isCircularProgressBar = true, isCircularProgressBar = true,
walletSnapshot = WalletSnapshotFixture.new( walletSnapshot = WalletSnapshotFixture.new(
status = Synchronizer.Status.SCANNING, status = Synchronizer.Status.SYNCING,
progress = PercentDecimal.ONE_HUNDRED_PERCENT progress = PercentDecimal.ONE_HUNDRED_PERCENT
) )
) )
@ -87,7 +87,7 @@ class HomeViewTest : UiTestPrerequisites() {
newTestSetup( newTestSetup(
isCircularProgressBar = false, isCircularProgressBar = false,
walletSnapshot = WalletSnapshotFixture.new( walletSnapshot = WalletSnapshotFixture.new(
status = Synchronizer.Status.SCANNING, status = Synchronizer.Status.SYNCING,
progress = PercentDecimal.ONE_HUNDRED_PERCENT progress = PercentDecimal.ONE_HUNDRED_PERCENT
) )
) )

View File

@ -25,13 +25,10 @@ object WalletSnapshotFixture {
null, null,
null, null,
null, null,
null,
null
), ),
orchardBalance: WalletBalance = ORCHARD_BALANCE, orchardBalance: WalletBalance = ORCHARD_BALANCE,
saplingBalance: WalletBalance = SAPLING_BALANCE, saplingBalance: WalletBalance = SAPLING_BALANCE,
transparentBalance: WalletBalance = TRANSPARENT_BALANCE, transparentBalance: WalletBalance = TRANSPARENT_BALANCE,
pendingCount: Int = 0,
progress: PercentDecimal = PROGRESS, progress: PercentDecimal = PROGRESS,
synchronizerError: SynchronizerError? = null synchronizerError: SynchronizerError? = null
) = WalletSnapshot( ) = WalletSnapshot(
@ -40,7 +37,6 @@ object WalletSnapshotFixture {
orchardBalance, orchardBalance,
saplingBalance, saplingBalance,
transparentBalance, transparentBalance,
pendingCount,
progress, progress,
synchronizerError synchronizerError
) )

View File

@ -1,12 +0,0 @@
package co.electriccoin.zcash.ui.screen.home.model
import cash.z.ecc.android.sdk.model.PendingTransaction
import cash.z.ecc.android.sdk.model.TransactionOverview
/**
* A common transactions wrapper class to provide unified way to work with a transactions classes from our SDK.
*/
sealed class CommonTransaction {
data class Pending(val data: PendingTransaction) : CommonTransaction()
data class Overview(val data: TransactionOverview) : CommonTransaction()
}

View File

@ -39,9 +39,7 @@ data class WalletDisplayValues(
var fiatCurrencyAmountText = getFiatCurrencyRateValue(context, fiatCurrencyAmountState) var fiatCurrencyAmountText = getFiatCurrencyRateValue(context, fiatCurrencyAmountState)
when (walletSnapshot.status) { when (walletSnapshot.status) {
Synchronizer.Status.PREPARING, Synchronizer.Status.SYNCING -> {
Synchronizer.Status.DOWNLOADING,
Synchronizer.Status.VALIDATING -> {
progress = walletSnapshot.progress progress = walletSnapshot.progress
val progressPercent = (walletSnapshot.progress.decimal * 100).roundToInt() val progressPercent = (walletSnapshot.progress.decimal * 100).roundToInt()
// we add "so far" to the amount // we add "so far" to the amount
@ -53,11 +51,6 @@ data class WalletDisplayValues(
} }
statusText = context.getString(R.string.home_status_syncing_format, progressPercent) statusText = context.getString(R.string.home_status_syncing_format, progressPercent)
} }
Synchronizer.Status.SCANNING -> {
// SDK provides us only one progress, which keeps on 100 in the scanning state
progress = PercentDecimal.ONE_HUNDRED_PERCENT
statusText = context.getString(R.string.home_status_syncing_catchup)
}
Synchronizer.Status.SYNCED, Synchronizer.Status.SYNCED,
Synchronizer.Status.ENHANCING -> { Synchronizer.Status.ENHANCING -> {
statusText = if (updateAvailable) { statusText = if (updateAvailable) {

View File

@ -15,7 +15,6 @@ data class WalletSnapshot(
val orchardBalance: WalletBalance, val orchardBalance: WalletBalance,
val saplingBalance: WalletBalance, val saplingBalance: WalletBalance,
val transparentBalance: WalletBalance, val transparentBalance: WalletBalance,
val pendingCount: Int,
val progress: PercentDecimal, val progress: PercentDecimal,
val synchronizerError: SynchronizerError? val synchronizerError: SynchronizerError?
) { ) {

View File

@ -58,6 +58,7 @@ import androidx.compose.ui.unit.dp
import cash.z.ecc.android.sdk.Synchronizer import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.model.FiatCurrencyConversionRateState import cash.z.ecc.android.sdk.model.FiatCurrencyConversionRateState
import cash.z.ecc.android.sdk.model.PercentDecimal import cash.z.ecc.android.sdk.model.PercentDecimal
import cash.z.ecc.android.sdk.model.TransactionOverview
import co.electriccoin.zcash.crash.android.GlobalCrashReporter import co.electriccoin.zcash.crash.android.GlobalCrashReporter
import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.DisableScreenTimeout import co.electriccoin.zcash.ui.common.DisableScreenTimeout
@ -71,7 +72,6 @@ import co.electriccoin.zcash.ui.design.component.PrimaryButton
import co.electriccoin.zcash.ui.design.theme.ZcashTheme import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.fixture.WalletSnapshotFixture import co.electriccoin.zcash.ui.fixture.WalletSnapshotFixture
import co.electriccoin.zcash.ui.screen.home.HomeTag import co.electriccoin.zcash.ui.screen.home.HomeTag
import co.electriccoin.zcash.ui.screen.home.model.CommonTransaction
import co.electriccoin.zcash.ui.screen.home.model.WalletDisplayValues import co.electriccoin.zcash.ui.screen.home.model.WalletDisplayValues
import co.electriccoin.zcash.ui.screen.home.model.WalletSnapshot import co.electriccoin.zcash.ui.screen.home.model.WalletSnapshot
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
@ -110,7 +110,7 @@ fun ComposablePreview() {
@Composable @Composable
fun Home( fun Home(
walletSnapshot: WalletSnapshot, walletSnapshot: WalletSnapshot,
transactionHistory: ImmutableList<CommonTransaction>, transactionHistory: ImmutableList<TransactionOverview>,
isUpdateAvailable: Boolean, isUpdateAvailable: Boolean,
isKeepScreenOnDuringSync: Boolean?, isKeepScreenOnDuringSync: Boolean?,
isFiatConversionEnabled: Boolean, isFiatConversionEnabled: Boolean,
@ -295,7 +295,7 @@ private fun HomeDrawer(
@Composable @Composable
private fun HomeMainContent( private fun HomeMainContent(
walletSnapshot: WalletSnapshot, walletSnapshot: WalletSnapshot,
transactionHistory: ImmutableList<CommonTransaction>, transactionHistory: ImmutableList<TransactionOverview>,
isUpdateAvailable: Boolean, isUpdateAvailable: Boolean,
isKeepScreenOnDuringSync: Boolean?, isKeepScreenOnDuringSync: Boolean?,
isFiatConversionEnabled: Boolean, isFiatConversionEnabled: Boolean,
@ -347,9 +347,7 @@ private fun HomeMainContent(
} }
private fun isSyncing(status: Synchronizer.Status): Boolean { private fun isSyncing(status: Synchronizer.Status): Boolean {
return status == Synchronizer.Status.DOWNLOADING || return status == Synchronizer.Status.SYNCING ||
status == Synchronizer.Status.VALIDATING ||
status == Synchronizer.Status.SCANNING ||
status == Synchronizer.Status.ENHANCING status == Synchronizer.Status.ENHANCING
} }
@ -458,7 +456,7 @@ private fun Status(
@Composable @Composable
@Suppress("MagicNumber") @Suppress("MagicNumber")
private fun History(transactionHistory: ImmutableList<CommonTransaction>) { private fun History(transactionHistory: ImmutableList<TransactionOverview>) {
if (transactionHistory.isEmpty()) { if (transactionHistory.isEmpty()) {
return return
} }

View File

@ -11,15 +11,13 @@ import cash.z.ecc.android.sdk.block.CompactBlockProcessor
import cash.z.ecc.android.sdk.model.Account import cash.z.ecc.android.sdk.model.Account
import cash.z.ecc.android.sdk.model.BlockHeight import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.FiatCurrency import cash.z.ecc.android.sdk.model.FiatCurrency
import cash.z.ecc.android.sdk.model.PendingTransaction
import cash.z.ecc.android.sdk.model.PercentDecimal import cash.z.ecc.android.sdk.model.PercentDecimal
import cash.z.ecc.android.sdk.model.PersistableWallet import cash.z.ecc.android.sdk.model.PersistableWallet
import cash.z.ecc.android.sdk.model.TransactionOverview
import cash.z.ecc.android.sdk.model.WalletAddresses import cash.z.ecc.android.sdk.model.WalletAddresses
import cash.z.ecc.android.sdk.model.WalletBalance import cash.z.ecc.android.sdk.model.WalletBalance
import cash.z.ecc.android.sdk.model.Zatoshi import cash.z.ecc.android.sdk.model.Zatoshi
import cash.z.ecc.android.sdk.model.ZcashNetwork import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.sdk.model.isMined
import cash.z.ecc.android.sdk.model.isSubmitSuccess
import cash.z.ecc.android.sdk.tool.DerivationTool import cash.z.ecc.android.sdk.tool.DerivationTool
import cash.z.ecc.sdk.type.fromResources import cash.z.ecc.sdk.type.fromResources
import co.electriccoin.zcash.global.getInstance import co.electriccoin.zcash.global.getInstance
@ -30,7 +28,6 @@ import co.electriccoin.zcash.ui.preference.EncryptedPreferenceKeys
import co.electriccoin.zcash.ui.preference.EncryptedPreferenceSingleton import co.electriccoin.zcash.ui.preference.EncryptedPreferenceSingleton
import co.electriccoin.zcash.ui.preference.StandardPreferenceKeys import co.electriccoin.zcash.ui.preference.StandardPreferenceKeys
import co.electriccoin.zcash.ui.preference.StandardPreferenceSingleton import co.electriccoin.zcash.ui.preference.StandardPreferenceSingleton
import co.electriccoin.zcash.ui.screen.home.model.CommonTransaction
import co.electriccoin.zcash.ui.screen.home.model.WalletSnapshot import co.electriccoin.zcash.ui.screen.home.model.WalletSnapshot
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
@ -44,7 +41,6 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.WhileSubscribed import kotlinx.coroutines.flow.WhileSubscribed
import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.filterNotNull
@ -154,12 +150,12 @@ class WalletViewModel(application: Application) : AndroidViewModel(application)
// This is not the right API, because the transaction list could be very long and might need UI filtering // This is not the right API, because the transaction list could be very long and might need UI filtering
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
val transactionSnapshot: StateFlow<ImmutableList<CommonTransaction>> = synchronizer val transactionSnapshot: StateFlow<ImmutableList<TransactionOverview>> = synchronizer
.flatMapLatest { .flatMapLatest {
if (null == it) { if (null == it) {
flowOf(persistentListOf()) flowOf(persistentListOf())
} else { } else {
it.toTransactions() it.transactions.map { list -> list.toPersistentList() }
} }
} }
.stateIn( .stateIn(
@ -331,25 +327,14 @@ private fun Synchronizer.toWalletSnapshot() =
orchardBalances, // 2 orchardBalances, // 2
saplingBalances, // 3 saplingBalances, // 3
transparentBalances, // 4 transparentBalances, // 4
pendingTransactions.distinctUntilChanged(), // 5 progress, // 5
progress, // 6 toCommonError() // 6
toCommonError() // 7
) { flows -> ) { flows ->
val pendingCount = (flows[5] as List<*>)
.filterIsInstance(PendingTransaction::class.java)
.count {
it.isSubmitSuccess() && !it.isMined()
}
val orchardBalance = flows[2] as WalletBalance? val orchardBalance = flows[2] as WalletBalance?
val saplingBalance = flows[3] as WalletBalance? val saplingBalance = flows[3] as WalletBalance?
val transparentBalance = flows[4] as WalletBalance? val transparentBalance = flows[4] as WalletBalance?
val progressPercentDecimal = (flows[6] as Int).let { value -> val progressPercentDecimal = flows[5] as PercentDecimal
if (value > PercentDecimal.MAX || value < PercentDecimal.MIN) {
PercentDecimal.ZERO_PERCENT
}
PercentDecimal((flows[6] as Int) / 100f)
}
WalletSnapshot( WalletSnapshot(
flows[0] as Synchronizer.Status, flows[0] as Synchronizer.Status,
@ -357,23 +342,7 @@ private fun Synchronizer.toWalletSnapshot() =
orchardBalance ?: WalletBalance(Zatoshi(0), Zatoshi(0)), orchardBalance ?: WalletBalance(Zatoshi(0), Zatoshi(0)),
saplingBalance ?: WalletBalance(Zatoshi(0), Zatoshi(0)), saplingBalance ?: WalletBalance(Zatoshi(0), Zatoshi(0)),
transparentBalance ?: WalletBalance(Zatoshi(0), Zatoshi(0)), transparentBalance ?: WalletBalance(Zatoshi(0), Zatoshi(0)),
pendingCount,
progressPercentDecimal, progressPercentDecimal,
flows[7] as SynchronizerError? flows[6] as SynchronizerError?
) )
} }
private fun Synchronizer.toTransactions(): Flow<ImmutableList<CommonTransaction>> =
combine(
clearedTransactions.distinctUntilChanged(),
pendingTransactions.distinctUntilChanged()
) { cleared, pending ->
// TODO [#157]: Sort the transactions to show the most recent
// TODO [#157]: https://github.com/zcash/secant-android-wallet/issues/157
// Note that the list of transactions will not be sorted.
buildList {
addAll(cleared.map { CommonTransaction.Overview(it) })
addAll(pending.map { CommonTransaction.Pending(it) })
}.toPersistentList()
}

View File

@ -16,8 +16,6 @@ import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.model.UnifiedSpendingKey import cash.z.ecc.android.sdk.model.UnifiedSpendingKey
import cash.z.ecc.android.sdk.model.Zatoshi import cash.z.ecc.android.sdk.model.Zatoshi
import cash.z.ecc.android.sdk.model.ZecSend import cash.z.ecc.android.sdk.model.ZecSend
import cash.z.ecc.android.sdk.model.isFailedSubmit
import cash.z.ecc.android.sdk.model.isSubmitSuccess
import cash.z.ecc.sdk.send import cash.z.ecc.sdk.send
import co.electriccoin.zcash.spackle.Twig import co.electriccoin.zcash.spackle.Twig
import co.electriccoin.zcash.ui.MainActivity import co.electriccoin.zcash.ui.MainActivity
@ -85,7 +83,9 @@ internal fun WrapSend(
when (sendStage) { when (sendStage) {
SendStage.Form -> goBack() SendStage.Form -> goBack()
SendStage.Confirmation -> setSendStage(SendStage.Form) SendStage.Confirmation -> setSendStage(SendStage.Form)
SendStage.Sending -> { /* no action - wait until done */ } SendStage.Sending -> { // no action - wait until done
}
SendStage.SendFailure -> setSendStage(SendStage.Form) SendStage.SendFailure -> setSendStage(SendStage.Form)
SendStage.SendSuccessful -> goBack() SendStage.SendSuccessful -> goBack()
} }
@ -108,21 +108,16 @@ internal fun WrapSend(
onBack = onBackAction, onBack = onBackAction,
onCreateAndSend = { onCreateAndSend = {
scope.launch { scope.launch {
synchronizer.send(spendingKey, it).collect { Twig.debug { "Sending transaction" }
Twig.debug { "Sending transaction id: ${it.id}" } runCatching { synchronizer.send(spendingKey, it) }
.onSuccess {
if (it.isSubmitSuccess()) {
setSendStage(SendStage.SendSuccessful) setSendStage(SendStage.SendSuccessful)
Twig.debug { Twig.debug { "Transaction id:$it submitted successfully" }
"Transaction id:${it.id} submitted successfully at ${it.createTime} with " + }
"${it.submitAttempts} attempts." .onFailure {
} Twig.debug { "Transaction submission failed with: $it." }
} else if (it.isFailedSubmit()) {
Twig.debug { "Transaction id:${it.id} submission failed with: ${it.errorMessage}." }
setSendStage(SendStage.SendFailure) setSendStage(SendStage.SendFailure)
} }
// All other states of Pending transaction mean waiting for one of the states above
}
} }
}, },
onQrScannerOpen = goToQrScanner, onQrScannerOpen = goToQrScanner,

View File

@ -10,6 +10,7 @@ import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import cash.z.ecc.android.sdk.Synchronizer import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.WalletCoordinator import cash.z.ecc.android.sdk.WalletCoordinator
import cash.z.ecc.android.sdk.model.PercentDecimal
import co.electriccoin.zcash.global.getInstance import co.electriccoin.zcash.global.getInstance
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
@ -35,7 +36,7 @@ class SyncWorker(context: Context, workerParameters: WorkerParameters) : Corouti
} ?: emptyFlow() } ?: emptyFlow()
} }
.takeWhile { .takeWhile {
it.status != Synchronizer.Status.DISCONNECTED && it.progress < ONE_HUNDRED_PERCENT it.status != Synchronizer.Status.DISCONNECTED && it.progress.isLessThanHundredPercent()
} }
.collect() .collect()
@ -43,7 +44,6 @@ class SyncWorker(context: Context, workerParameters: WorkerParameters) : Corouti
} }
companion object { companion object {
private const val ONE_HUNDRED_PERCENT = 100
/* /*
* There may be better periods; we have not optimized for this yet. * There may be better periods; we have not optimized for this yet.
@ -63,4 +63,4 @@ class SyncWorker(context: Context, workerParameters: WorkerParameters) : Corouti
} }
} }
private data class StatusAndProgress(val status: Synchronizer.Status, val progress: Int) private data class StatusAndProgress(val status: Synchronizer.Status, val progress: PercentDecimal)