[#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:
parent
b544de316d
commit
2f33ccf818
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue