[#1160] Trx History UI incorporated into Account

- Closes #1160
- Changelog update
This commit is contained in:
Honza Rychnovský 2024-03-05 08:52:47 +01:00 committed by GitHub
parent e000745885
commit 9a929c1109
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 237 additions and 374 deletions

View File

@ -16,6 +16,9 @@ directly impact users rather than highlighting other key architectural updates.*
lightwalletd servers in runtime. lightwalletd servers in runtime.
- The About screen now contains a link to the new Zashi Privacy Policy website - The About screen now contains a link to the new Zashi Privacy Policy website
### Changed
- The Transaction History UI has been incorporated into the Account screen
### Fixed ### Fixed
- Button sizing has been updated to align with the design guidelines and preserve stretching if necessary - Button sizing has been updated to align with the design guidelines and preserve stretching if necessary

View File

@ -54,7 +54,7 @@ fun FormTextField(
unfocusedContainerColor = Color.Transparent, unfocusedContainerColor = Color.Transparent,
disabledContainerColor = ZcashTheme.colors.textDisabled, disabledContainerColor = ZcashTheme.colors.textDisabled,
errorContainerColor = Color.Transparent, errorContainerColor = Color.Transparent,
focusedIndicatorColor = Color.Green, focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent, unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent disabledIndicatorColor = Color.Transparent
), ),

View File

@ -45,6 +45,7 @@ data class ExtendedColors(
val panelBackgroundColor: Color, val panelBackgroundColor: Color,
val radioButtonColor: Color, val radioButtonColor: Color,
val radioButtonTextColor: Color, val radioButtonTextColor: Color,
val historyBackgroundColor: Color,
) { ) {
@Composable @Composable
fun surfaceGradient() = fun surfaceGradient() =

View File

@ -79,6 +79,8 @@ internal object Dark {
val disabledButtonTextColor = Color(0xFFDDDDDD) val disabledButtonTextColor = Color(0xFFDDDDDD)
val buttonShadowColor = Color(0xFFFFFFFF) val buttonShadowColor = Color(0xFFFFFFFF)
val historyBackgroundColor = Color(0xFFF6F6F6)
} }
internal object Light { internal object Light {
@ -145,6 +147,8 @@ internal object Light {
val disabledButtonColor = Color(0xFFB7B7B7) val disabledButtonColor = Color(0xFFB7B7B7)
val disabledButtonTextColor = Color(0xFFDDDDDD) val disabledButtonTextColor = Color(0xFFDDDDDD)
val buttonShadowColor = Color(0xFF000000) val buttonShadowColor = Color(0xFF000000)
val historyBackgroundColor = Color(0xFFF6F6F6)
} }
internal val DarkColorPalette = internal val DarkColorPalette =
@ -210,6 +214,7 @@ internal val DarkExtendedColorPalette =
panelBackgroundColor = Dark.panelBackgroundColor, panelBackgroundColor = Dark.panelBackgroundColor,
radioButtonColor = Dark.radioButtonColor, radioButtonColor = Dark.radioButtonColor,
radioButtonTextColor = Dark.radioButtonTextColor, radioButtonTextColor = Dark.radioButtonTextColor,
historyBackgroundColor = Dark.historyBackgroundColor,
) )
internal val LightExtendedColorPalette = internal val LightExtendedColorPalette =
@ -251,6 +256,7 @@ internal val LightExtendedColorPalette =
panelBackgroundColor = Light.panelBackgroundColor, panelBackgroundColor = Light.panelBackgroundColor,
radioButtonColor = Dark.radioButtonColor, radioButtonColor = Dark.radioButtonColor,
radioButtonTextColor = Dark.radioButtonTextColor, radioButtonTextColor = Dark.radioButtonTextColor,
historyBackgroundColor = Dark.historyBackgroundColor,
) )
@Suppress("CompositionLocalAllowlist") @Suppress("CompositionLocalAllowlist")
@ -294,5 +300,6 @@ internal val LocalExtendedColors =
panelBackgroundColor = Color.Unspecified, panelBackgroundColor = Color.Unspecified,
radioButtonColor = Color.Unspecified, radioButtonColor = Color.Unspecified,
radioButtonTextColor = Color.Unspecified, radioButtonTextColor = Color.Unspecified,
historyBackgroundColor = Color.Unspecified,
) )
} }

View File

