[#1307] Keep transaction history UI state
- Closes #1307 - Changelog update - #1162 is the direct follow-up of this PR
This commit is contained in:
parent
809820c1f2
commit
c99c2907b1
|
@ -9,6 +9,7 @@ syntax: glob
|
||||||
.idea/modules.xml
|
.idea/modules.xml
|
||||||
.idea/tasks.xml
|
.idea/tasks.xml
|
||||||
.idea/workspace.xml
|
.idea/workspace.xml
|
||||||
|
.idea/deploymentTargetSelector.xml
|
||||||
.settings
|
.settings
|
||||||
*.iml
|
*.iml
|
||||||
bin/
|
bin/
|
||||||
|
|
|
@ -21,7 +21,8 @@ directly impact users rather than highlighting other key architectural updates.*
|
||||||
submission
|
submission
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- The Transaction History UI has been incorporated into the Account screen
|
- The Transaction History UI has been incorporated into the Account screen and partly reworked according to the
|
||||||
|
design guidelines
|
||||||
- Reworked Send screens flow and their look (e.g., Send Failure screen is now a modal dialog instead of a separate
|
- Reworked Send screens flow and their look (e.g., Send Failure screen is now a modal dialog instead of a separate
|
||||||
screen)
|
screen)
|
||||||
- The sending and shielding funds logic has been connected to the new Proposal API from the Zcash SDK
|
- The sending and shielding funds logic has been connected to the new Proposal API from the Zcash SDK
|
||||||
|
|
|
@ -23,7 +23,7 @@ private fun CircularScreenProgressIndicatorComposablePreview() {
|
||||||
GradientSurface {
|
GradientSurface {
|
||||||
Column {
|
Column {
|
||||||
CircularScreenProgressIndicator()
|
CircularScreenProgressIndicator()
|
||||||
CircularSmallProgressIndicator()
|
CircularMidProgressIndicator()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,18 @@ fun CircularScreenProgressIndicator(modifier: Modifier = Modifier) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CircularMidProgressIndicator(modifier: Modifier = Modifier) {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
color = ZcashTheme.colors.circularProgressBarScreen,
|
||||||
|
strokeWidth = 3.dp,
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.size(ZcashTheme.dimens.circularMidProgressWidth)
|
||||||
|
.then(modifier)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CircularSmallProgressIndicator(modifier: Modifier = Modifier) {
|
fun CircularSmallProgressIndicator(modifier: Modifier = Modifier) {
|
||||||
CircularProgressIndicator(
|
CircularProgressIndicator(
|
||||||
|
|
|
@ -33,6 +33,7 @@ data class Dimens(
|
||||||
val chipStroke: Dp,
|
val chipStroke: Dp,
|
||||||
// Progress
|
// Progress
|
||||||
val circularScreenProgressWidth: Dp,
|
val circularScreenProgressWidth: Dp,
|
||||||
|
val circularMidProgressWidth: Dp,
|
||||||
val circularSmallProgressWidth: Dp,
|
val circularSmallProgressWidth: Dp,
|
||||||
val linearProgressHeight: Dp,
|
val linearProgressHeight: Dp,
|
||||||
// TopAppBar:
|
// TopAppBar:
|
||||||
|
@ -75,6 +76,7 @@ private val defaultDimens =
|
||||||
chipShadowElevation = 4.dp,
|
chipShadowElevation = 4.dp,
|
||||||
chipStroke = 0.5.dp,
|
chipStroke = 0.5.dp,
|
||||||
circularScreenProgressWidth = 48.dp,
|
circularScreenProgressWidth = 48.dp,
|
||||||
|
circularMidProgressWidth = 22.dp,
|
||||||
circularSmallProgressWidth = 14.dp,
|
circularSmallProgressWidth = 14.dp,
|
||||||
linearProgressHeight = 14.dp,
|
linearProgressHeight = 14.dp,
|
||||||
topAppBarZcashLogoHeight = 24.dp,
|
topAppBarZcashLogoHeight = 24.dp,
|
||||||
|
|
|
@ -4,8 +4,8 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
||||||
import co.electriccoin.zcash.ui.common.model.WalletSnapshot
|
import co.electriccoin.zcash.ui.common.model.WalletSnapshot
|
||||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||||
import co.electriccoin.zcash.ui.screen.account.history.fixture.TransactionHistorySyncStateFixture
|
import co.electriccoin.zcash.ui.screen.account.history.fixture.TransactionHistoryUiStateFixture
|
||||||
import co.electriccoin.zcash.ui.screen.account.state.TransactionHistorySyncState
|
import co.electriccoin.zcash.ui.screen.account.model.TransactionUiState
|
||||||
import co.electriccoin.zcash.ui.screen.account.view.Account
|
import co.electriccoin.zcash.ui.screen.account.view.Account
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ class AccountTestSetup(
|
||||||
// TODO [#1282]: Update AccountView Tests #1282
|
// TODO [#1282]: Update AccountView Tests #1282
|
||||||
// TODO [#1282]: https://github.com/Electric-Coin-Company/zashi-android/issues/1282
|
// TODO [#1282]: https://github.com/Electric-Coin-Company/zashi-android/issues/1282
|
||||||
|
|
||||||
val initialHistorySyncState: TransactionHistorySyncState = TransactionHistorySyncStateFixture.new()
|
val initialTransactionState: TransactionUiState = TransactionHistoryUiStateFixture.new()
|
||||||
|
|
||||||
private val onSettingsCount = AtomicInteger(0)
|
private val onSettingsCount = AtomicInteger(0)
|
||||||
private val onReceiveCount = AtomicInteger(0)
|
private val onReceiveCount = AtomicInteger(0)
|
||||||
|
@ -64,13 +64,10 @@ class AccountTestSetup(
|
||||||
onSettingsCount.incrementAndGet()
|
onSettingsCount.incrementAndGet()
|
||||||
},
|
},
|
||||||
goBalances = {},
|
goBalances = {},
|
||||||
transactionState = initialHistorySyncState,
|
transactionsUiState = initialTransactionState,
|
||||||
onItemClick = {
|
onTransactionItemAction = {
|
||||||
onItemClickCount.incrementAndGet()
|
onItemClickCount.incrementAndGet()
|
||||||
},
|
},
|
||||||
onTransactionIdClick = {
|
|
||||||
onItemIdClickCount.incrementAndGet()
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,13 @@ package co.electriccoin.zcash.ui.screen.account.history
|
||||||
|
|
||||||
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
||||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||||
import co.electriccoin.zcash.ui.screen.account.state.TransactionHistorySyncState
|
import co.electriccoin.zcash.ui.screen.account.model.TransactionUiState
|
||||||
import co.electriccoin.zcash.ui.screen.account.view.HistoryContainer
|
import co.electriccoin.zcash.ui.screen.account.view.HistoryContainer
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
class HistoryTestSetup(
|
class HistoryTestSetup(
|
||||||
private val composeTestRule: ComposeContentTestRule,
|
private val composeTestRule: ComposeContentTestRule,
|
||||||
initialHistorySyncState: TransactionHistorySyncState
|
initialHistoryUiState: TransactionUiState
|
||||||
) {
|
) {
|
||||||
private val onItemClickCount = AtomicInteger(0)
|
private val onItemClickCount = AtomicInteger(0)
|
||||||
private val onItemIdClickCount = AtomicInteger(0)
|
private val onItemIdClickCount = AtomicInteger(0)
|
||||||
|
@ -27,11 +27,8 @@ class HistoryTestSetup(
|
||||||
composeTestRule.setContent {
|
composeTestRule.setContent {
|
||||||
ZcashTheme {
|
ZcashTheme {
|
||||||
HistoryContainer(
|
HistoryContainer(
|
||||||
transactionState = initialHistorySyncState,
|
transactionState = initialHistoryUiState,
|
||||||
onItemClick = {
|
onTransactionItemAction = {
|
||||||
onItemClickCount.incrementAndGet()
|
|
||||||
},
|
|
||||||
onTransactionIdClick = {
|
|
||||||
onItemIdClickCount.incrementAndGet()
|
onItemIdClickCount.incrementAndGet()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,8 +3,8 @@ package co.electriccoin.zcash.ui.screen.account.history.fixture
|
||||||
import cash.z.ecc.android.sdk.fixture.TransactionOverviewFixture
|
import cash.z.ecc.android.sdk.fixture.TransactionOverviewFixture
|
||||||
import cash.z.ecc.android.sdk.model.Account
|
import cash.z.ecc.android.sdk.model.Account
|
||||||
import cash.z.ecc.android.sdk.model.TransactionRecipient
|
import cash.z.ecc.android.sdk.model.TransactionRecipient
|
||||||
|
import co.electriccoin.zcash.ui.screen.account.ext.TransactionOverviewExt
|
||||||
import co.electriccoin.zcash.ui.screen.account.state.TransactionHistorySyncState
|
import co.electriccoin.zcash.ui.screen.account.state.TransactionHistorySyncState
|
||||||
import co.electriccoin.zcash.ui.screen.account.state.TransactionOverviewExt
|
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
package co.electriccoin.zcash.ui.screen.account.history.fixture
|
||||||
|
|
||||||
|
import cash.z.ecc.android.sdk.fixture.TransactionOverviewFixture
|
||||||
|
import cash.z.ecc.android.sdk.model.Account
|
||||||
|
import cash.z.ecc.android.sdk.model.TransactionRecipient
|
||||||
|
import co.electriccoin.zcash.ui.screen.account.model.HistoryItemExpandableState
|
||||||
|
import co.electriccoin.zcash.ui.screen.account.model.TransactionUi
|
||||||
|
import co.electriccoin.zcash.ui.screen.account.model.TransactionUiState
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
||||||
|
internal object TransactionHistoryUiStateFixture {
|
||||||
|
val TRANSACTIONS =
|
||||||
|
persistentListOf(
|
||||||
|
TransactionUi(
|
||||||
|
TransactionOverviewFixture.new(),
|
||||||
|
TransactionRecipient.Account(Account.DEFAULT),
|
||||||
|
HistoryItemExpandableState.COLLAPSED
|
||||||
|
),
|
||||||
|
TransactionUi(
|
||||||
|
TransactionOverviewFixture.new(),
|
||||||
|
TransactionRecipient.Account(Account(1)),
|
||||||
|
HistoryItemExpandableState.EXPANDED
|
||||||
|
),
|
||||||
|
TransactionUi(
|
||||||
|
TransactionOverviewFixture.new(),
|
||||||
|
null,
|
||||||
|
HistoryItemExpandableState.COLLAPSED
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val STATE = TransactionUiState.Prepared(TRANSACTIONS)
|
||||||
|
|
||||||
|
fun new(
|
||||||
|
transactions: ImmutableList<TransactionUi> = TRANSACTIONS,
|
||||||
|
state: TransactionUiState = STATE
|
||||||
|
) = when (state) {
|
||||||
|
is TransactionUiState.Loading -> state
|
||||||
|
is TransactionUiState.Syncing -> state
|
||||||
|
is TransactionUiState.Prepared -> {
|
||||||
|
state.copy(transactions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,8 @@ import androidx.test.filters.MediumTest
|
||||||
import co.electriccoin.zcash.ui.screen.account.HistoryTag
|
import co.electriccoin.zcash.ui.screen.account.HistoryTag
|
||||||
import co.electriccoin.zcash.ui.screen.account.history.HistoryTestSetup
|
import co.electriccoin.zcash.ui.screen.account.history.HistoryTestSetup
|
||||||
import co.electriccoin.zcash.ui.screen.account.history.fixture.TransactionHistorySyncStateFixture
|
import co.electriccoin.zcash.ui.screen.account.history.fixture.TransactionHistorySyncStateFixture
|
||||||
import co.electriccoin.zcash.ui.screen.account.state.TransactionHistorySyncState
|
import co.electriccoin.zcash.ui.screen.account.history.fixture.TransactionHistoryUiStateFixture
|
||||||
|
import co.electriccoin.zcash.ui.screen.account.model.TransactionUiState
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
|
@ -26,7 +27,7 @@ class HistoryViewTest {
|
||||||
@Test
|
@Test
|
||||||
@MediumTest
|
@MediumTest
|
||||||
fun check_loading_state() {
|
fun check_loading_state() {
|
||||||
newTestSetup(TransactionHistorySyncState.Loading)
|
newTestSetup(TransactionUiState.Loading)
|
||||||
|
|
||||||
composeTestRule.onNodeWithTag(HistoryTag.PROGRESS).also {
|
composeTestRule.onNodeWithTag(HistoryTag.PROGRESS).also {
|
||||||
it.assertExists()
|
it.assertExists()
|
||||||
|
@ -37,9 +38,9 @@ class HistoryViewTest {
|
||||||
@MediumTest
|
@MediumTest
|
||||||
fun check_syncing_state() {
|
fun check_syncing_state() {
|
||||||
newTestSetup(
|
newTestSetup(
|
||||||
TransactionHistorySyncStateFixture.new(
|
TransactionHistoryUiStateFixture.new(
|
||||||
state = TransactionHistorySyncStateFixture.STATE,
|
state = TransactionUiState.Prepared(persistentListOf()),
|
||||||
transactions = TransactionHistorySyncStateFixture.TRANSACTIONS
|
transactions = TransactionHistoryUiStateFixture.TRANSACTIONS
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -60,9 +61,9 @@ class HistoryViewTest {
|
||||||
@MediumTest
|
@MediumTest
|
||||||
fun check_done_state_no_transactions() {
|
fun check_done_state_no_transactions() {
|
||||||
newTestSetup(
|
newTestSetup(
|
||||||
TransactionHistorySyncStateFixture.new(
|
TransactionHistoryUiStateFixture.new(
|
||||||
state = TransactionHistorySyncState.Done(persistentListOf()),
|
state = TransactionUiState.Prepared(persistentListOf()),
|
||||||
transactions = persistentListOf()
|
transactions = TransactionHistoryUiStateFixture.TRANSACTIONS
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
// composeTestRule.onNodeWithText(getStringResource(R.string.history_syncing)).also {
|
// composeTestRule.onNodeWithText(getStringResource(R.string.history_syncing)).also {
|
||||||
|
@ -83,9 +84,9 @@ class HistoryViewTest {
|
||||||
@MediumTest
|
@MediumTest
|
||||||
fun check_done_state_with_transactions() {
|
fun check_done_state_with_transactions() {
|
||||||
newTestSetup(
|
newTestSetup(
|
||||||
TransactionHistorySyncStateFixture.new(
|
TransactionHistoryUiStateFixture.new(
|
||||||
state = TransactionHistorySyncState.Done(persistentListOf()),
|
state = TransactionUiState.Prepared(persistentListOf()),
|
||||||
transactions = TransactionHistorySyncStateFixture.TRANSACTIONS
|
transactions = TransactionHistoryUiStateFixture.TRANSACTIONS
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
// composeTestRule.onNodeWithText(getStringResource(R.string.history_syncing)).also {
|
// composeTestRule.onNodeWithText(getStringResource(R.string.history_syncing)).also {
|
||||||
|
@ -106,7 +107,7 @@ class HistoryViewTest {
|
||||||
@Test
|
@Test
|
||||||
@MediumTest
|
@MediumTest
|
||||||
fun item_click_test() {
|
fun item_click_test() {
|
||||||
val testSetup = newTestSetup(TransactionHistorySyncStateFixture.STATE)
|
val testSetup = newTestSetup(TransactionHistoryUiStateFixture.STATE)
|
||||||
|
|
||||||
assertEquals(0, testSetup.getOnItemClickCount())
|
assertEquals(0, testSetup.getOnItemClickCount())
|
||||||
|
|
||||||
|
@ -124,7 +125,7 @@ class HistoryViewTest {
|
||||||
@Test
|
@Test
|
||||||
@MediumTest
|
@MediumTest
|
||||||
fun transaction_id_click_test() {
|
fun transaction_id_click_test() {
|
||||||
val testSetup = newTestSetup(TransactionHistorySyncStateFixture.STATE)
|
val testSetup = newTestSetup(TransactionHistoryUiStateFixture.STATE)
|
||||||
|
|
||||||
assertEquals(0, testSetup.getOnItemIdClickCount())
|
assertEquals(0, testSetup.getOnItemIdClickCount())
|
||||||
|
|
||||||
|
@ -140,11 +141,11 @@ class HistoryViewTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun newTestSetup(
|
private fun newTestSetup(
|
||||||
transactionHistorySyncState: TransactionHistorySyncState = TransactionHistorySyncStateFixture.new()
|
transactionHistorySyncState: TransactionUiState = TransactionHistoryUiStateFixture.new()
|
||||||
): HistoryTestSetup {
|
): HistoryTestSetup {
|
||||||
return HistoryTestSetup(
|
return HistoryTestSetup(
|
||||||
composeTestRule = composeTestRule,
|
composeTestRule = composeTestRule,
|
||||||
initialHistorySyncState = transactionHistorySyncState
|
initialHistoryUiState = transactionHistorySyncState
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,9 +34,9 @@ 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.account.ext.TransactionOverviewExt
|
||||||
|
import co.electriccoin.zcash.ui.screen.account.ext.getSortHeight
|
||||||
import co.electriccoin.zcash.ui.screen.account.state.TransactionHistorySyncState
|
import co.electriccoin.zcash.ui.screen.account.state.TransactionHistorySyncState
|
||||||
import co.electriccoin.zcash.ui.screen.account.state.TransactionOverviewExt
|
|
||||||
import co.electriccoin.zcash.ui.screen.account.state.getSortHeight
|
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
|
|
@ -13,6 +13,8 @@ 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.design.component.CircularScreenProgressIndicator
|
||||||
import co.electriccoin.zcash.ui.screen.account.view.Account
|
import co.electriccoin.zcash.ui.screen.account.view.Account
|
||||||
|
import co.electriccoin.zcash.ui.screen.account.view.TransactionItemAction
|
||||||
|
import co.electriccoin.zcash.ui.screen.account.viewmodel.TransactionHistoryViewModel
|
||||||
import co.electriccoin.zcash.ui.screen.settings.viewmodel.SettingsViewModel
|
import co.electriccoin.zcash.ui.screen.settings.viewmodel.SettingsViewModel
|
||||||
import kotlinx.coroutines.flow.toList
|
import kotlinx.coroutines.flow.toList
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -26,17 +28,24 @@ internal fun WrapAccount(
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
val walletViewModel by activity.viewModels<WalletViewModel>()
|
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||||
val walletSnapshot = walletViewModel.walletSnapshot.collectAsStateWithLifecycle().value
|
|
||||||
|
val transactionHistoryViewModel by activity.viewModels<TransactionHistoryViewModel>()
|
||||||
|
|
||||||
val settingsViewModel by activity.viewModels<SettingsViewModel>()
|
val settingsViewModel by activity.viewModels<SettingsViewModel>()
|
||||||
|
|
||||||
|
val walletSnapshot = walletViewModel.walletSnapshot.collectAsStateWithLifecycle().value
|
||||||
|
|
||||||
val isKeepScreenOnWhileSyncing = settingsViewModel.isKeepScreenOnWhileSyncing.collectAsStateWithLifecycle().value
|
val isKeepScreenOnWhileSyncing = settingsViewModel.isKeepScreenOnWhileSyncing.collectAsStateWithLifecycle().value
|
||||||
|
|
||||||
val synchronizer = walletViewModel.synchronizer.collectAsStateWithLifecycle().value
|
val synchronizer = walletViewModel.synchronizer.collectAsStateWithLifecycle().value
|
||||||
|
|
||||||
val transactionHistoryState = walletViewModel.transactionHistoryState.collectAsStateWithLifecycle().value
|
val transactionHistoryState = walletViewModel.transactionHistoryState.collectAsStateWithLifecycle().value
|
||||||
|
|
||||||
Twig.info { "Current transaction history state: $transactionHistoryState" }
|
val transactionsUiState = transactionHistoryViewModel.transactionUiState.collectAsStateWithLifecycle().value
|
||||||
|
|
||||||
|
Twig.info { "Current transaction history state: $transactionsUiState" }
|
||||||
|
|
||||||
|
transactionHistoryViewModel.processTransactionState(transactionHistoryState)
|
||||||
|
|
||||||
if (null == walletSnapshot) {
|
if (null == walletSnapshot) {
|
||||||
// TODO [#1146]: Consider moving CircularScreenProgressIndicator from Android layer to View layer
|
// TODO [#1146]: Consider moving CircularScreenProgressIndicator from Android layer to View layer
|
||||||
|
@ -47,30 +56,37 @@ internal fun WrapAccount(
|
||||||
Account(
|
Account(
|
||||||
walletSnapshot = walletSnapshot,
|
walletSnapshot = walletSnapshot,
|
||||||
isKeepScreenOnWhileSyncing = isKeepScreenOnWhileSyncing,
|
isKeepScreenOnWhileSyncing = isKeepScreenOnWhileSyncing,
|
||||||
transactionState = transactionHistoryState,
|
transactionsUiState = transactionsUiState,
|
||||||
onItemClick = { tx ->
|
onTransactionItemAction = { action ->
|
||||||
Twig.debug { "Transaction item clicked - querying memos..." }
|
when (action) {
|
||||||
val memos = synchronizer?.getMemos(tx.overview)
|
is TransactionItemAction.IdClick -> {
|
||||||
scope.launch {
|
Twig.info { "Transaction ID clicked: ${action.id}" }
|
||||||
memos?.toList()?.let {
|
|
||||||
val merged = it.joinToString().ifEmpty { "-" }
|
|
||||||
Twig.info { "Transaction memos: count: ${it.size}, contains: $merged" }
|
|
||||||
ClipboardManagerUtil.copyToClipboard(
|
ClipboardManagerUtil.copyToClipboard(
|
||||||
activity.applicationContext,
|
activity.applicationContext,
|
||||||
activity.getString(R.string.account_history_item_clipboard_tag),
|
activity.getString(R.string.account_history_id_clipboard_tag),
|
||||||
merged
|
action.id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
is TransactionItemAction.MemoClick -> {
|
||||||
|
Twig.info { "Transaction item clicked - querying memos..." }
|
||||||
|
val memos = synchronizer?.getMemos(action.overview)
|
||||||
|
scope.launch {
|
||||||
|
memos?.toList()?.let {
|
||||||
|
val merged = it.joinToString().ifEmpty { "-" }
|
||||||
|
Twig.info { "Transaction memos: count: ${it.size}, contains: $merged" }
|
||||||
|
ClipboardManagerUtil.copyToClipboard(
|
||||||
|
activity.applicationContext,
|
||||||
|
activity.getString(R.string.account_history_item_clipboard_tag),
|
||||||
|
merged
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is TransactionItemAction.ExpandableStateChange -> {
|
||||||
|
transactionHistoryViewModel.updateTransactionItemState(action.txId, action.newState)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onTransactionIdClick = { txId ->
|
|
||||||
Twig.debug { "Transaction ID clicked: $txId" }
|
|
||||||
ClipboardManagerUtil.copyToClipboard(
|
|
||||||
activity.applicationContext,
|
|
||||||
activity.getString(R.string.account_history_id_clipboard_tag),
|
|
||||||
txId
|
|
||||||
)
|
|
||||||
},
|
|
||||||
goBalances = goBalances,
|
goBalances = goBalances,
|
||||||
goSettings = goSettings,
|
goSettings = goSettings,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package co.electriccoin.zcash.ui.screen.account.state
|
package co.electriccoin.zcash.ui.screen.account.ext
|
||||||
|
|
||||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||||
import cash.z.ecc.android.sdk.model.TransactionOverview
|
import cash.z.ecc.android.sdk.model.TransactionOverview
|
|
@ -0,0 +1,8 @@
|
||||||
|
package co.electriccoin.zcash.ui.screen.account.model
|
||||||
|
|
||||||
|
enum class HistoryItemExpandableState {
|
||||||
|
COLLAPSED,
|
||||||
|
EXPANDED,
|
||||||
|
EXPANDED_ADDRESS,
|
||||||
|
EXPANDED_ID
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package co.electriccoin.zcash.ui.screen.account.model
|
||||||
|
|
||||||
|
import cash.z.ecc.android.sdk.model.TransactionOverview
|
||||||
|
import cash.z.ecc.android.sdk.model.TransactionRecipient
|
||||||
|
import co.electriccoin.zcash.ui.screen.account.ext.TransactionOverviewExt
|
||||||
|
|
||||||
|
data class TransactionUi(
|
||||||
|
val overview: TransactionOverview,
|
||||||
|
val recipient: TransactionRecipient?,
|
||||||
|
val expandableState: HistoryItemExpandableState
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun new(
|
||||||
|
data: TransactionOverviewExt,
|
||||||
|
expandableState: HistoryItemExpandableState
|
||||||
|
) = TransactionUi(
|
||||||
|
overview = data.overview,
|
||||||
|
recipient = data.recipient,
|
||||||
|
expandableState = expandableState
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package co.electriccoin.zcash.ui.screen.account.model
|
||||||
|
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
|
sealed interface TransactionUiState {
|
||||||
|
data object Loading : TransactionUiState
|
||||||
|
|
||||||
|
data object Syncing : TransactionUiState
|
||||||
|
|
||||||
|
data class Prepared(val transactions: ImmutableList<TransactionUi>) : TransactionUiState
|
||||||
|
}
|
|
@ -1,21 +1,14 @@
|
||||||
package co.electriccoin.zcash.ui.screen.account.state
|
package co.electriccoin.zcash.ui.screen.account.state
|
||||||
|
|
||||||
|
import co.electriccoin.zcash.ui.screen.account.ext.TransactionOverviewExt
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
sealed class TransactionHistorySyncState {
|
sealed interface TransactionHistorySyncState {
|
||||||
object Loading : TransactionHistorySyncState() {
|
data object Loading : TransactionHistorySyncState
|
||||||
override fun toString() = "Loading" // NON-NLS
|
|
||||||
}
|
|
||||||
|
|
||||||
data class Syncing(val transactions: ImmutableList<TransactionOverviewExt>) : TransactionHistorySyncState() {
|
sealed class Prepared(open val transactions: ImmutableList<TransactionOverviewExt>) : TransactionHistorySyncState
|
||||||
fun hasNoTransactions(): Boolean {
|
|
||||||
return transactions.isEmpty()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class Done(val transactions: ImmutableList<TransactionOverviewExt>) : TransactionHistorySyncState() {
|
data class Syncing(override val transactions: ImmutableList<TransactionOverviewExt>) : Prepared(transactions)
|
||||||
fun hasNoTransactions(): Boolean {
|
|
||||||
return transactions.isEmpty()
|
data class Done(override val transactions: ImmutableList<TransactionOverviewExt>) : Prepared(transactions)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,9 @@ import co.electriccoin.zcash.ui.design.component.SmallTopAppBar
|
||||||
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.account.AccountTag
|
import co.electriccoin.zcash.ui.screen.account.AccountTag
|
||||||
import co.electriccoin.zcash.ui.screen.account.state.TransactionHistorySyncState
|
import co.electriccoin.zcash.ui.screen.account.model.HistoryItemExpandableState
|
||||||
import co.electriccoin.zcash.ui.screen.account.state.TransactionOverviewExt
|
import co.electriccoin.zcash.ui.screen.account.model.TransactionUi
|
||||||
|
import co.electriccoin.zcash.ui.screen.account.model.TransactionUiState
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
||||||
@Preview("Account No History")
|
@Preview("Account No History")
|
||||||
|
@ -42,9 +43,8 @@ private fun HistoryLoadingComposablePreview() {
|
||||||
isKeepScreenOnWhileSyncing = false,
|
isKeepScreenOnWhileSyncing = false,
|
||||||
goBalances = {},
|
goBalances = {},
|
||||||
goSettings = {},
|
goSettings = {},
|
||||||
transactionState = TransactionHistorySyncState.Loading,
|
transactionsUiState = TransactionUiState.Loading,
|
||||||
onItemClick = {},
|
onTransactionItemAction = {}
|
||||||
onTransactionIdClick = {}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,31 +55,34 @@ private fun HistoryLoadingComposablePreview() {
|
||||||
private fun HistoryListComposablePreview() {
|
private fun HistoryListComposablePreview() {
|
||||||
ZcashTheme(forceDarkMode = false) {
|
ZcashTheme(forceDarkMode = false) {
|
||||||
GradientSurface {
|
GradientSurface {
|
||||||
|
@Suppress("MagicNumber")
|
||||||
Account(
|
Account(
|
||||||
walletSnapshot = WalletSnapshotFixture.new(),
|
walletSnapshot = WalletSnapshotFixture.new(),
|
||||||
isKeepScreenOnWhileSyncing = false,
|
isKeepScreenOnWhileSyncing = false,
|
||||||
goBalances = {},
|
goBalances = {},
|
||||||
goSettings = {},
|
goSettings = {},
|
||||||
transactionState =
|
transactionsUiState =
|
||||||
TransactionHistorySyncState.Syncing(
|
TransactionUiState.Prepared(
|
||||||
@Suppress("MagicNumber")
|
transactions =
|
||||||
persistentListOf(
|
persistentListOf(
|
||||||
TransactionOverviewExt(
|
TransactionUi(
|
||||||
TransactionOverviewFixture.new(netValue = Zatoshi(100000000)),
|
TransactionOverviewFixture.new(netValue = Zatoshi(100000000)),
|
||||||
null
|
null,
|
||||||
),
|
HistoryItemExpandableState.EXPANDED
|
||||||
TransactionOverviewExt(
|
),
|
||||||
TransactionOverviewFixture.new(netValue = Zatoshi(200000000)),
|
TransactionUi(
|
||||||
null
|
TransactionOverviewFixture.new(netValue = Zatoshi(200000000)),
|
||||||
),
|
null,
|
||||||
TransactionOverviewExt(
|
HistoryItemExpandableState.COLLAPSED
|
||||||
TransactionOverviewFixture.new(netValue = Zatoshi(300000000)),
|
),
|
||||||
null
|
TransactionUi(
|
||||||
),
|
TransactionOverviewFixture.new(netValue = Zatoshi(300000000)),
|
||||||
)
|
null,
|
||||||
|
HistoryItemExpandableState.COLLAPSED
|
||||||
|
),
|
||||||
|
)
|
||||||
),
|
),
|
||||||
onItemClick = {},
|
onTransactionItemAction = {},
|
||||||
onTransactionIdClick = {}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,13 +90,12 @@ private fun HistoryListComposablePreview() {
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
fun Account(
|
internal fun Account(
|
||||||
goBalances: () -> Unit,
|
goBalances: () -> Unit,
|
||||||
goSettings: () -> Unit,
|
goSettings: () -> Unit,
|
||||||
isKeepScreenOnWhileSyncing: Boolean?,
|
isKeepScreenOnWhileSyncing: Boolean?,
|
||||||
onItemClick: (TransactionOverviewExt) -> Unit,
|
onTransactionItemAction: (TransactionItemAction) -> Unit,
|
||||||
onTransactionIdClick: (String) -> Unit,
|
transactionsUiState: TransactionUiState,
|
||||||
transactionState: TransactionHistorySyncState,
|
|
||||||
walletSnapshot: WalletSnapshot,
|
walletSnapshot: WalletSnapshot,
|
||||||
) {
|
) {
|
||||||
Scaffold(topBar = {
|
Scaffold(topBar = {
|
||||||
|
@ -103,9 +105,8 @@ fun Account(
|
||||||
walletSnapshot = walletSnapshot,
|
walletSnapshot = walletSnapshot,
|
||||||
isKeepScreenOnWhileSyncing = isKeepScreenOnWhileSyncing,
|
isKeepScreenOnWhileSyncing = isKeepScreenOnWhileSyncing,
|
||||||
goBalances = goBalances,
|
goBalances = goBalances,
|
||||||
transactionState = transactionState,
|
transactionState = transactionsUiState,
|
||||||
onItemClick = onItemClick,
|
onTransactionItemAction = onTransactionItemAction,
|
||||||
onTransactionIdClick = onTransactionIdClick,
|
|
||||||
modifier =
|
modifier =
|
||||||
Modifier.padding(
|
Modifier.padding(
|
||||||
top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault,
|
top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault,
|
||||||
|
@ -140,9 +141,8 @@ private fun AccountMainContent(
|
||||||
walletSnapshot: WalletSnapshot,
|
walletSnapshot: WalletSnapshot,
|
||||||
isKeepScreenOnWhileSyncing: Boolean?,
|
isKeepScreenOnWhileSyncing: Boolean?,
|
||||||
goBalances: () -> Unit,
|
goBalances: () -> Unit,
|
||||||
onItemClick: (TransactionOverviewExt) -> Unit,
|
onTransactionItemAction: (TransactionItemAction) -> Unit,
|
||||||
onTransactionIdClick: (String) -> Unit,
|
transactionState: TransactionUiState,
|
||||||
transactionState: TransactionHistorySyncState,
|
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
|
@ -163,8 +163,7 @@ private fun AccountMainContent(
|
||||||
|
|
||||||
HistoryContainer(
|
HistoryContainer(
|
||||||
transactionState = transactionState,
|
transactionState = transactionState,
|
||||||
onItemClick = onItemClick,
|
onTransactionItemAction = onTransactionItemAction,
|
||||||
onTransactionIdClick = onTransactionIdClick,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (isKeepScreenOnWhileSyncing == true && walletSnapshot.status == Synchronizer.Status.SYNCING) {
|
if (isKeepScreenOnWhileSyncing == true && walletSnapshot.status == Synchronizer.Status.SYNCING) {
|
||||||
|
|
|
@ -14,19 +14,13 @@ 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
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
|
||||||
import androidx.compose.material3.Divider
|
import androidx.compose.material3.Divider
|
||||||
import androidx.compose.material3.DividerDefaults
|
import androidx.compose.material3.DividerDefaults
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Alignment.Companion.Center
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
@ -38,20 +32,22 @@ import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import cash.z.ecc.android.sdk.fixture.TransactionOverviewFixture
|
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.TransactionOverview
|
||||||
import cash.z.ecc.android.sdk.model.TransactionRecipient
|
import cash.z.ecc.android.sdk.model.TransactionRecipient
|
||||||
import cash.z.ecc.android.sdk.model.TransactionState
|
import cash.z.ecc.android.sdk.model.TransactionState
|
||||||
import cash.z.ecc.android.sdk.model.Zatoshi
|
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||||
import cash.z.ecc.android.sdk.model.toZecString
|
import cash.z.ecc.android.sdk.model.toZecString
|
||||||
import co.electriccoin.zcash.ui.R
|
import co.electriccoin.zcash.ui.R
|
||||||
import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator
|
import co.electriccoin.zcash.ui.design.component.CircularMidProgressIndicator
|
||||||
import co.electriccoin.zcash.ui.design.component.GradientSurface
|
import co.electriccoin.zcash.ui.design.component.GradientSurface
|
||||||
import co.electriccoin.zcash.ui.design.component.StyledBalance
|
import co.electriccoin.zcash.ui.design.component.StyledBalance
|
||||||
import co.electriccoin.zcash.ui.design.component.Tiny
|
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.account.HistoryTag
|
import co.electriccoin.zcash.ui.screen.account.HistoryTag
|
||||||
import co.electriccoin.zcash.ui.screen.account.state.TransactionHistorySyncState
|
import co.electriccoin.zcash.ui.screen.account.model.HistoryItemExpandableState
|
||||||
import co.electriccoin.zcash.ui.screen.account.state.TransactionOverviewExt
|
import co.electriccoin.zcash.ui.screen.account.model.TransactionUi
|
||||||
|
import co.electriccoin.zcash.ui.screen.account.model.TransactionUiState
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
|
@ -64,9 +60,8 @@ private fun ComposablePreview() {
|
||||||
ZcashTheme(forceDarkMode = false) {
|
ZcashTheme(forceDarkMode = false) {
|
||||||
GradientSurface {
|
GradientSurface {
|
||||||
HistoryContainer(
|
HistoryContainer(
|
||||||
transactionState = TransactionHistorySyncState.Loading,
|
transactionState = TransactionUiState.Loading,
|
||||||
onItemClick = {},
|
onTransactionItemAction = {}
|
||||||
onTransactionIdClick = {}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,27 +72,30 @@ private fun ComposablePreview() {
|
||||||
private fun ComposableHistoryListPreview() {
|
private fun ComposableHistoryListPreview() {
|
||||||
ZcashTheme(forceDarkMode = false) {
|
ZcashTheme(forceDarkMode = false) {
|
||||||
GradientSurface {
|
GradientSurface {
|
||||||
|
@Suppress("MagicNumber")
|
||||||
HistoryContainer(
|
HistoryContainer(
|
||||||
transactionState =
|
transactionState =
|
||||||
TransactionHistorySyncState.Syncing(
|
TransactionUiState.Prepared(
|
||||||
@Suppress("MagicNumber")
|
transactions =
|
||||||
persistentListOf(
|
persistentListOf(
|
||||||
TransactionOverviewExt(
|
TransactionUi(
|
||||||
TransactionOverviewFixture.new(netValue = Zatoshi(100000000)),
|
TransactionOverviewFixture.new(netValue = Zatoshi(100000000)),
|
||||||
null
|
null,
|
||||||
),
|
HistoryItemExpandableState.EXPANDED
|
||||||
TransactionOverviewExt(
|
),
|
||||||
TransactionOverviewFixture.new(netValue = Zatoshi(200000000)),
|
TransactionUi(
|
||||||
null
|
TransactionOverviewFixture.new(netValue = Zatoshi(200000000)),
|
||||||
),
|
null,
|
||||||
TransactionOverviewExt(
|
HistoryItemExpandableState.COLLAPSED
|
||||||
TransactionOverviewFixture.new(netValue = Zatoshi(300000000)),
|
),
|
||||||
null
|
TransactionUi(
|
||||||
),
|
TransactionOverviewFixture.new(netValue = Zatoshi(300000000)),
|
||||||
)
|
null,
|
||||||
|
HistoryItemExpandableState.COLLAPSED
|
||||||
|
),
|
||||||
|
)
|
||||||
),
|
),
|
||||||
onItemClick = {},
|
onTransactionItemAction = {}
|
||||||
onTransactionIdClick = {}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,11 +112,9 @@ val dateFormat: DateFormat by lazy {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Suppress("LongMethod")
|
internal fun HistoryContainer(
|
||||||
fun HistoryContainer(
|
transactionState: TransactionUiState,
|
||||||
transactionState: TransactionHistorySyncState,
|
onTransactionItemAction: (TransactionItemAction) -> Unit,
|
||||||
onItemClick: (TransactionOverviewExt) -> Unit,
|
|
||||||
onTransactionIdClick: (String) -> Unit,
|
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
|
@ -131,27 +127,37 @@ fun HistoryContainer(
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
when (transactionState) {
|
when (transactionState) {
|
||||||
is TransactionHistorySyncState.Loading -> {
|
TransactionUiState.Loading, TransactionUiState.Syncing -> {
|
||||||
CircularScreenProgressIndicator(
|
Column(
|
||||||
modifier =
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
Modifier
|
modifier = Modifier.fillMaxWidth()
|
||||||
.align(alignment = Center)
|
) {
|
||||||
.testTag(HistoryTag.PROGRESS)
|
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingUpLarge))
|
||||||
)
|
CircularMidProgressIndicator(
|
||||||
|
modifier = Modifier.testTag(HistoryTag.PROGRESS),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
is TransactionHistorySyncState.Syncing -> {
|
is TransactionUiState.Prepared -> {
|
||||||
HistoryList(
|
if (transactionState.transactions.isEmpty()) {
|
||||||
transactions = transactionState.transactions,
|
Column {
|
||||||
onItemClick = onItemClick,
|
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingUpLarge))
|
||||||
onTransactionIdClick = onTransactionIdClick
|
Text(
|
||||||
)
|
modifier = Modifier.fillMaxWidth(),
|
||||||
}
|
textAlign = TextAlign.Center,
|
||||||
is TransactionHistorySyncState.Done -> {
|
text = stringResource(id = R.string.account_history_empty),
|
||||||
HistoryList(
|
style = ZcashTheme.extendedTypography.transactionItemStyles.titleRegular,
|
||||||
transactions = transactionState.transactions,
|
color = ZcashTheme.colors.textCommon,
|
||||||
onItemClick = onItemClick,
|
maxLines = 1,
|
||||||
onTransactionIdClick = onTransactionIdClick
|
overflow = TextOverflow.Ellipsis,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
HistoryList(
|
||||||
|
transactions = transactionState.transactions,
|
||||||
|
onAction = onTransactionItemAction,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,60 +165,53 @@ fun HistoryContainer(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun HistoryList(
|
private fun HistoryList(
|
||||||
transactions: ImmutableList<TransactionOverviewExt>,
|
transactions: ImmutableList<TransactionUi>,
|
||||||
onItemClick: (TransactionOverviewExt) -> Unit,
|
onAction: (TransactionItemAction) -> Unit
|
||||||
onTransactionIdClick: (String) -> Unit
|
|
||||||
) {
|
) {
|
||||||
if (transactions.isEmpty()) {
|
LazyColumn(
|
||||||
Column {
|
modifier = Modifier.testTag(HistoryTag.TRANSACTION_LIST)
|
||||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingUpLarge))
|
) {
|
||||||
|
items(transactions.size) { index ->
|
||||||
Text(
|
HistoryItem(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
transaction = transactions[index],
|
||||||
textAlign = TextAlign.Center,
|
onAction = onAction
|
||||||
text = stringResource(id = R.string.account_history_empty),
|
|
||||||
style = ZcashTheme.extendedTypography.transactionItemStyles.titleRegular,
|
|
||||||
color = ZcashTheme.colors.textCommon,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LazyColumn(
|
|
||||||
modifier = Modifier.testTag(HistoryTag.TRANSACTION_LIST)
|
|
||||||
) {
|
|
||||||
itemsIndexed(transactions) { _, item ->
|
|
||||||
HistoryItem(
|
|
||||||
transaction = item,
|
|
||||||
onItemClick = onItemClick,
|
|
||||||
onIdClick = onTransactionIdClick,
|
|
||||||
)
|
|
||||||
|
|
||||||
Divider(
|
Divider(
|
||||||
color = ZcashTheme.colors.dividerColor,
|
color = ZcashTheme.colors.dividerColor,
|
||||||
thickness = DividerDefaults.Thickness,
|
thickness = DividerDefaults.Thickness,
|
||||||
modifier = Modifier.padding(horizontal = ZcashTheme.dimens.spacingDefault)
|
modifier = Modifier.padding(horizontal = ZcashTheme.dimens.spacingDefault)
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum class ItemExpandedState {
|
@Composable
|
||||||
COLLAPSED,
|
@Preview("History List Item")
|
||||||
EXPANDED,
|
private fun ComposableHistoryListItemPreview() {
|
||||||
EXPANDED_ADDRESS,
|
ZcashTheme(forceDarkMode = false) {
|
||||||
EXPANDED_ID
|
GradientSurface {
|
||||||
|
@Suppress("MagicNumber")
|
||||||
|
HistoryItem(
|
||||||
|
onAction = {},
|
||||||
|
transaction =
|
||||||
|
TransactionUi(
|
||||||
|
TransactionOverviewFixture.new(netValue = Zatoshi(100000000)),
|
||||||
|
recipient = null,
|
||||||
|
expandableState = HistoryItemExpandableState.EXPANDED
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const val ADDRESS_IN_TITLE_WIDTH_RATIO = 0.5f
|
const val ADDRESS_IN_TITLE_WIDTH_RATIO = 0.5f
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||||
fun HistoryItem(
|
private fun HistoryItem(
|
||||||
transaction: TransactionOverviewExt,
|
transaction: TransactionUi,
|
||||||
onItemClick: (TransactionOverviewExt) -> Unit,
|
onAction: (TransactionItemAction) -> Unit,
|
||||||
onIdClick: (String) -> Unit,
|
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val typeText: String
|
val typeText: String
|
||||||
|
@ -259,10 +258,6 @@ fun HistoryItem(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var expandedState: ItemExpandedState by rememberSaveable {
|
|
||||||
mutableStateOf(ItemExpandedState.COLLAPSED)
|
|
||||||
}
|
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier =
|
modifier =
|
||||||
modifier
|
modifier
|
||||||
|
@ -270,10 +265,14 @@ fun HistoryItem(
|
||||||
Modifier
|
Modifier
|
||||||
.background(color = ZcashTheme.colors.historyBackgroundColor)
|
.background(color = ZcashTheme.colors.historyBackgroundColor)
|
||||||
.clickable {
|
.clickable {
|
||||||
if (expandedState == ItemExpandedState.COLLAPSED) {
|
if (transaction.expandableState <= HistoryItemExpandableState.COLLAPSED) {
|
||||||
expandedState = ItemExpandedState.EXPANDED
|
onAction(
|
||||||
|
TransactionItemAction.ExpandableStateChange(
|
||||||
|
transaction.overview.rawId,
|
||||||
|
HistoryItemExpandableState.EXPANDED
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
onItemClick(transaction)
|
|
||||||
}
|
}
|
||||||
.padding(all = ZcashTheme.dimens.spacingLarge)
|
.padding(all = ZcashTheme.dimens.spacingLarge)
|
||||||
.animateContentSize()
|
.animateContentSize()
|
||||||
|
@ -363,7 +362,7 @@ fun HistoryItem(
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (expandedState >= ItemExpandedState.EXPANDED) {
|
if (transaction.expandableState == HistoryItemExpandableState.EXPANDED) {
|
||||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))
|
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))
|
||||||
|
|
||||||
val txId = transaction.overview.txIdString()
|
val txId = transaction.overview.txIdString()
|
||||||
|
@ -371,15 +370,54 @@ fun HistoryItem(
|
||||||
text = txId,
|
text = txId,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.clickable { onIdClick(txId) }
|
.clickable { onAction(TransactionItemAction.IdClick(txId)) }
|
||||||
.testTag(HistoryTag.TRANSACTION_ID)
|
.testTag(HistoryTag.TRANSACTION_ID)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = (Modifier.height(ZcashTheme.dimens.spacingDefault)))
|
||||||
|
|
||||||
|
// TODO [#1162]: Will be reworked
|
||||||
|
// TODO [#1162]: Expandable transaction history item
|
||||||
|
// TODO [#1162]: https://github.com/Electric-Coin-Company/zashi-android/issues/1162
|
||||||
|
Tiny(
|
||||||
|
text = "Tap to copy message",
|
||||||
|
modifier = Modifier.clickable { onAction(TransactionItemAction.MemoClick(transaction.overview)) }
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = (Modifier.height(ZcashTheme.dimens.spacingDefault)))
|
||||||
|
|
||||||
|
Tiny(
|
||||||
|
text = stringResource(id = R.string.account_history_item_collapse_transaction),
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.clickable {
|
||||||
|
if (transaction.expandableState >= HistoryItemExpandableState.EXPANDED) {
|
||||||
|
onAction(
|
||||||
|
TransactionItemAction.ExpandableStateChange(
|
||||||
|
transaction.overview.rawId,
|
||||||
|
HistoryItemExpandableState.COLLAPSED
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class TransactionExtendedState {
|
internal sealed class TransactionItemAction {
|
||||||
|
data class IdClick(val id: String) : TransactionItemAction()
|
||||||
|
|
||||||
|
data class ExpandableStateChange(
|
||||||
|
val txId: FirstClassByteArray,
|
||||||
|
val newState: HistoryItemExpandableState
|
||||||
|
) : TransactionItemAction()
|
||||||
|
|
||||||
|
data class MemoClick(val overview: TransactionOverview) : TransactionItemAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal enum class TransactionExtendedState {
|
||||||
SENT,
|
SENT,
|
||||||
SENDING,
|
SENDING,
|
||||||
SEND_FAILED,
|
SEND_FAILED,
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
package co.electriccoin.zcash.ui.screen.account.viewmodel
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.lifecycle.AndroidViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import cash.z.ecc.android.sdk.model.FirstClassByteArray
|
||||||
|
import co.electriccoin.zcash.ui.common.ANDROID_STATE_FLOW_TIMEOUT
|
||||||
|
import co.electriccoin.zcash.ui.screen.account.model.HistoryItemExpandableState
|
||||||
|
import co.electriccoin.zcash.ui.screen.account.model.TransactionUi
|
||||||
|
import co.electriccoin.zcash.ui.screen.account.model.TransactionUiState
|
||||||
|
import co.electriccoin.zcash.ui.screen.account.state.TransactionHistorySyncState
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.WhileSubscribed
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
|
||||||
|
class TransactionHistoryViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
|
private val transactions: MutableStateFlow<ImmutableList<TransactionUi>> = MutableStateFlow(persistentListOf())
|
||||||
|
|
||||||
|
val transactionUiState: StateFlow<TransactionUiState> =
|
||||||
|
transactions.map {
|
||||||
|
if (it.isEmpty()) {
|
||||||
|
TransactionUiState.Syncing
|
||||||
|
} else {
|
||||||
|
TransactionUiState.Prepared(it)
|
||||||
|
}
|
||||||
|
}.stateIn(
|
||||||
|
viewModelScope,
|
||||||
|
SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
|
||||||
|
TransactionUiState.Loading
|
||||||
|
)
|
||||||
|
|
||||||
|
fun processTransactionState(dataState: TransactionHistorySyncState) {
|
||||||
|
transactions.value =
|
||||||
|
when (dataState) {
|
||||||
|
TransactionHistorySyncState.Loading -> persistentListOf()
|
||||||
|
is TransactionHistorySyncState.Prepared -> {
|
||||||
|
dataState.transactions.map { data ->
|
||||||
|
TransactionUi.new(
|
||||||
|
data = data,
|
||||||
|
expandableState =
|
||||||
|
transactions.value.find {
|
||||||
|
data.overview.rawId == it.overview.rawId
|
||||||
|
}?.expandableState ?: HistoryItemExpandableState.COLLAPSED
|
||||||
|
)
|
||||||
|
}.toPersistentList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateTransactionItemState(
|
||||||
|
txId: FirstClassByteArray,
|
||||||
|
newState: HistoryItemExpandableState
|
||||||
|
) {
|
||||||
|
transactions.value =
|
||||||
|
transactions.value.map { item ->
|
||||||
|
if (item.overview.rawId == txId) {
|
||||||
|
item.copy(expandableState = newState)
|
||||||
|
} else {
|
||||||
|
item
|
||||||
|
}
|
||||||
|
}.toPersistentList()
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@
|
||||||
<string name="account_history_item_shielded">Shielded transaction</string>
|
<string name="account_history_item_shielded">Shielded transaction</string>
|
||||||
<string name="account_history_item_sent_prefix">-</string>
|
<string name="account_history_item_sent_prefix">-</string>
|
||||||
<string name="account_history_item_received_prefix">+</string>
|
<string name="account_history_item_received_prefix">+</string>
|
||||||
|
<string name="account_history_item_collapse_transaction">Collapse transaction</string>
|
||||||
|
|
||||||
<string name="account_history_item_clipboard_tag">Transaction memo</string>
|
<string name="account_history_item_clipboard_tag">Transaction memo</string>
|
||||||
<string name="account_history_id_clipboard_tag">Transaction ID</string>
|
<string name="account_history_id_clipboard_tag">Transaction ID</string>
|
||||||
|
|
Loading…
Reference in New Issue