[#1143] Transaction ID row in Transaction item

* [#1143] Transaction ID row in Transaction item

- Added a new transaction ID row into the transaction item on the Transactions screen
- It displays a TODO value instead of the TXID until the #1316 in SDK done
- Closes #1143

Changelog update
This commit is contained in:
Honza Rychnovský 2024-01-02 14:25:24 +01:00 committed by GitHub
parent b544de316d
commit 2f33ccf818
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 124 additions and 45 deletions

View File

@ -11,6 +11,7 @@ directly impact users rather than highlighting other key architectural updates.*
### Added ### Added
- Transaction history items now display Memos within the Android Toast, triggered by clicking the item - Transaction history items now display Memos within the Android Toast, triggered by clicking the item
- Transaction history items add displaying transaction IDs; the ID element is also clickable
## [0.2.0 (517)] - 2023-12-21 ## [0.2.0 (517)] - 2023-12-21

View File

@ -124,6 +124,27 @@ fun Small(
) )
} }
@Composable
@Suppress("LongParameterList")
fun Tiny(
text: String,
modifier: Modifier = Modifier,
maxLines: Int = Int.MAX_VALUE,
overflow: TextOverflow = TextOverflow.Clip,
textAlign: TextAlign = TextAlign.Start,
color: Color = MaterialTheme.colorScheme.onBackground,
) {
Text(
text = text,
color = color,
maxLines = maxLines,
overflow = overflow,
textAlign = textAlign,
modifier = modifier,
style = MaterialTheme.typography.labelSmall,
)
}
@Composable @Composable
fun ListItem( fun ListItem(
text: String, text: String,

View File

@ -12,6 +12,7 @@ class HistoryTestSetup(
) { ) {
private val onBackClickCount = AtomicInteger(0) private val onBackClickCount = AtomicInteger(0)
private val onItemClickCount = AtomicInteger(0) private val onItemClickCount = AtomicInteger(0)
private val onItemIdClickCount = AtomicInteger(0)
fun getOnBackClickCount(): Int { fun getOnBackClickCount(): Int {
composeTestRule.waitForIdle() composeTestRule.waitForIdle()
@ -23,6 +24,11 @@ class HistoryTestSetup(
return onItemClickCount.get() return onItemClickCount.get()
} }
fun getOnItemIdClickCount(): Int {
composeTestRule.waitForIdle()
return onItemIdClickCount.get()
}
init { init {
composeTestRule.setContent { composeTestRule.setContent {
ZcashTheme { ZcashTheme {
@ -33,6 +39,9 @@ class HistoryTestSetup(
}, },
onItemClick = { onItemClick = {
onItemClickCount.incrementAndGet() onItemClickCount.incrementAndGet()
},
onTransactionIdClick = {
onItemIdClickCount.incrementAndGet()
} }
) )
} }

View File

@ -1,5 +1,6 @@
package co.electriccoin.zcash.ui.screen.history.view package co.electriccoin.zcash.ui.screen.history.view
import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.assertHeightIsAtLeast import androidx.compose.ui.test.assertHeightIsAtLeast
import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onAllNodesWithTag import androidx.compose.ui.test.onAllNodesWithTag
@ -127,7 +128,9 @@ class HistoryViewTest {
assertEquals(0, testSetup.getOnItemClickCount()) assertEquals(0, testSetup.getOnItemClickCount())
composeTestRule.onAllNodesWithTag(HistoryTag.TRANSACTION_ITEM).also { composeTestRule.onAllNodesWithTag(HistoryTag.TRANSACTION_ITEM, useUnmergedTree = true).also {
it.assertCountEquals(TransactionHistorySyncStateFixture.TRANSACTIONS.size)
TransactionHistorySyncStateFixture.TRANSACTIONS.forEachIndexed { index, _ -> TransactionHistorySyncStateFixture.TRANSACTIONS.forEachIndexed { index, _ ->
it[index].performClick() it[index].performClick()
} }
@ -136,6 +139,24 @@ class HistoryViewTest {
assertEquals(TransactionHistorySyncStateFixture.TRANSACTIONS.size, testSetup.getOnItemClickCount()) assertEquals(TransactionHistorySyncStateFixture.TRANSACTIONS.size, testSetup.getOnItemClickCount())
} }
@Test
@MediumTest
fun transaction_id_click_test() {
val testSetup = newTestSetup(TransactionHistorySyncStateFixture.STATE)
assertEquals(0, testSetup.getOnItemIdClickCount())
composeTestRule.onAllNodesWithTag(HistoryTag.TRANSACTION_ID).also {
it.assertCountEquals(TransactionHistorySyncStateFixture.TRANSACTIONS.size)
TransactionHistorySyncStateFixture.TRANSACTIONS.forEachIndexed { index, _ ->
it[index].performClick()
}
}
assertEquals(TransactionHistorySyncStateFixture.TRANSACTIONS.size, testSetup.getOnItemIdClickCount())
}
private fun newTestSetup( private fun newTestSetup(
transactionHistorySyncState: TransactionHistorySyncState = TransactionHistorySyncStateFixture.new() transactionHistorySyncState: TransactionHistorySyncState = TransactionHistorySyncStateFixture.new()
): HistoryTestSetup { ): HistoryTestSetup {

View File

@ -10,7 +10,6 @@ import co.electriccoin.zcash.spackle.ClipboardManagerUtil
import co.electriccoin.zcash.ui.MainActivity import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator
import co.electriccoin.zcash.ui.screen.history.view.History import co.electriccoin.zcash.ui.screen.history.view.History
import kotlinx.coroutines.flow.toList import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -39,30 +38,31 @@ internal fun WrapHistory(
Twig.info { "Current transaction history state: $transactionHistoryState" } Twig.info { "Current transaction history state: $transactionHistoryState" }
if (synchronizer == null) { History(
// TODO [#1146]: Consider moving CircularScreenProgressIndicator from Android layer to View layer transactionState = transactionHistoryState,
// TODO [#1146]: Improve this by allowing screen composition and updating it after the data is available onBack = goBack,
// TODO [#1146]: https://github.com/Electric-Coin-Company/zashi-android/issues/1146 onItemClick = { tx ->
CircularScreenProgressIndicator() Twig.debug { "Transaction item clicked - querying memos..." }
} else { val memos = synchronizer?.getMemos(tx)
History( queryScope.launch {
transactionState = transactionHistoryState, memos?.toList()?.let {
onBack = goBack, val merged = it.joinToString().ifEmpty { "-" }
onItemClick = { tx -> Twig.info { "Transaction memos: count: ${it.size}, contains: $merged" }
Twig.debug { "Querying transaction memos..." } ClipboardManagerUtil.copyToClipboard(
val memos = synchronizer.getMemos(tx) activity.applicationContext,
queryScope.launch { activity.getString(R.string.history_item_clipboard_tag),
memos.toList().run { merged
val merged = joinToString().ifEmpty { "-" } )
Twig.info { "Transaction memos: count: $size, contains: $merged" }
ClipboardManagerUtil.copyToClipboard(
activity.applicationContext,
activity.getString(R.string.history_item_clipboard_tag),
merged
)
}
} }
}, }
) },
} onTransactionIdClick = { txId ->
Twig.debug { "Transaction ID clicked: $txId" }
ClipboardManagerUtil.copyToClipboard(
activity.applicationContext,
activity.getString(R.string.history_id_clipboard_tag),
txId
)
}
)
} }

View File

@ -6,5 +6,6 @@ package co.electriccoin.zcash.ui.screen.history
object HistoryTag { object HistoryTag {
const val TRANSACTION_LIST = "transaction_list" const val TRANSACTION_LIST = "transaction_list"
const val TRANSACTION_ITEM = "transaction_item" const val TRANSACTION_ITEM = "transaction_item"
const val TRANSACTION_ID = "transaction_id"
const val PROGRESS = "progress_bar" const val PROGRESS = "progress_bar"
} }

View File

@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
@ -49,7 +50,9 @@ import cash.z.ecc.android.sdk.model.toZecString
import cash.z.ecc.sdk.type.ZcashCurrency import cash.z.ecc.sdk.type.ZcashCurrency
import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.component.Body import co.electriccoin.zcash.ui.design.component.Body
import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator
import co.electriccoin.zcash.ui.design.component.GradientSurface import co.electriccoin.zcash.ui.design.component.GradientSurface
import co.electriccoin.zcash.ui.design.component.Tiny
import co.electriccoin.zcash.ui.design.theme.ZcashTheme import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.screen.history.HistoryTag import co.electriccoin.zcash.ui.screen.history.HistoryTag
import co.electriccoin.zcash.ui.screen.history.state.TransactionHistorySyncState import co.electriccoin.zcash.ui.screen.history.state.TransactionHistorySyncState
@ -67,7 +70,8 @@ private fun ComposablePreview() {
History( History(
transactionState = TransactionHistorySyncState.Loading, transactionState = TransactionHistorySyncState.Loading,
onBack = {}, onBack = {},
onItemClick = {} onItemClick = {},
onTransactionIdClick = {}
) )
} }
} }
@ -89,7 +93,8 @@ private fun ComposableHistoryListPreview() {
) )
), ),
onBack = {}, onBack = {},
onItemClick = {} onItemClick = {},
onTransactionIdClick = {}
) )
} }
} }
@ -107,7 +112,8 @@ val dateFormat: DateFormat by lazy {
fun History( fun History(
transactionState: TransactionHistorySyncState, transactionState: TransactionHistorySyncState,
onBack: () -> Unit, onBack: () -> Unit,
onItemClick: (TransactionOverview) -> Unit onItemClick: (TransactionOverview) -> Unit,
onTransactionIdClick: (String) -> Unit
) { ) {
Scaffold(topBar = { Scaffold(topBar = {
HistoryTopBar(onBack = onBack) HistoryTopBar(onBack = onBack)
@ -115,6 +121,7 @@ fun History(
HistoryMainContent( HistoryMainContent(
transactionState = transactionState, transactionState = transactionState,
onItemClick = onItemClick, onItemClick = onItemClick,
onTransactionIdClick = onTransactionIdClick,
modifier = modifier =
Modifier Modifier
.fillMaxHeight() .fillMaxHeight()
@ -145,15 +152,17 @@ private fun HistoryTopBar(onBack: () -> Unit) {
} }
@Composable @Composable
@Suppress("LongMethod")
private fun HistoryMainContent( private fun HistoryMainContent(
transactionState: TransactionHistorySyncState, transactionState: TransactionHistorySyncState,
onItemClick: (TransactionOverview) -> Unit, onItemClick: (TransactionOverview) -> Unit,
onTransactionIdClick: (String) -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
Box(modifier = modifier.fillMaxSize()) { Box(modifier = modifier.fillMaxSize()) {
when (transactionState) { when (transactionState) {
is TransactionHistorySyncState.Loading -> { is TransactionHistorySyncState.Loading -> {
CircularProgressIndicator( CircularScreenProgressIndicator(
modifier = modifier =
Modifier Modifier
.align(alignment = Center) .align(alignment = Center)
@ -177,7 +186,8 @@ private fun HistoryMainContent(
) )
HistoryList( HistoryList(
transactions = transactionState.transactions, transactions = transactionState.transactions,
onItemClick = onItemClick onItemClick = onItemClick,
onTransactionIdClick = onTransactionIdClick
) )
} }
// Add progress indicator only in the state of empty transaction // Add progress indicator only in the state of empty transaction
@ -202,7 +212,8 @@ private fun HistoryMainContent(
} else { } else {
HistoryList( HistoryList(
transactions = transactionState.transactions, transactions = transactionState.transactions,
onItemClick = onItemClick onItemClick = onItemClick,
onTransactionIdClick = onTransactionIdClick
) )
} }
} }
@ -213,7 +224,8 @@ private fun HistoryMainContent(
@Composable @Composable
private fun HistoryList( private fun HistoryList(
transactions: ImmutableList<TransactionOverview>, transactions: ImmutableList<TransactionOverview>,
onItemClick: (TransactionOverview) -> Unit onItemClick: (TransactionOverview) -> Unit,
onTransactionIdClick: (String) -> Unit
) { ) {
val currency = ZcashCurrency.fromResources(LocalContext.current) val currency = ZcashCurrency.fromResources(LocalContext.current)
LazyColumn(modifier = Modifier.testTag(HistoryTag.TRANSACTION_LIST)) { LazyColumn(modifier = Modifier.testTag(HistoryTag.TRANSACTION_LIST)) {
@ -222,7 +234,7 @@ private fun HistoryList(
transaction = item, transaction = item,
currency = currency, currency = currency,
onItemClick = onItemClick, onItemClick = onItemClick,
modifier = Modifier.testTag(HistoryTag.TRANSACTION_ITEM) onIdClick = onTransactionIdClick,
) )
if (index < transactions.lastIndex) { if (index < transactions.lastIndex) {
Divider( Divider(
@ -240,6 +252,7 @@ fun HistoryItem(
transaction: TransactionOverview, transaction: TransactionOverview,
currency: ZcashCurrency, currency: ZcashCurrency,
onItemClick: (TransactionOverview) -> Unit, onItemClick: (TransactionOverview) -> Unit,
onIdClick: (String) -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val transactionTypeText: String val transactionTypeText: String
@ -269,15 +282,13 @@ fun HistoryItem(
Row( Row(
modifier = modifier =
modifier.then( Modifier
Modifier .fillMaxWidth()
.fillMaxWidth() .clickable { onItemClick(transaction) }
.clickable { onItemClick(transaction) } .padding(
.padding( horizontal = ZcashTheme.dimens.spacingDefault,
horizontal = ZcashTheme.dimens.spacingDefault, vertical = ZcashTheme.dimens.spacingDefault
vertical = ZcashTheme.dimens.spacingDefault ).then(modifier),
)
),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Image( Image(
@ -296,7 +307,8 @@ fun HistoryItem(
) { ) {
Body( Body(
text = transactionTypeText, text = transactionTypeText,
color = Color.Black color = Color.Black,
modifier = Modifier.testTag(HistoryTag.TRANSACTION_ITEM)
) )
val dateString = val dateString =
@ -332,6 +344,19 @@ fun HistoryItem(
} }
} }
} }
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingTiny))
// TODO [#1316]: Provide readable TxId on TransactionOverview
// TODO [#1316]: https://github.com/Electric-Coin-Company/zcash-android-wallet-sdk/issues/1316
// TODO [#1316]: transaction.rawId.byteArray.toHexReversed()
val txId = "TODO [#1316]: SDK: Provide readable TxId"
Tiny(
text = txId,
modifier =
Modifier
.clickable { onIdClick(txId) }
.testTag(HistoryTag.TRANSACTION_ID)
)
} }
} }
} }

View File

@ -4,6 +4,7 @@
<string name="history_syncing">Additional transactions may be found after syncing completes…</string> <string name="history_syncing">Additional transactions may be found after syncing completes…</string>
<string name="history_empty">No transactions yet</string> <string name="history_empty">No transactions yet</string>
<string name="history_item_clipboard_tag">Transaction memo</string> <string name="history_item_clipboard_tag">Transaction memo</string>
<string name="history_id_clipboard_tag">Transaction ID</string>
<string name="history_item_sent">Sent</string> <string name="history_item_sent">Sent</string>
<string name="history_item_received">Received</string> <string name="history_item_received">Received</string>