@ -36,7 +36,6 @@ android {
"src/main/res/ui/balances", "src/main/res/ui/balances",
"src/main/res/ui/common", "src/main/res/ui/common",
"src/main/res/ui/export_data", "src/main/res/ui/export_data",
"src/main/res/ui/history",
"src/main/res/ui/home", "src/main/res/ui/home",
"src/main/res/ui/choose_server", "src/main/res/ui/choose_server",
"src/main/res/ui/new_wallet_recovery", "src/main/res/ui/new_wallet_recovery",

View File

@ -4,6 +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.state.TransactionHistorySyncState
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
@ -11,10 +13,26 @@ class AccountTestSetup(
private val composeTestRule: ComposeContentTestRule, private val composeTestRule: ComposeContentTestRule,
private val walletSnapshot: WalletSnapshot, private val walletSnapshot: WalletSnapshot,
) { ) {
// TODO [#1282]: Update AccountView Tests #1282
// TODO [#1282]: https://github.com/Electric-Coin-Company/zashi-android/issues/1282
val initialHistorySyncState: TransactionHistorySyncState = TransactionHistorySyncStateFixture.new()
private val onSettingsCount = AtomicInteger(0) private val onSettingsCount = AtomicInteger(0)
private val onReceiveCount = AtomicInteger(0) private val onReceiveCount = AtomicInteger(0)
private val onSendCount = AtomicInteger(0) private val onSendCount = AtomicInteger(0)
private val onHistoryCount = AtomicInteger(0) private val onItemClickCount = AtomicInteger(0)
private val onItemIdClickCount = AtomicInteger(0)
fun getOnItemClickCount(): Int {
composeTestRule.waitForIdle()
return onItemClickCount.get()
}
fun getOnItemIdClickCount(): Int {
composeTestRule.waitForIdle()
return onItemIdClickCount.get()
}
fun getOnSettingsCount(): Int { fun getOnSettingsCount(): Int {
composeTestRule.waitForIdle() composeTestRule.waitForIdle()
@ -31,11 +49,6 @@ class AccountTestSetup(
return onSendCount.get() return onSendCount.get()
} }
fun getOnHistoryCount(): Int {
composeTestRule.waitForIdle()
return onHistoryCount.get()
}
fun getWalletSnapshot(): WalletSnapshot { fun getWalletSnapshot(): WalletSnapshot {
composeTestRule.waitForIdle() composeTestRule.waitForIdle()
return walletSnapshot return walletSnapshot
@ -51,9 +64,13 @@ class AccountTestSetup(
onSettingsCount.incrementAndGet() onSettingsCount.incrementAndGet()
}, },
goBalances = {}, goBalances = {},
goHistory = { transactionState = initialHistorySyncState,
onHistoryCount.incrementAndGet() onItemClick = {
onItemClickCount.incrementAndGet()
}, },
onTransactionIdClick = {
onItemIdClickCount.incrementAndGet()
}
) )
} }

View File

