[#1416] Shielded transaction UI

* [#1416] Shielded transaction UI

* Shielded transaction design update

* [#1416] Design updates

Closes #1416

* [#1416] Code cleanup

Closes #1416

* [#1416] Documentation update

Closes #1416

* [#1416] Code cleanup

Closes #1416

* [#1416] Shielding icon fix

#Closes #1416

* [#1416] Resources update

#Closes #1416

* Add Spanish whatsnew

* [#1416] SDK snapshot

Closes #1416

* Fix failing tests

* Fix changelogs entry

---------

Co-authored-by: Honza <rychnovsky.honza@gmail.com>
This commit is contained in:
Milan 2024-11-12 20:15:23 +01:00 committed by GitHub
parent c6350641e3
commit 60fa9268e6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 124 additions and 46 deletions

View File

@ -11,7 +11,9 @@ and this application adheres to [Semantic Versioning](https://semver.org/spec/v2
- Zashi app now supports Spanish language
- The Flexa SDK has been adopted to enable payments using the embedded Flexa UI
### Changed
### Changelog
- Shielded transactions are properly indicated in transaction history
- The in-app update logic has been fixed and is now correctly requested with every app launch
- The Not enough space and In-app udpate screens have been redesigned
### Fixed

View File

@ -14,7 +14,9 @@ directly impact users rather than highlighting other key architectural updates.*
- Zashi app now supports Spanish language
- The Flexa SDK has been adopted to enable payments using the embedded Flexa UI
### Changed
### Changelog
- Shielded transactions are properly indicated in transaction history
- The in-app update logic has been fixed and is now correctly requested with every app launch
- The Not enough space and In-app udpate screens have been redesigned
### Fixed

View File

@ -14,11 +14,13 @@ directly impact users rather than highlighting other key architectural updates.*
- Zashi app now supports Spanish language
- The Flexa SDK has been adopted to enable payments using the embedded Flexa UI
### Changelog
- Shielded transactions are properly indicated in transaction history
- The in-app update logic has been fixed and is now correctly requested with every app launch
- The Not enough space and In-app udpate screens have been redesigned
### Fixed
- Address book toast now correctly shows on send screen when adding both new and known addresses to text field
- The application now correctly navigates to the homepage after deleting the current wallet and creating a new or
recovering an older one
- The in-app update logic has been fixed and is now correctly requested with every app launch
### Changed
- The Not enough space and In-app udpate screens have been redesigned
- The in-app update logic has been fixed and is now correctly requested with every app launch

View File

@ -219,7 +219,7 @@ ZCASH_BIP39_VERSION=1.0.8
FLEXA_VERSION=1.0.5
# WARNING: Ensure a non-snapshot version is used before releasing to production
ZCASH_SDK_VERSION=2.2.5
ZCASH_SDK_VERSION=2.2.5-SNAPSHOT
# Toolchain is the Java version used to build the application, which is separate from the
# Java version used to run the application.

View File

@ -10,6 +10,7 @@ import cash.z.ecc.android.sdk.model.FastestServersResult
import cash.z.ecc.android.sdk.model.ObserveFiatCurrencyResult
import cash.z.ecc.android.sdk.model.PercentDecimal
import cash.z.ecc.android.sdk.model.Proposal
import cash.z.ecc.android.sdk.model.TransactionOutput
import cash.z.ecc.android.sdk.model.TransactionOverview
import cash.z.ecc.android.sdk.model.TransactionRecipient
import cash.z.ecc.android.sdk.model.TransactionSubmitResult
@ -234,6 +235,10 @@ internal class MockSynchronizer : CloseableSynchronizer {
error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.")
}
override suspend fun getTransactionOutputs(transactionOverview: TransactionOverview): List<TransactionOutput> {
error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.")
}
companion object {
fun new() = MockSynchronizer()
}

View File

@ -15,17 +15,20 @@ internal object TransactionHistorySyncStateFixture {
TransactionOverviewExt(
TransactionOverviewFixture.new(),
TransactionRecipient.Account(Account.DEFAULT),
AddressType.Shielded
AddressType.Shielded,
emptyList()
),
TransactionOverviewExt(
TransactionOverviewFixture.new(),
TransactionRecipient.Account(Account(1)),
AddressType.Transparent
AddressType.Transparent,
emptyList()
),
TransactionOverviewExt(
TransactionOverviewFixture.new(),
null,
AddressType.Unified
AddressType.Unified,
emptyList()
),
)
val STATE = TransactionHistorySyncState.Syncing(TRANSACTIONS)

View File

@ -108,6 +108,8 @@ class WalletViewModel(
it.getSortHeight(networkHeight)
}
.map {
val outputs = synchronizer.getTransactionOutputs(it)
if (it.isSentTransaction) {
val recipient = synchronizer.getRecipients(it).firstOrNull()
TransactionOverviewExt(
@ -118,14 +120,16 @@ class WalletViewModel(
synchronizer.validateAddress(recipient.addressValue)
} else {
null
}
},
transactionOutputs = outputs,
)
} else {
// Note that recipients can only be queried for sent transactions
TransactionOverviewExt(
overview = it,
recipient = null,
recipientAddressType = null
recipientAddressType = null,
transactionOutputs = outputs,
)
}
}

View File

@ -1,6 +1,7 @@
package co.electriccoin.zcash.ui.screen.account.ext
import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.sdk.model.TransactionOutput
import cash.z.ecc.android.sdk.model.TransactionOverview
import cash.z.ecc.android.sdk.model.TransactionRecipient
import cash.z.ecc.android.sdk.type.AddressType
@ -8,7 +9,8 @@ import cash.z.ecc.android.sdk.type.AddressType
data class TransactionOverviewExt(
val overview: TransactionOverview,
val recipient: TransactionRecipient?,
val recipientAddressType: AddressType?
val recipientAddressType: AddressType?,
val transactionOutputs: List<TransactionOutput>
)
/**

View File

@ -2,6 +2,7 @@ package co.electriccoin.zcash.ui.screen.account.fixture
import cash.z.ecc.android.sdk.fixture.TransactionOverviewFixture
import cash.z.ecc.android.sdk.fixture.WalletFixture
import cash.z.ecc.android.sdk.model.TransactionOutput
import cash.z.ecc.android.sdk.model.TransactionOverview
import cash.z.ecc.android.sdk.model.TransactionRecipient
import cash.z.ecc.android.sdk.model.ZcashNetwork
@ -26,6 +27,8 @@ object TransactionUiFixture {
val ADDRESS_BOOK_CONTACT: AddressBookContact? = null
val OUTPUTS: List<TransactionOutput> = emptyList()
@Suppress("LongParameterList")
internal fun new(
overview: TransactionOverview = OVERVIEW,
@ -33,13 +36,15 @@ object TransactionUiFixture {
recipientAddressType: AddressType = RECIPIENT_ADDRESS_TYPE,
expandableState: TrxItemState = EXPANDABLE_STATE,
messages: List<String> = MESSAGES,
addressBookContact: AddressBookContact? = ADDRESS_BOOK_CONTACT
addressBookContact: AddressBookContact? = ADDRESS_BOOK_CONTACT,
outputs: List<TransactionOutput> = OUTPUTS
) = TransactionUi(
overview = overview,
recipient = recipient,
recipientAddressType = recipientAddressType,
expandableState = expandableState,
messages = messages,
addressBookContact = addressBookContact
addressBookContact = addressBookContact,
outputs = outputs
)
}

View File

@ -1,5 +1,6 @@
package co.electriccoin.zcash.ui.screen.account.model
import cash.z.ecc.android.sdk.model.TransactionOutput
import cash.z.ecc.android.sdk.model.TransactionOverview
import cash.z.ecc.android.sdk.model.TransactionRecipient
import cash.z.ecc.android.sdk.type.AddressType
@ -12,21 +13,23 @@ data class TransactionUi(
val recipientAddressType: AddressType?,
val expandableState: TrxItemState,
val messages: List<String>?,
val addressBookContact: AddressBookContact?
val addressBookContact: AddressBookContact?,
val outputs: List<TransactionOutput>,
) {
companion object {
fun new(
data: TransactionOverviewExt,
expandableState: TrxItemState,
messages: List<String>?,
addressBookContact: AddressBookContact?
addressBookContact: AddressBookContact?,
) = TransactionUi(
overview = data.overview,
recipient = data.recipient,
recipientAddressType = data.recipientAddressType,
expandableState = expandableState,
messages = messages,
addressBookContact = addressBookContact
addressBookContact = addressBookContact,
outputs = data.transactionOutputs
)
}
}

View File

@ -46,6 +46,7 @@ import androidx.compose.ui.unit.dp
import cash.z.ecc.android.sdk.fixture.TransactionOverviewFixture
import cash.z.ecc.android.sdk.model.FirstClassByteArray
import cash.z.ecc.android.sdk.model.TransactionOverview
import cash.z.ecc.android.sdk.model.TransactionPool
import cash.z.ecc.android.sdk.model.TransactionRecipient
import cash.z.ecc.android.sdk.model.TransactionState
import cash.z.ecc.android.sdk.model.Zatoshi
@ -316,7 +317,7 @@ private fun ComposableHistoryListItemsPreview() {
const val ADDRESS_IN_TITLE_WIDTH_RATIO = 0.5f
@Composable
@Suppress("LongMethod")
@Suppress("LongMethod", "CyclomaticComplexMethod")
private fun HistoryItem(
transaction: TransactionUi,
isHideBalances: Boolean,
@ -375,6 +376,30 @@ private fun HistoryItem(
textDecoration = TextDecoration.LineThrough
)
}
TransactionExtendedState.SHIELDED -> {
typeText = stringResource(id = R.string.account_history_item_shielded_funds)
typeIcon = ImageVector.vectorResource(R.drawable.ic_trx_shielded_funds)
textColor = ZashiColors.Text.textPrimary
textStyle = ZashiTypography.textSm
}
TransactionExtendedState.SHIELDING -> {
typeText = stringResource(id = R.string.account_history_item_shielding_funds)
typeIcon = ImageVector.vectorResource(R.drawable.ic_trx_shielded_funds)
textColor = ZashiColors.Text.textPrimary
textStyle = ZashiTypography.textSm
}
TransactionExtendedState.SHIELDING_FAILED -> {
typeText = stringResource(id = R.string.account_history_item_shielding_failed)
typeIcon = ImageVector.vectorResource(R.drawable.ic_trx_shielded_funds)
textColor = ZashiColors.Text.textError
textStyle =
ZashiTypography.textSm.copy(
textDecoration = TextDecoration.LineThrough
)
}
}
Row(
@ -581,7 +606,10 @@ private fun HistoryItemCollapsedAddressPart(
)
}
}
} else {
} else if (
transaction.outputs.none { it.pool == TransactionPool.TRANSPARENT } &&
!transaction.overview.isShielding
) {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_trx_shielded),
contentDescription = stringResource(id = R.string.account_history_item_shielded)
@ -689,7 +717,7 @@ private fun HistoryItemExpandedPart(
modifier: Modifier = Modifier
) {
Column(modifier = modifier) {
if (transaction.messages.containsValidMemo()) {
if (transaction.overview.isShielding.not() && transaction.messages.containsValidMemo()) {
Text(
text =
pluralStringResource(
@ -974,41 +1002,41 @@ internal enum class TransactionExtendedState {
SEND_FAILED,
RECEIVED,
RECEIVING,
RECEIVE_FAILED;
RECEIVE_FAILED,
SHIELDED,
SHIELDING,
SHIELDING_FAILED;
fun isFailed(): Boolean = this == SEND_FAILED || this == RECEIVE_FAILED
fun isShielding() = this in listOf(SHIELDED, RECEIVE_FAILED, SHIELDING_FAILED)
fun isSendType(): Boolean = this == SENDING || this == SENT || this == SEND_FAILED
fun isFailed(): Boolean = this in listOf(SEND_FAILED, RECEIVE_FAILED, SHIELDING_FAILED)
fun isSendType(): Boolean = this in listOf(SENDING, SENT, SEND_FAILED, SHIELDED, SHIELDING_FAILED, SHIELDING)
}
private fun TransactionOverview.getExtendedState(): TransactionExtendedState {
return when (transactionState) {
TransactionState.Expired -> {
if (isSentTransaction) {
TransactionExtendedState.SEND_FAILED
} else {
TransactionExtendedState.RECEIVE_FAILED
TransactionState.Expired ->
when {
isShielding -> TransactionExtendedState.SHIELDING_FAILED
isSentTransaction -> TransactionExtendedState.SEND_FAILED
else -> TransactionExtendedState.RECEIVE_FAILED
}
}
TransactionState.Confirmed -> {
if (isSentTransaction) {
TransactionExtendedState.SENT
} else {
TransactionExtendedState.RECEIVED
TransactionState.Confirmed ->
when {
isShielding -> TransactionExtendedState.SHIELDED
isSentTransaction -> TransactionExtendedState.SENT
else -> TransactionExtendedState.RECEIVED
}
}
TransactionState.Pending -> {
if (isSentTransaction) {
TransactionExtendedState.SENDING
} else {
TransactionExtendedState.RECEIVING
TransactionState.Pending ->
when {
isShielding -> TransactionExtendedState.SHIELDING
isSentTransaction -> TransactionExtendedState.SENDING
else -> TransactionExtendedState.RECEIVING
}
}
else -> {
error("Unexpected transaction state found while calculating its extended state.")
}
else -> error("Unexpected transaction state found while calculating its extended state.")
}
}

View File

@ -131,7 +131,7 @@ class TransactionHistoryViewModel(
data = data,
expandableState = existingTransaction?.expandableState ?: TrxItemState.COLLAPSED,
messages = existingTransaction?.messages,
addressBookContact = addressBookContact
addressBookContact = addressBookContact,
)
}

View File

@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="19dp"
android:height="18dp"
android:viewportWidth="19"
android:viewportHeight="18">
<group>
<clip-path
android:pathData="M5.979,0l12.774,8.126l-5.979,9.399l-12.774,-8.126z"/>
<path
android:pathData="M16.174,6.853C16.209,6.649 16.121,6.451 15.944,6.339L10.325,2.764C10.215,2.694 10.087,2.672 9.949,2.703C9.811,2.734 9.704,2.808 9.634,2.918C9.565,3.027 9.537,3.164 9.573,3.293C9.601,3.418 9.678,3.538 9.788,3.608L14.04,6.313L4.548,6.367L2.102,6.375C1.84,6.433 1.659,6.698 1.721,6.974C1.782,7.25 2.049,7.408 2.32,7.355L15.792,7.249C15.985,7.206 16.145,7.047 16.174,6.853Z"
android:fillColor="#000000"/>
<path
android:pathData="M17.028,10.56C17,10.436 16.923,10.315 16.813,10.245C16.703,10.176 16.567,10.148 16.437,10.184L2.962,10.276C2.769,10.319 2.609,10.478 2.58,10.673C2.545,10.876 2.633,11.074 2.801,11.181L8.421,14.756C8.53,14.826 8.667,14.853 8.797,14.817C8.921,14.79 9.041,14.712 9.111,14.602C9.181,14.493 9.208,14.356 9.172,14.227C9.145,14.102 9.067,13.982 8.957,13.912L4.815,11.277L16.652,11.151C16.914,11.092 17.094,10.828 17.033,10.552L17.028,10.56Z"
android:fillColor="#000000"/>
</group>
</vector>

View File

@ -3,6 +3,9 @@
<string name="account_history_empty">Sin historial de transacciones</string>
<string name="account_history_item_sent">Enviado</string>
<string name="account_history_item_shielded_funds">Fondos Protegidos</string>
<string name="account_history_item_shielding_funds">Protegiendo fondos…</string>
<string name="account_history_item_shielding_failed">Fondos Protegidos Fallidos…</string>
<string name="account_history_item_received">Recibido</string>
<string name="account_history_item_sending">Enviando…</string>
<string name="account_history_item_receiving">Recibiendo…</string>

View File

@ -3,6 +3,9 @@
<string name="account_history_empty">No transaction history</string>
<string name="account_history_item_sent">Sent</string>
<string name="account_history_item_shielded_funds">Shielded Funds</string>
<string name="account_history_item_shielding_funds">Shielding Funds…</string>
<string name="account_history_item_shielding_failed">Shielding Failed…</string>
<string name="account_history_item_received">Received</string>
<string name="account_history_item_sending">Sending…</string>
<string name="account_history_item_receiving">Receiving…</string>