@ -1,24 +1,18 @@
package co.electriccoin.zcash.ui.screen.history 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.history.state.TransactionHistorySyncState import co.electriccoin.zcash.ui.screen.account.state.TransactionHistorySyncState
import co.electriccoin.zcash.ui.screen.history.view.History 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 initialHistorySyncState: TransactionHistorySyncState
) { ) {
private val onBackClickCount = AtomicInteger(0)
private val onItemClickCount = AtomicInteger(0) private val onItemClickCount = AtomicInteger(0)
private val onItemIdClickCount = AtomicInteger(0) private val onItemIdClickCount = AtomicInteger(0)
fun getOnBackClickCount(): Int {
composeTestRule.waitForIdle()
return onBackClickCount.get()
}
fun getOnItemClickCount(): Int { fun getOnItemClickCount(): Int {
composeTestRule.waitForIdle() composeTestRule.waitForIdle()
return onItemClickCount.get() return onItemClickCount.get()
@ -32,11 +26,8 @@ class HistoryTestSetup(
init { init {
composeTestRule.setContent { composeTestRule.setContent {
ZcashTheme { ZcashTheme {
History( HistoryContainer(
transactionState = initialHistorySyncState, transactionState = initialHistorySyncState,
onBack = {
onBackClickCount.incrementAndGet()
},
onItemClick = { onItemClick = {
onItemClickCount.incrementAndGet() onItemClickCount.incrementAndGet()
}, },

View File

@ -1,8 +1,8 @@
package co.electriccoin.zcash.ui.screen.history.fixture 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.TransactionOverview import cash.z.ecc.android.sdk.model.TransactionOverview
import co.electriccoin.zcash.ui.screen.history.state.TransactionHistorySyncState import co.electriccoin.zcash.ui.screen.account.state.TransactionHistorySyncState
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf

View File

@ -1,26 +1,24 @@
package co.electriccoin.zcash.ui.screen.history.view package co.electriccoin.zcash.ui.screen.account.history.view
import androidx.compose.ui.test.assertCountEquals 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
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performClick
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.test.filters.MediumTest import androidx.test.filters.MediumTest
import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.screen.account.HistoryTag
import co.electriccoin.zcash.ui.screen.history.HistoryTag import co.electriccoin.zcash.ui.screen.account.history.HistoryTestSetup
import co.electriccoin.zcash.ui.screen.history.HistoryTestSetup import co.electriccoin.zcash.ui.screen.account.history.fixture.TransactionHistorySyncStateFixture
import co.electriccoin.zcash.ui.screen.history.fixture.TransactionHistorySyncStateFixture import co.electriccoin.zcash.ui.screen.account.state.TransactionHistorySyncState
import co.electriccoin.zcash.ui.screen.history.state.TransactionHistorySyncState
import co.electriccoin.zcash.ui.test.getStringResource
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
import org.junit.Test import kotlin.test.Ignore
import kotlin.test.Test
@Ignore("Disabled because of #1160. Will be resolved as part of #1282.")
class HistoryViewTest { class HistoryViewTest {
@get:Rule @get:Rule
val composeTestRule = createComposeRule() val composeTestRule = createComposeRule()
@ -45,9 +43,9 @@ class HistoryViewTest {
) )
) )
composeTestRule.onNodeWithText(getStringResource(R.string.history_syncing)).also { // composeTestRule.onNodeWithText(getStringResource(R.string.history_syncing)).also {
it.assertExists() // it.assertExists()
} // }
// No progress bar, as we have some transactions laid out // No progress bar, as we have some transactions laid out
composeTestRule.onNodeWithTag(HistoryTag.PROGRESS).also { composeTestRule.onNodeWithTag(HistoryTag.PROGRESS).also {
it.assertDoesNotExist() it.assertDoesNotExist()
@ -67,18 +65,18 @@ class HistoryViewTest {
transactions = persistentListOf() transactions = persistentListOf()
) )
) )
composeTestRule.onNodeWithText(getStringResource(R.string.history_syncing)).also { // composeTestRule.onNodeWithText(getStringResource(R.string.history_syncing)).also {
it.assertDoesNotExist() // it.assertDoesNotExist()
} // }
composeTestRule.onNodeWithTag(HistoryTag.PROGRESS).also { composeTestRule.onNodeWithTag(HistoryTag.PROGRESS).also {
it.assertDoesNotExist() it.assertDoesNotExist()
} }
composeTestRule.onNodeWithTag(HistoryTag.TRANSACTION_LIST).also { composeTestRule.onNodeWithTag(HistoryTag.TRANSACTION_LIST).also {
it.assertDoesNotExist() it.assertDoesNotExist()
} }
composeTestRule.onNodeWithText(getStringResource(R.string.history_empty)).also { // composeTestRule.onNodeWithText(getStringResource(R.string.history_empty)).also {
it.assertExists() // it.assertExists()
} // }
} }
@Test @Test
@ -90,9 +88,9 @@ class HistoryViewTest {
transactions = TransactionHistorySyncStateFixture.TRANSACTIONS transactions = TransactionHistorySyncStateFixture.TRANSACTIONS
) )
) )
composeTestRule.onNodeWithText(getStringResource(R.string.history_syncing)).also { // composeTestRule.onNodeWithText(getStringResource(R.string.history_syncing)).also {
it.assertDoesNotExist() // it.assertDoesNotExist()
} // }
composeTestRule.onNodeWithTag(HistoryTag.PROGRESS).also { composeTestRule.onNodeWithTag(HistoryTag.PROGRESS).also {
it.assertDoesNotExist() it.assertDoesNotExist()
} }
@ -100,25 +98,9 @@ class HistoryViewTest {
it.assertExists() it.assertExists()
it.assertHeightIsAtLeast(1.dp) it.assertHeightIsAtLeast(1.dp)
} }
composeTestRule.onNodeWithText(getStringResource(R.string.history_empty)).also { // composeTestRule.onNodeWithText(getStringResource(R.string.history_empty)).also {
it.assertDoesNotExist() // it.assertDoesNotExist()
} // }
}
@Test
@MediumTest
fun back_click_test() {
val testSetup = newTestSetup()
assertEquals(0, testSetup.getOnBackClickCount())
composeTestRule.onNodeWithContentDescription(
getStringResource(R.string.history_back_content_description)
).also {
it.performClick()
}
assertEquals(1, testSetup.getOnBackClickCount())
} }
@Test @Test

View File

@ -1,22 +1,16 @@
package co.electriccoin.zcash.ui.screen.account.view package co.electriccoin.zcash.ui.screen.account.view
import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollTo
import androidx.test.filters.MediumTest import androidx.test.filters.MediumTest
import co.electriccoin.zcash.test.UiTestPrerequisites import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.model.WalletSnapshot import co.electriccoin.zcash.ui.common.model.WalletSnapshot
import co.electriccoin.zcash.ui.design.component.CommonTag import co.electriccoin.zcash.ui.design.component.CommonTag
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.AccountTestSetup import co.electriccoin.zcash.ui.screen.account.AccountTestSetup
import co.electriccoin.zcash.ui.screen.send.clickSettingsTopAppBarMenu import co.electriccoin.zcash.ui.screen.send.clickSettingsTopAppBarMenu
import co.electriccoin.zcash.ui.test.getStringResource
import org.junit.Assert import org.junit.Assert
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
@ -41,22 +35,6 @@ class AccountViewTest : UiTestPrerequisites() {
composeTestRule.onNodeWithTag(AccountTag.BALANCE_VIEWS).also { composeTestRule.onNodeWithTag(AccountTag.BALANCE_VIEWS).also {
it.assertIsDisplayed() it.assertIsDisplayed()
} }
composeTestRule.onNodeWithText(getStringResource(R.string.account_button_history), ignoreCase = true).also {
it.assertIsDisplayed()
}
}
@Test
@MediumTest
fun click_history_button() {
val testSetup = newTestSetup()
Assert.assertEquals(0, testSetup.getOnHistoryCount())
composeTestRule.clickHistory()
Assert.assertEquals(1, testSetup.getOnHistoryCount())
} }
@Test @Test
@ -79,10 +57,3 @@ class AccountViewTest : UiTestPrerequisites() {
setDefaultContent() setDefaultContent()
} }
} }
private fun ComposeContentTestRule.clickHistory() {
onNodeWithText(getStringResource(R.string.home_button_history), ignoreCase = true).also {
it.performScrollTo()
it.performClick()
}
}

View File

@ -13,7 +13,6 @@ import co.electriccoin.zcash.ui.NavigationTargets.ABOUT
import co.electriccoin.zcash.ui.NavigationTargets.ADVANCED_SETTINGS import co.electriccoin.zcash.ui.NavigationTargets.ADVANCED_SETTINGS
import co.electriccoin.zcash.ui.NavigationTargets.CHOOSE_SERVER import co.electriccoin.zcash.ui.NavigationTargets.CHOOSE_SERVER
import co.electriccoin.zcash.ui.NavigationTargets.EXPORT_PRIVATE_DATA import co.electriccoin.zcash.ui.NavigationTargets.EXPORT_PRIVATE_DATA
import co.electriccoin.zcash.ui.NavigationTargets.HISTORY
import co.electriccoin.zcash.ui.NavigationTargets.HOME import co.electriccoin.zcash.ui.NavigationTargets.HOME
import co.electriccoin.zcash.ui.NavigationTargets.REQUEST import co.electriccoin.zcash.ui.NavigationTargets.REQUEST
import co.electriccoin.zcash.ui.NavigationTargets.SCAN import co.electriccoin.zcash.ui.NavigationTargets.SCAN
@ -26,7 +25,6 @@ import co.electriccoin.zcash.ui.screen.about.WrapAbout
import co.electriccoin.zcash.ui.screen.advancedsettings.WrapAdvancedSettings import co.electriccoin.zcash.ui.screen.advancedsettings.WrapAdvancedSettings
import co.electriccoin.zcash.ui.screen.chooseserver.WrapChooseServer import co.electriccoin.zcash.ui.screen.chooseserver.WrapChooseServer
import co.electriccoin.zcash.ui.screen.exportdata.WrapExportPrivateData import co.electriccoin.zcash.ui.screen.exportdata.WrapExportPrivateData
import co.electriccoin.zcash.ui.screen.history.WrapHistory
import co.electriccoin.zcash.ui.screen.home.WrapHome import co.electriccoin.zcash.ui.screen.home.WrapHome
import co.electriccoin.zcash.ui.screen.request.WrapRequest import co.electriccoin.zcash.ui.screen.request.WrapRequest
import co.electriccoin.zcash.ui.screen.scan.WrapScanValidator import co.electriccoin.zcash.ui.screen.scan.WrapScanValidator
@ -53,7 +51,6 @@ internal fun MainActivity.Navigation() {
homeViewModel.screenIndex.value = it homeViewModel.screenIndex.value = it
}, },
goBack = { finish() }, goBack = { finish() },
goHistory = { navController.navigateJustOnce(HISTORY) },
goSettings = { navController.navigateJustOnce(SETTINGS) }, goSettings = { navController.navigateJustOnce(SETTINGS) },
goScan = { navController.navigateJustOnce(SCAN) }, goScan = { navController.navigateJustOnce(SCAN) },
// At this point we only read scan result data // At this point we only read scan result data
@ -155,9 +152,6 @@ internal fun MainActivity.Navigation() {
onConfirm = { navController.popBackStackJustOnce(EXPORT_PRIVATE_DATA) } onConfirm = { navController.popBackStackJustOnce(EXPORT_PRIVATE_DATA) }
) )
} }
composable(HISTORY) {
WrapHistory(goBack = { navController.navigateUp() })
}
} }
} }
@ -200,7 +194,6 @@ object NavigationTargets {
const val ACCOUNT = "account" const val ACCOUNT = "account"
const val ADVANCED_SETTINGS = "advanced_settings" const val ADVANCED_SETTINGS = "advanced_settings"
const val EXPORT_PRIVATE_DATA = "export_private_data" const val EXPORT_PRIVATE_DATA = "export_private_data"
const val HISTORY = "history"
const val HOME = "home" const val HOME = "home"
const val CHOOSE_SERVER = "choose_server" const val CHOOSE_SERVER = "choose_server"
const val RECEIVE = "receive" const val RECEIVE = "receive"

View File

@ -34,7 +34,7 @@ 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.history.state.TransactionHistorySyncState import co.electriccoin.zcash.ui.screen.account.state.TransactionHistorySyncState
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

View File

@ -5,19 +5,26 @@ package co.electriccoin.zcash.ui.screen.account
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import cash.z.ecc.android.sdk.internal.Twig
import co.electriccoin.zcash.spackle.ClipboardManagerUtil
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.settings.viewmodel.SettingsViewModel import co.electriccoin.zcash.ui.screen.settings.viewmodel.SettingsViewModel
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
@Composable @Composable
internal fun WrapAccount( internal fun WrapAccount(
activity: ComponentActivity, activity: ComponentActivity,
goHistory: () -> Unit,
goBalances: () -> Unit, goBalances: () -> Unit,
goSettings: () -> Unit, goSettings: () -> Unit,
) { ) {
val scope = rememberCoroutineScope()
val walletViewModel by activity.viewModels<WalletViewModel>() val walletViewModel by activity.viewModels<WalletViewModel>()
val walletSnapshot = walletViewModel.walletSnapshot.collectAsStateWithLifecycle().value val walletSnapshot = walletViewModel.walletSnapshot.collectAsStateWithLifecycle().value
@ -25,6 +32,12 @@ internal fun WrapAccount(
val isKeepScreenOnWhileSyncing = settingsViewModel.isKeepScreenOnWhileSyncing.collectAsStateWithLifecycle().value val isKeepScreenOnWhileSyncing = settingsViewModel.isKeepScreenOnWhileSyncing.collectAsStateWithLifecycle().value
val synchronizer = walletViewModel.synchronizer.collectAsStateWithLifecycle().value
val transactionHistoryState = walletViewModel.transactionHistoryState.collectAsStateWithLifecycle().value
Twig.info { "Current transaction history state: $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
// TODO [#1146]: Improve this by allowing screen composition and updating it after the data is available // TODO [#1146]: Improve this by allowing screen composition and updating it after the data is available
@ -34,8 +47,31 @@ internal fun WrapAccount(
Account( Account(
walletSnapshot = walletSnapshot, walletSnapshot = walletSnapshot,
isKeepScreenOnWhileSyncing = isKeepScreenOnWhileSyncing, isKeepScreenOnWhileSyncing = isKeepScreenOnWhileSyncing,
transactionState = transactionHistoryState,
onItemClick = { tx ->
Twig.debug { "Transaction item clicked - querying memos..." }
val memos = synchronizer?.getMemos(tx)
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.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
)
},
goBalances = goBalances, goBalances = goBalances,
goHistory = goHistory,
goSettings = goSettings, goSettings = goSettings,
) )

View File

@ -1,4 +1,4 @@
package co.electriccoin.zcash.ui.screen.history package co.electriccoin.zcash.ui.screen.account
/** /**
* These are only used for automated testing. * These are only used for automated testing.

View File

@ -1,4 +1,4 @@
package co.electriccoin.zcash.ui.screen.history.state package co.electriccoin.zcash.ui.screen.account.state
import cash.z.ecc.android.sdk.model.TransactionOverview import cash.z.ecc.android.sdk.model.TransactionOverview
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList

View File

@ -2,12 +2,9 @@ package co.electriccoin.zcash.ui.screen.account.view
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
@ -19,42 +16,76 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import cash.z.ecc.android.sdk.Synchronizer import cash.z.ecc.android.sdk.Synchronizer
import cash.z.ecc.android.sdk.fixture.TransactionOverviewFixture
import cash.z.ecc.android.sdk.model.TransactionOverview
import cash.z.ecc.android.sdk.model.Zatoshi
import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.compose.BalanceWidget import co.electriccoin.zcash.ui.common.compose.BalanceWidget
import co.electriccoin.zcash.ui.common.compose.DisableScreenTimeout import co.electriccoin.zcash.ui.common.compose.DisableScreenTimeout
import co.electriccoin.zcash.ui.common.model.WalletSnapshot import co.electriccoin.zcash.ui.common.model.WalletSnapshot
import co.electriccoin.zcash.ui.common.test.CommonTag import co.electriccoin.zcash.ui.common.test.CommonTag
import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT
import co.electriccoin.zcash.ui.design.component.GradientSurface import co.electriccoin.zcash.ui.design.component.GradientSurface
import co.electriccoin.zcash.ui.design.component.PrimaryButton
import co.electriccoin.zcash.ui.design.component.SmallTopAppBar 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 kotlinx.collections.immutable.persistentListOf
@Preview("Account") @Preview("Account No History")
@Composable @Composable
private fun ComposablePreview() { private fun HistoryLoadingComposablePreview() {
ZcashTheme(forceDarkMode = false) { ZcashTheme(forceDarkMode = false) {
GradientSurface { GradientSurface {
Account( Account(
walletSnapshot = WalletSnapshotFixture.new(), walletSnapshot = WalletSnapshotFixture.new(),
isKeepScreenOnWhileSyncing = false, isKeepScreenOnWhileSyncing = false,
goHistory = {},
goBalances = {}, goBalances = {},
goSettings = {}, goSettings = {},
transactionState = TransactionHistorySyncState.Loading,
onItemClick = {},
onTransactionIdClick = {}
) )
} }
} }
} }
@Composable @Composable
@Preview("Account History List")
private fun HistoryListComposablePreview() {
ZcashTheme(forceDarkMode = false) {
GradientSurface {
Account(
walletSnapshot = WalletSnapshotFixture.new(),
isKeepScreenOnWhileSyncing = false,
goBalances = {},
goSettings = {},
transactionState =
TransactionHistorySyncState.Syncing(
@Suppress("MagicNumber")
persistentListOf(
TransactionOverviewFixture.new(netValue = Zatoshi(100000000)),
TransactionOverviewFixture.new(netValue = Zatoshi(200000000)),
TransactionOverviewFixture.new(netValue = Zatoshi(300000000)),
)
),
onItemClick = {},
onTransactionIdClick = {}
)
}
}
}
@Composable
@Suppress("LongParameterList")
fun Account( fun Account(
walletSnapshot: WalletSnapshot,
isKeepScreenOnWhileSyncing: Boolean?,
goBalances: () -> Unit, goBalances: () -> Unit,
goHistory: () -> Unit,
goSettings: () -> Unit, goSettings: () -> Unit,
isKeepScreenOnWhileSyncing: Boolean?,
onItemClick: (TransactionOverview) -> Unit,
onTransactionIdClick: (String) -> Unit,
transactionState: TransactionHistorySyncState,
walletSnapshot: WalletSnapshot,
) { ) {
Scaffold(topBar = { Scaffold(topBar = {
AccountTopAppBar(onSettings = goSettings) AccountTopAppBar(onSettings = goSettings)
@ -62,14 +93,15 @@ fun Account(
AccountMainContent( AccountMainContent(
walletSnapshot = walletSnapshot, walletSnapshot = walletSnapshot,
isKeepScreenOnWhileSyncing = isKeepScreenOnWhileSyncing, isKeepScreenOnWhileSyncing = isKeepScreenOnWhileSyncing,
goHistory = goHistory,
goBalances = goBalances, goBalances = goBalances,
transactionState = transactionState,
onItemClick = onItemClick,
onTransactionIdClick = onTransactionIdClick,
modifier = modifier =
Modifier.padding( Modifier.padding(
top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault, top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault,
bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingHuge, // We intentionally do not set the bottom and horizontal paddings here. Those are set by the
start = ZcashTheme.dimens.screenHorizontalSpacingRegular, // underlying transaction history composable
end = ZcashTheme.dimens.screenHorizontalSpacingRegular
) )
) )
} }
@ -94,34 +126,37 @@ private fun AccountTopAppBar(onSettings: () -> Unit) {
} }
@Composable @Composable
@Suppress("LongParameterList")
private fun AccountMainContent( private fun AccountMainContent(
walletSnapshot: WalletSnapshot, walletSnapshot: WalletSnapshot,
isKeepScreenOnWhileSyncing: Boolean?, isKeepScreenOnWhileSyncing: Boolean?,
goBalances: () -> Unit, goBalances: () -> Unit,
goHistory: () -> Unit, onItemClick: (TransactionOverview) -> Unit,
onTransactionIdClick: (String) -> Unit,
transactionState: TransactionHistorySyncState,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
Column( Column(
Modifier modifier = modifier,
.fillMaxHeight()
.verticalScroll(rememberScrollState())
.then(modifier),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))
BalancesStatus(walletSnapshot, goBalances)
Spacer(
modifier =
Modifier
.fillMaxHeight()
.weight(MINIMAL_WEIGHT)
)
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall)) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall))
PrimaryButton(onClick = goHistory, text = stringResource(R.string.account_button_history)) BalancesStatus(
walletSnapshot = walletSnapshot,
goBalances = goBalances,
modifier =
Modifier
.padding(horizontal = ZcashTheme.dimens.screenHorizontalSpacingRegular)
)
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingXlarge))
HistoryContainer(
transactionState = transactionState,
onItemClick = onItemClick,
onTransactionIdClick = onTransactionIdClick,
)
if (isKeepScreenOnWhileSyncing == true && walletSnapshot.status == Synchronizer.Status.SYNCING) { if (isKeepScreenOnWhileSyncing == true && walletSnapshot.status == Synchronizer.Status.SYNCING) {
DisableScreenTimeout() DisableScreenTimeout()
@ -130,16 +165,18 @@ private fun AccountMainContent(
} }
@Composable @Composable
@Suppress("LongMethod")
private fun BalancesStatus( private fun BalancesStatus(
walletSnapshot: WalletSnapshot, walletSnapshot: WalletSnapshot,
goBalances: () -> Unit goBalances: () -> Unit,
modifier: Modifier = Modifier
) { ) {
Column( Column(
modifier = modifier =
modifier.then(
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()
.testTag(AccountTag.BALANCE_VIEWS), .testTag(AccountTag.BALANCE_VIEWS)
),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
BalanceWidget( BalanceWidget(

View File

@ -1,12 +1,12 @@
package co.electriccoin.zcash.ui.screen.history.view package co.electriccoin.zcash.ui.screen.account.view
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
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.height
@ -15,25 +15,16 @@ 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.foundation.lazy.itemsIndexed
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Cancel import androidx.compose.material.icons.filled.Cancel
import androidx.compose.material.icons.outlined.ArrowCircleDown import androidx.compose.material.icons.outlined.ArrowCircleDown
import androidx.compose.material.icons.outlined.ArrowCircleUp import androidx.compose.material.icons.outlined.ArrowCircleUp
import androidx.compose.material.icons.twotone.ArrowCircleDown import androidx.compose.material.icons.twotone.ArrowCircleDown
import androidx.compose.material.icons.twotone.ArrowCircleUp import androidx.compose.material.icons.twotone.ArrowCircleUp
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Divider import androidx.compose.material3.Divider
import androidx.compose.material3.DividerDefaults import androidx.compose.material3.DividerDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.Center import androidx.compose.ui.Alignment.Companion.Center
import androidx.compose.ui.Alignment.Companion.TopCenter
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
@ -54,8 +45,8 @@ 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.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.account.HistoryTag
import co.electriccoin.zcash.ui.screen.history.state.TransactionHistorySyncState import co.electriccoin.zcash.ui.screen.account.state.TransactionHistorySyncState
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
@ -67,9 +58,8 @@ import java.util.Locale
private fun ComposablePreview() { private fun ComposablePreview() {
ZcashTheme(forceDarkMode = false) { ZcashTheme(forceDarkMode = false) {
GradientSurface { GradientSurface {
History( HistoryContainer(
transactionState = TransactionHistorySyncState.Loading, transactionState = TransactionHistorySyncState.Loading,
onBack = {},
onItemClick = {}, onItemClick = {},
onTransactionIdClick = {} onTransactionIdClick = {}
) )
@ -82,7 +72,7 @@ private fun ComposablePreview() {
private fun ComposableHistoryListPreview() { private fun ComposableHistoryListPreview() {
ZcashTheme(forceDarkMode = false) { ZcashTheme(forceDarkMode = false) {
GradientSurface { GradientSurface {
History( HistoryContainer(
transactionState = transactionState =
TransactionHistorySyncState.Syncing( TransactionHistorySyncState.Syncing(
@Suppress("MagicNumber") @Suppress("MagicNumber")
@ -92,7 +82,6 @@ private fun ComposableHistoryListPreview() {
TransactionOverviewFixture.new(netValue = Zatoshi(300000000)), TransactionOverviewFixture.new(netValue = Zatoshi(300000000)),
) )
), ),
onBack = {},
onItemClick = {}, onItemClick = {},
onTransactionIdClick = {} onTransactionIdClick = {}
) )
@ -108,58 +97,23 @@ val dateFormat: DateFormat by lazy {
) )
} }
@Composable
fun History(
transactionState: TransactionHistorySyncState,
onBack: () -> Unit,
onItemClick: (TransactionOverview) -> Unit,
onTransactionIdClick: (String) -> Unit
) {
Scaffold(topBar = {
HistoryTopBar(onBack = onBack)
}) { paddingValues ->
HistoryMainContent(
transactionState = transactionState,
onItemClick = onItemClick,
onTransactionIdClick = onTransactionIdClick,
modifier =
Modifier
.fillMaxHeight()
.padding(
top = paddingValues.calculateTopPadding(),
bottom = paddingValues.calculateBottomPadding()
)
)
}
}
@Composable
@OptIn(ExperimentalMaterial3Api::class)
private fun HistoryTopBar(onBack: () -> Unit) {
TopAppBar(
title = { Text(text = stringResource(id = R.string.history_title)) },
navigationIcon = {
IconButton(
onClick = onBack
) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = stringResource(R.string.history_back_content_description)
)
}
}
)
}
@Composable @Composable
@Suppress("LongMethod") @Suppress("LongMethod")
private fun HistoryMainContent( fun HistoryContainer(
transactionState: TransactionHistorySyncState, transactionState: TransactionHistorySyncState,
onItemClick: (TransactionOverview) -> Unit, onItemClick: (TransactionOverview) -> Unit,
onTransactionIdClick: (String) -> Unit, onTransactionIdClick: (String) -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
Box(modifier = modifier.fillMaxSize()) { Box(
modifier =
modifier
.then(
Modifier
.fillMaxSize()
.background(ZcashTheme.colors.historyBackgroundColor)
)
) {
when (transactionState) { when (transactionState) {
is TransactionHistorySyncState.Loading -> { is TransactionHistorySyncState.Loading -> {
CircularScreenProgressIndicator( CircularScreenProgressIndicator(
@ -170,46 +124,13 @@ private fun HistoryMainContent(
) )
} }
is TransactionHistorySyncState.Syncing -> { is TransactionHistorySyncState.Syncing -> {
Column(
modifier = Modifier.align(alignment = TopCenter)
) {
Body(
text = stringResource(id = R.string.history_syncing),
modifier =
Modifier
.padding(
top = ZcashTheme.dimens.spacingSmall,
bottom = ZcashTheme.dimens.spacingSmall,
start = ZcashTheme.dimens.spacingDefault,
end = ZcashTheme.dimens.spacingDefault
)
)
HistoryList( HistoryList(
transactions = transactionState.transactions, transactions = transactionState.transactions,
onItemClick = onItemClick, onItemClick = onItemClick,
onTransactionIdClick = onTransactionIdClick onTransactionIdClick = onTransactionIdClick
) )
} }
// Add progress indicator only in the state of empty transaction
if (transactionState.hasNoTransactions()) {
CircularProgressIndicator(
modifier =
Modifier
.align(alignment = Center)
.testTag(HistoryTag.PROGRESS)
)
}
}
is TransactionHistorySyncState.Done -> { is TransactionHistorySyncState.Done -> {
if (transactionState.hasNoTransactions()) {
Body(
text = stringResource(id = R.string.history_empty),
modifier =
Modifier
.padding(all = ZcashTheme.dimens.spacingDefault)
.align(alignment = Center)
)
} else {
HistoryList( HistoryList(
transactions = transactionState.transactions, transactions = transactionState.transactions,
onItemClick = onItemClick, onItemClick = onItemClick,
@ -219,7 +140,6 @@ private fun HistoryMainContent(
} }
} }
} }
}
@Composable @Composable
private fun HistoryList( private fun HistoryList(
@ -228,23 +148,26 @@ private fun HistoryList(
onTransactionIdClick: (String) -> Unit onTransactionIdClick: (String) -> Unit
) { ) {
val currency = ZcashCurrency.getLocalizedName(LocalContext.current) val currency = ZcashCurrency.getLocalizedName(LocalContext.current)
LazyColumn(modifier = Modifier.testTag(HistoryTag.TRANSACTION_LIST)) {
itemsIndexed(transactions) { index, item -> LazyColumn(
modifier = Modifier.testTag(HistoryTag.TRANSACTION_LIST)
) {
itemsIndexed(transactions) { _, item ->
HistoryItem( HistoryItem(
transaction = item, transaction = item,
currency = currency, currency = currency,
onItemClick = onItemClick, onItemClick = onItemClick,
onIdClick = onTransactionIdClick, onIdClick = onTransactionIdClick,
) )
if (index < transactions.lastIndex) {
Divider( Divider(
color = ZcashTheme.colors.dividerColor, color = ZcashTheme.colors.dividerColor,
thickness = DividerDefaults.Thickness thickness = DividerDefaults.Thickness,
modifier = Modifier.padding(horizontal = ZcashTheme.dimens.spacingDefault)
) )
} }
} }
} }
}
@Composable @Composable
@Suppress("LongMethod") @Suppress("LongMethod")
@ -282,13 +205,14 @@ fun HistoryItem(
Row( Row(
modifier = modifier =
modifier
.then(
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { onItemClick(transaction) } .clickable { onItemClick(transaction) }
.padding( .background(color = ZcashTheme.colors.historyBackgroundColor)
horizontal = ZcashTheme.dimens.spacingDefault, .padding(all = ZcashTheme.dimens.spacingDefault)
vertical = ZcashTheme.dimens.spacingDefault ),
).then(modifier),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Image( Image(

View File

@ -196,7 +196,7 @@ private fun BalancesMainContent(
.then(modifier), .then(modifier),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault)) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall))
BalanceWidget( BalanceWidget(
walletSnapshot = walletSnapshot, walletSnapshot = walletSnapshot,

View File

@ -1,68 +0,0 @@
package co.electriccoin.zcash.ui.screen.history
import androidx.activity.ComponentActivity
import androidx.activity.viewModels
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import cash.z.ecc.android.sdk.internal.Twig
import co.electriccoin.zcash.spackle.ClipboardManagerUtil
import co.electriccoin.zcash.ui.MainActivity
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
import co.electriccoin.zcash.ui.screen.history.view.History
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
@Composable
internal fun MainActivity.WrapHistory(goBack: () -> Unit) {
WrapHistory(
activity = this,
goBack = goBack
)
}
@Composable
internal fun WrapHistory(
activity: ComponentActivity,
goBack: () -> Unit
) {
val queryScope = rememberCoroutineScope()
val walletViewModel by activity.viewModels<WalletViewModel>()
val synchronizer = walletViewModel.synchronizer.collectAsStateWithLifecycle().value
val transactionHistoryState =
walletViewModel.transactionHistoryState.collectAsStateWithLifecycle().value
Twig.info { "Current transaction history state: $transactionHistoryState" }
History(
transactionState = transactionHistoryState,
onBack = goBack,
onItemClick = { tx ->
Twig.debug { "Transaction item clicked - querying memos..." }
val memos = synchronizer?.getMemos(tx)
queryScope.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.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

@ -22,12 +22,10 @@ import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
@Suppress("LongParameterList")
@Composable @Composable
internal fun MainActivity.WrapHome( internal fun MainActivity.WrapHome(
onPageChange: (HomeScreenIndex) -> Unit, onPageChange: (HomeScreenIndex) -> Unit,
goBack: () -> Unit, goBack: () -> Unit,
goHistory: () -> Unit,
goSettings: () -> Unit, goSettings: () -> Unit,
goScan: () -> Unit, goScan: () -> Unit,
sendArgumentsWrapper: SendArgumentsWrapper sendArgumentsWrapper: SendArgumentsWrapper
@ -36,7 +34,6 @@ internal fun MainActivity.WrapHome(
this, this,
onPageChange = onPageChange, onPageChange = onPageChange,
goBack = goBack, goBack = goBack,
goHistory = goHistory,
goScan = goScan, goScan = goScan,
goSettings = goSettings, goSettings = goSettings,
sendArgumentsWrapper = sendArgumentsWrapper sendArgumentsWrapper = sendArgumentsWrapper
@ -48,7 +45,6 @@ internal fun MainActivity.WrapHome(
internal fun WrapHome( internal fun WrapHome(
activity: ComponentActivity, activity: ComponentActivity,
goBack: () -> Unit, goBack: () -> Unit,
goHistory: () -> Unit,
goSettings: () -> Unit, goSettings: () -> Unit,
goScan: () -> Unit, goScan: () -> Unit,
onPageChange: (HomeScreenIndex) -> Unit, onPageChange: (HomeScreenIndex) -> Unit,
@ -88,7 +84,6 @@ internal fun WrapHome(
WrapAccount( WrapAccount(
activity = activity, activity = activity,
goBalances = { forceHomePageIndexFlow.tryEmit(ForcePage(HomeScreenIndex.BALANCES)) }, goBalances = { forceHomePageIndexFlow.tryEmit(ForcePage(HomeScreenIndex.BALANCES)) },
goHistory = goHistory,
goSettings = goSettings, goSettings = goSettings,
) )
} }

View File

@ -368,7 +368,7 @@ private fun SendForm(
.then(modifier), .then(modifier),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault)) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall))
BalanceWidget( BalanceWidget(
walletSnapshot = walletSnapshot, walletSnapshot = walletSnapshot,

View File

@ -1,3 +1,11 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="account_button_history">Transaction History</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_received">Received</string>
<string name="history_item_sending">Sending</string>
<string name="history_item_receiving">Receiving</string>
<string name="history_item_expired">Expired</string>
<string name="history_item_date_not_available">Date not available</string>
</resources> </resources>

View File

@ -1,16 +0,0 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="history_title">Transaction history</string>
<string name="history_back_content_description">Back</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_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_received">Received</string>
<string name="history_item_sending">Sending</string>
<string name="history_item_receiving">Receiving</string>
<string name="history_item_expired">Expired</string>
<string name="history_item_date_not_available">Date not available</string>
</resources>

View File

@ -1,6 +1,4 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="home_button_history">Transaction History</string>
<string name="home_tab_account">Account</string> <string name="home_tab_account">Account</string>
<string name="home_tab_send">Send</string> <string name="home_tab_send">Send</string>
<string name="home_tab_receive">Receive</string> <string name="home_tab_receive">Receive</string>

View File

@ -291,9 +291,6 @@ class ScreenshotTest : UiTestPrerequisites() {
composeTestRule.navigateInHomeTab(HomeTag.TAB_BALANCES) composeTestRule.navigateInHomeTab(HomeTag.TAB_BALANCES)
balancesScreenshots(resContext, tag, composeTestRule) balancesScreenshots(resContext, tag, composeTestRule)
navigateTo(NavigationTargets.HISTORY)
transactionHistoryScreenshots(resContext, tag, composeTestRule)
navigateTo(NavigationTargets.SETTINGS) navigateTo(NavigationTargets.SETTINGS)
settingsScreenshots(resContext, tag, composeTestRule) settingsScreenshots(resContext, tag, composeTestRule)
@ -419,18 +416,6 @@ private fun settingsScreenshots(
ScreenshotTest.takeScreenshot(tag, "Settings 1") ScreenshotTest.takeScreenshot(tag, "Settings 1")
} }
private fun transactionHistoryScreenshots(
resContext: Context,
tag: String,
composeTestRule: ComposeTestRule
) {
composeTestRule.onNode(hasText(resContext.getString(R.string.history_title))).also {
it.assertExists()
}
ScreenshotTest.takeScreenshot(tag, "Transaction History 1")
}
// This screen is not currently navigable from the app // This screen is not currently navigable from the app
@Suppress("UnusedPrivateMember") @Suppress("UnusedPrivateMember")
private fun requestZecScreenshots( private fun requestZecScreenshots(