[#664] Transaction History List
* [#664] Transaction history * Move under the screens folder * Fix Request screen Preview * Add TODO link * Improve Text design component * HistoryView UI enhancing * Adopt ZcashCurrency API * Add transaction history sync state * Compact time format * Bump Compose Material Icons to v1.5.0-beta02 * Add support for pending and expired transactions * Add progress in syncing with no transaction yet * Screenshot test * Simplified no transaction check * Transaction history manual test case * Home screen history button test * Fix flow collecting * View tests * Sent transaction sign * Remove unused transaction snapshot from VM --------- Co-authored-by: Honza <rychnovsky.honza@gmail.com>
This commit is contained in:
parent
2b55b1df4f
commit
fc7321e049
|
@ -0,0 +1,18 @@
|
||||||
|
# Check transactions appear in the Transaction History screen
|
||||||
|
|
||||||
|
## Test Prerequisites
|
||||||
|
- Testnet wallet seed phrase mnemonic:
|
||||||
|
`board palm case fever fuel above dinosaur caution erode search ignore damage print rare lady agent stereo tomorrow end thank hurry deputy swamp wild`
|
||||||
|
- And its birthday height: `2379900`
|
||||||
|
- Install the latest Testnet variant wallet app
|
||||||
|
- Open the wallet app and for restoring its previous state, use the wallet information above
|
||||||
|
|
||||||
|
## Test
|
||||||
|
- Click on the _Transaction History_ button on the _Home_ screen (an additional scroll down may be needed)
|
||||||
|
- **Confirm** there are no transactions displayed. There should be only a text saying that the transaction may
|
||||||
|
appear once the sync completes.
|
||||||
|
- Go back to the _Home_ screen
|
||||||
|
- **Wait** for a few minutes to sync completes and _Up-to-date_ is displayed under the current balance text
|
||||||
|
- Go to the _Transaction History_ screen again
|
||||||
|
- **Confirm** that there are some transactions displayed. There should be some incoming as well as outgoing
|
||||||
|
transactions displayed.
|
|
@ -120,7 +120,7 @@ ANDROIDX_APPCOMPAT_VERSION=1.6.1
|
||||||
ANDROIDX_CAMERA_VERSION=1.3.0-alpha06
|
ANDROIDX_CAMERA_VERSION=1.3.0-alpha06
|
||||||
ANDROIDX_COMPOSE_COMPILER_VERSION=1.4.7
|
ANDROIDX_COMPOSE_COMPILER_VERSION=1.4.7
|
||||||
ANDROIDX_COMPOSE_MATERIAL3_VERSION=1.1.0-rc01
|
ANDROIDX_COMPOSE_MATERIAL3_VERSION=1.1.0-rc01
|
||||||
ANDROIDX_COMPOSE_MATERIAL_ICONS_VERSION=1.4.3
|
ANDROIDX_COMPOSE_MATERIAL_ICONS_VERSION=1.5.0-beta02
|
||||||
ANDROIDX_COMPOSE_VERSION=1.4.3
|
ANDROIDX_COMPOSE_VERSION=1.4.3
|
||||||
ANDROIDX_CONSTRAINTLAYOUT_VERSION=1.0.1
|
ANDROIDX_CONSTRAINTLAYOUT_VERSION=1.0.1
|
||||||
ANDROIDX_CORE_VERSION=1.9.0
|
ANDROIDX_CORE_VERSION=1.9.0
|
||||||
|
|
|
@ -10,6 +10,7 @@ import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import co.electriccoin.zcash.ui.design.R
|
import co.electriccoin.zcash.ui.design.R
|
||||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||||
|
|
||||||
|
@ -30,15 +31,20 @@ fun Header(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@Suppress("LongParameterList")
|
||||||
fun Body(
|
fun Body(
|
||||||
text: String,
|
text: String,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
maxLines: Int = Int.MAX_VALUE,
|
||||||
|
overflow: TextOverflow = TextOverflow.Clip,
|
||||||
textAlign: TextAlign = TextAlign.Start,
|
textAlign: TextAlign = TextAlign.Start,
|
||||||
color: Color = MaterialTheme.colorScheme.onBackground,
|
color: Color = MaterialTheme.colorScheme.onBackground,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = text,
|
text = text,
|
||||||
color = color,
|
color = color,
|
||||||
|
maxLines = maxLines,
|
||||||
|
overflow = overflow,
|
||||||
textAlign = textAlign,
|
textAlign = textAlign,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
|
|
@ -30,6 +30,7 @@ android {
|
||||||
"src/main/res/ui/about",
|
"src/main/res/ui/about",
|
||||||
"src/main/res/ui/backup",
|
"src/main/res/ui/backup",
|
||||||
"src/main/res/ui/common",
|
"src/main/res/ui/common",
|
||||||
|
"src/main/res/ui/history",
|
||||||
"src/main/res/ui/home",
|
"src/main/res/ui/home",
|
||||||
"src/main/res/ui/onboarding",
|
"src/main/res/ui/onboarding",
|
||||||
"src/main/res/ui/receive",
|
"src/main/res/ui/receive",
|
||||||
|
|
|
@ -2,7 +2,6 @@ package co.electriccoin.zcash.ui.common
|
||||||
|
|
||||||
import androidx.test.filters.FlakyTest
|
import androidx.test.filters.FlakyTest
|
||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
||||||
import kotlinx.coroutines.flow.asFlow
|
import kotlinx.coroutines.flow.asFlow
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
@ -17,7 +16,7 @@ import kotlin.time.TimeSource
|
||||||
|
|
||||||
class FlowExtTest {
|
class FlowExtTest {
|
||||||
|
|
||||||
@OptIn(ExperimentalTime::class, ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalTime::class)
|
||||||
@Test
|
@Test
|
||||||
@SmallTest
|
@SmallTest
|
||||||
fun throttle_one_sec() = runTest {
|
fun throttle_one_sec() = runTest {
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
package co.electriccoin.zcash.ui.screen.history
|
||||||
|
|
||||||
|
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
||||||
|
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||||
|
import co.electriccoin.zcash.ui.screen.history.state.TransactionHistorySyncState
|
||||||
|
import co.electriccoin.zcash.ui.screen.history.view.History
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
|
class HistoryTestSetup(
|
||||||
|
private val composeTestRule: ComposeContentTestRule,
|
||||||
|
initialHistorySyncState: TransactionHistorySyncState
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val onBackCount = AtomicInteger(0)
|
||||||
|
|
||||||
|
fun getOnBackCount(): Int {
|
||||||
|
composeTestRule.waitForIdle()
|
||||||
|
return onBackCount.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
composeTestRule.setContent {
|
||||||
|
ZcashTheme {
|
||||||
|
History(
|
||||||
|
transactionState = initialHistorySyncState,
|
||||||
|
goBack = {
|
||||||
|
onBackCount.incrementAndGet()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package co.electriccoin.zcash.ui.screen.history.fixture
|
||||||
|
|
||||||
|
import cash.z.ecc.android.sdk.fixture.TransactionOverviewFixture
|
||||||
|
import cash.z.ecc.android.sdk.model.TransactionOverview
|
||||||
|
import co.electriccoin.zcash.ui.screen.history.state.TransactionHistorySyncState
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
||||||
|
internal object TransactionHistorySyncStateFixture {
|
||||||
|
val TRANSACTIONS = persistentListOf(
|
||||||
|
TransactionOverviewFixture.new(id = 0),
|
||||||
|
TransactionOverviewFixture.new(id = 1),
|
||||||
|
TransactionOverviewFixture.new(id = 2)
|
||||||
|
)
|
||||||
|
val STATE = TransactionHistorySyncState.Syncing(TRANSACTIONS)
|
||||||
|
|
||||||
|
fun new(
|
||||||
|
transactions: ImmutableList<TransactionOverview> = TRANSACTIONS,
|
||||||
|
state: TransactionHistorySyncState = STATE
|
||||||
|
) = when (state) {
|
||||||
|
is TransactionHistorySyncState.Syncing -> {
|
||||||
|
state.copy(transactions)
|
||||||
|
}
|
||||||
|
is TransactionHistorySyncState.Done -> {
|
||||||
|
state.copy(transactions)
|
||||||
|
}
|
||||||
|
TransactionHistorySyncState.Loading -> {
|
||||||
|
state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
package co.electriccoin.zcash.ui.screen.history.view
|
||||||
|
|
||||||
|
import androidx.compose.ui.test.assertHeightIsAtLeast
|
||||||
|
import androidx.compose.ui.test.junit4.createComposeRule
|
||||||
|
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||||
|
import androidx.compose.ui.test.onNodeWithTag
|
||||||
|
import androidx.compose.ui.test.onNodeWithText
|
||||||
|
import androidx.compose.ui.test.performClick
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.test.filters.MediumTest
|
||||||
|
import co.electriccoin.zcash.ui.R
|
||||||
|
import co.electriccoin.zcash.ui.screen.history.HistoryTag
|
||||||
|
import co.electriccoin.zcash.ui.screen.history.HistoryTestSetup
|
||||||
|
import co.electriccoin.zcash.ui.screen.history.fixture.TransactionHistorySyncStateFixture
|
||||||
|
import co.electriccoin.zcash.ui.screen.history.state.TransactionHistorySyncState
|
||||||
|
import co.electriccoin.zcash.ui.test.getStringResource
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class HistoryViewTest {
|
||||||
|
@get:Rule
|
||||||
|
val composeTestRule = createComposeRule()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@MediumTest
|
||||||
|
fun check_loading_state() {
|
||||||
|
newTestSetup(TransactionHistorySyncState.Loading)
|
||||||
|
|
||||||
|
composeTestRule.onNodeWithTag(HistoryTag.PROGRESS).also {
|
||||||
|
it.assertExists()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@MediumTest
|
||||||
|
fun check_syncing_state() {
|
||||||
|
newTestSetup(
|
||||||
|
TransactionHistorySyncStateFixture.new(
|
||||||
|
state = TransactionHistorySyncStateFixture.STATE,
|
||||||
|
transactions = TransactionHistorySyncStateFixture.TRANSACTIONS
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
composeTestRule.onNodeWithText(getStringResource(R.string.history_syncing)).also {
|
||||||
|
it.assertExists()
|
||||||
|
}
|
||||||
|
// No progress bar, as we have some transactions laid out
|
||||||
|
composeTestRule.onNodeWithTag(HistoryTag.PROGRESS).also {
|
||||||
|
it.assertDoesNotExist()
|
||||||
|
}
|
||||||
|
composeTestRule.onNodeWithTag(HistoryTag.TRANSACTION_LIST).also {
|
||||||
|
it.assertExists()
|
||||||
|
it.assertHeightIsAtLeast(1.dp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@MediumTest
|
||||||
|
fun check_done_state_no_transactions() {
|
||||||
|
newTestSetup(
|
||||||
|
TransactionHistorySyncStateFixture.new(
|
||||||
|
state = TransactionHistorySyncState.Done(persistentListOf()),
|
||||||
|
transactions = persistentListOf()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
composeTestRule.onNodeWithText(getStringResource(R.string.history_syncing)).also {
|
||||||
|
it.assertDoesNotExist()
|
||||||
|
}
|
||||||
|
composeTestRule.onNodeWithTag(HistoryTag.PROGRESS).also {
|
||||||
|
it.assertDoesNotExist()
|
||||||
|
}
|
||||||
|
composeTestRule.onNodeWithTag(HistoryTag.TRANSACTION_LIST).also {
|
||||||
|
it.assertDoesNotExist()
|
||||||
|
}
|
||||||
|
composeTestRule.onNodeWithText(getStringResource(R.string.history_empty)).also {
|
||||||
|
it.assertExists()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@MediumTest
|
||||||
|
fun check_done_state_with_transactions() {
|
||||||
|
newTestSetup(
|
||||||
|
TransactionHistorySyncStateFixture.new(
|
||||||
|
state = TransactionHistorySyncState.Done(persistentListOf()),
|
||||||
|
transactions = TransactionHistorySyncStateFixture.TRANSACTIONS
|
||||||
|
)
|
||||||
|
)
|
||||||
|
composeTestRule.onNodeWithText(getStringResource(R.string.history_syncing)).also {
|
||||||
|
it.assertDoesNotExist()
|
||||||
|
}
|
||||||
|
composeTestRule.onNodeWithTag(HistoryTag.PROGRESS).also {
|
||||||
|
it.assertDoesNotExist()
|
||||||
|
}
|
||||||
|
composeTestRule.onNodeWithTag(HistoryTag.TRANSACTION_LIST).also {
|
||||||
|
it.assertExists()
|
||||||
|
it.assertHeightIsAtLeast(1.dp)
|
||||||
|
}
|
||||||
|
composeTestRule.onNodeWithText(getStringResource(R.string.history_empty)).also {
|
||||||
|
it.assertDoesNotExist()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@MediumTest
|
||||||
|
fun back() {
|
||||||
|
val testSetup = newTestSetup()
|
||||||
|
|
||||||
|
assertEquals(0, testSetup.getOnBackCount())
|
||||||
|
|
||||||
|
composeTestRule.onNodeWithContentDescription(
|
||||||
|
getStringResource(R.string.history_back_content_description)
|
||||||
|
).also {
|
||||||
|
it.performClick()
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(1, testSetup.getOnBackCount())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun newTestSetup(
|
||||||
|
transactionHistorySyncState: TransactionHistorySyncState = TransactionHistorySyncStateFixture.new()
|
||||||
|
) = HistoryTestSetup(
|
||||||
|
composeTestRule = composeTestRule,
|
||||||
|
initialHistorySyncState = transactionHistorySyncState
|
||||||
|
)
|
||||||
|
}
|
|
@ -5,7 +5,6 @@ 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.home.model.WalletSnapshot
|
import co.electriccoin.zcash.ui.screen.home.model.WalletSnapshot
|
||||||
import co.electriccoin.zcash.ui.screen.home.view.Home
|
import co.electriccoin.zcash.ui.screen.home.view.Home
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
class HomeTestSetup(
|
class HomeTestSetup(
|
||||||
|
@ -20,6 +19,7 @@ class HomeTestSetup(
|
||||||
private val onSupportCount = AtomicInteger(0)
|
private val onSupportCount = 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)
|
||||||
|
|
||||||
fun getOnAboutCount(): Int {
|
fun getOnAboutCount(): Int {
|
||||||
composeTestRule.waitForIdle()
|
composeTestRule.waitForIdle()
|
||||||
|
@ -51,6 +51,11 @@ class HomeTestSetup(
|
||||||
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
|
||||||
|
@ -62,7 +67,6 @@ class HomeTestSetup(
|
||||||
val drawerValues = drawerBackHandler()
|
val drawerValues = drawerBackHandler()
|
||||||
Home(
|
Home(
|
||||||
walletSnapshot,
|
walletSnapshot,
|
||||||
transactionHistory = persistentListOf(),
|
|
||||||
isUpdateAvailable = false,
|
isUpdateAvailable = false,
|
||||||
isKeepScreenOnDuringSync = false,
|
isKeepScreenOnDuringSync = false,
|
||||||
isFiatConversionEnabled = isShowFiatConversion,
|
isFiatConversionEnabled = isShowFiatConversion,
|
||||||
|
@ -86,6 +90,9 @@ class HomeTestSetup(
|
||||||
goSend = {
|
goSend = {
|
||||||
onSendCount.incrementAndGet()
|
onSendCount.incrementAndGet()
|
||||||
},
|
},
|
||||||
|
goHistory = {
|
||||||
|
onHistoryCount.incrementAndGet()
|
||||||
|
},
|
||||||
resetSdk = {},
|
resetSdk = {},
|
||||||
drawerState = drawerValues.drawerState,
|
drawerState = drawerValues.drawerState,
|
||||||
scope = drawerValues.scope
|
scope = drawerValues.scope
|
||||||
|
|
|
@ -121,6 +121,18 @@ class HomeViewTest : UiTestPrerequisites() {
|
||||||
assertEquals(1, testSetup.getOnSendCount())
|
assertEquals(1, testSetup.getOnSendCount())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@MediumTest
|
||||||
|
fun click_history_button() {
|
||||||
|
val testSetup = newTestSetup()
|
||||||
|
|
||||||
|
assertEquals(0, testSetup.getOnHistoryCount())
|
||||||
|
|
||||||
|
composeTestRule.clickHistory()
|
||||||
|
|
||||||
|
assertEquals(1, testSetup.getOnHistoryCount())
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@MediumTest
|
@MediumTest
|
||||||
fun hamburger_seed() {
|
fun hamburger_seed() {
|
||||||
|
@ -217,3 +229,10 @@ private fun ComposeContentTestRule.clickSend() {
|
||||||
it.performClick()
|
it.performClick()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun ComposeContentTestRule.clickHistory() {
|
||||||
|
onNodeWithText(getStringResource(R.string.home_button_history)).also {
|
||||||
|
it.performScrollTo()
|
||||||
|
it.performClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import co.electriccoin.zcash.ui.NavigationArguments.SEND_AMOUNT
|
||||||
import co.electriccoin.zcash.ui.NavigationArguments.SEND_MEMO
|
import co.electriccoin.zcash.ui.NavigationArguments.SEND_MEMO
|
||||||
import co.electriccoin.zcash.ui.NavigationArguments.SEND_RECIPIENT_ADDRESS
|
import co.electriccoin.zcash.ui.NavigationArguments.SEND_RECIPIENT_ADDRESS
|
||||||
import co.electriccoin.zcash.ui.NavigationTargets.ABOUT
|
import co.electriccoin.zcash.ui.NavigationTargets.ABOUT
|
||||||
|
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.RECEIVE
|
import co.electriccoin.zcash.ui.NavigationTargets.RECEIVE
|
||||||
import co.electriccoin.zcash.ui.NavigationTargets.REQUEST
|
import co.electriccoin.zcash.ui.NavigationTargets.REQUEST
|
||||||
|
@ -25,6 +26,7 @@ import co.electriccoin.zcash.ui.configuration.ConfigurationEntries
|
||||||
import co.electriccoin.zcash.ui.configuration.RemoteConfig
|
import co.electriccoin.zcash.ui.configuration.RemoteConfig
|
||||||
import co.electriccoin.zcash.ui.screen.about.WrapAbout
|
import co.electriccoin.zcash.ui.screen.about.WrapAbout
|
||||||
import co.electriccoin.zcash.ui.screen.address.WrapWalletAddresses
|
import co.electriccoin.zcash.ui.screen.address.WrapWalletAddresses
|
||||||
|
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.receive.WrapReceive
|
import co.electriccoin.zcash.ui.screen.receive.WrapReceive
|
||||||
import co.electriccoin.zcash.ui.screen.request.WrapRequest
|
import co.electriccoin.zcash.ui.screen.request.WrapRequest
|
||||||
|
@ -54,6 +56,7 @@ internal fun MainActivity.Navigation() {
|
||||||
goAbout = { navController.navigateJustOnce(ABOUT) },
|
goAbout = { navController.navigateJustOnce(ABOUT) },
|
||||||
goReceive = { navController.navigateJustOnce(RECEIVE) },
|
goReceive = { navController.navigateJustOnce(RECEIVE) },
|
||||||
goSend = { navController.navigateJustOnce(SEND) },
|
goSend = { navController.navigateJustOnce(SEND) },
|
||||||
|
goHistory = { navController.navigateJustOnce(HISTORY) }
|
||||||
)
|
)
|
||||||
|
|
||||||
if (ConfigurationEntries.IS_APP_UPDATE_CHECK_ENABLED.getValue(RemoteConfig.current)) {
|
if (ConfigurationEntries.IS_APP_UPDATE_CHECK_ENABLED.getValue(RemoteConfig.current)) {
|
||||||
|
@ -128,6 +131,10 @@ internal fun MainActivity.Navigation() {
|
||||||
goBack = { navController.popBackStackJustOnce(SCAN) }
|
goBack = { navController.popBackStackJustOnce(SCAN) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
composable(HISTORY) {
|
||||||
|
WrapHistory(goBack = { navController.navigateUp() })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,6 +185,8 @@ object NavigationTargets {
|
||||||
|
|
||||||
const val REQUEST = "request"
|
const val REQUEST = "request"
|
||||||
|
|
||||||
|
const val HISTORY = "history"
|
||||||
|
|
||||||
const val SEND = "send"
|
const val SEND = "send"
|
||||||
|
|
||||||
const val SUPPORT = "support"
|
const val SUPPORT = "support"
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package co.electriccoin.zcash.ui.screen.history
|
||||||
|
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import cash.z.ecc.android.sdk.internal.Twig
|
||||||
|
import co.electriccoin.zcash.ui.MainActivity
|
||||||
|
import co.electriccoin.zcash.ui.screen.history.view.History
|
||||||
|
import co.electriccoin.zcash.ui.screen.home.viewmodel.WalletViewModel
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun MainActivity.WrapHistory(
|
||||||
|
goBack: () -> Unit
|
||||||
|
) {
|
||||||
|
WrapHistory(
|
||||||
|
activity = this,
|
||||||
|
goBack = goBack
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun WrapHistory(
|
||||||
|
activity: ComponentActivity,
|
||||||
|
goBack: () -> Unit
|
||||||
|
) {
|
||||||
|
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||||
|
|
||||||
|
val transactionHistoryState =
|
||||||
|
walletViewModel.transactionHistoryState.collectAsStateWithLifecycle().value
|
||||||
|
|
||||||
|
Twig.info { "Current transaction history state: $transactionHistoryState" }
|
||||||
|
|
||||||
|
History(
|
||||||
|
transactionState = transactionHistoryState,
|
||||||
|
goBack = goBack
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package co.electriccoin.zcash.ui.screen.history
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These are only used for automated testing.
|
||||||
|
*/
|
||||||
|
object HistoryTag {
|
||||||
|
const val TRANSACTION_LIST = "transaction_list"
|
||||||
|
const val PROGRESS = "progress_bar"
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package co.electriccoin.zcash.ui.screen.history.state
|
||||||
|
|
||||||
|
import cash.z.ecc.android.sdk.model.TransactionOverview
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
|
sealed class TransactionHistorySyncState {
|
||||||
|
|
||||||
|
object Loading : TransactionHistorySyncState() {
|
||||||
|
override fun toString() = "Loading" // NON-NLS
|
||||||
|
}
|
||||||
|
data class Syncing(val transactions: ImmutableList<TransactionOverview>) : TransactionHistorySyncState() {
|
||||||
|
fun hasNoTransactions(): Boolean {
|
||||||
|
return transactions.isEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data class Done(val transactions: ImmutableList<TransactionOverview>) : TransactionHistorySyncState() {
|
||||||
|
fun hasNoTransactions(): Boolean {
|
||||||
|
return transactions.isEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,300 @@
|
||||||
|
package co.electriccoin.zcash.ui.screen.history.view
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
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.outlined.ArrowCircleDown
|
||||||
|
import androidx.compose.material.icons.outlined.ArrowCircleUp
|
||||||
|
import androidx.compose.material.icons.twotone.ArrowCircleDown
|
||||||
|
import androidx.compose.material.icons.twotone.ArrowCircleUp
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
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.ui.Alignment
|
||||||
|
import androidx.compose.ui.Alignment.Companion.Center
|
||||||
|
import androidx.compose.ui.Alignment.Companion.TopCenter
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.testTag
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import cash.z.ecc.android.sdk.model.TransactionOverview
|
||||||
|
import cash.z.ecc.android.sdk.model.TransactionState
|
||||||
|
import cash.z.ecc.android.sdk.model.toZecString
|
||||||
|
import cash.z.ecc.sdk.type.ZcashCurrency
|
||||||
|
import co.electriccoin.zcash.ui.R
|
||||||
|
import co.electriccoin.zcash.ui.design.component.Body
|
||||||
|
import co.electriccoin.zcash.ui.design.component.GradientSurface
|
||||||
|
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||||
|
import co.electriccoin.zcash.ui.screen.history.HistoryTag
|
||||||
|
import co.electriccoin.zcash.ui.screen.history.state.TransactionHistorySyncState
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import java.text.DateFormat
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@Preview("History")
|
||||||
|
@Composable
|
||||||
|
private fun ComposablePreview() {
|
||||||
|
ZcashTheme(darkTheme = true) {
|
||||||
|
GradientSurface {
|
||||||
|
History(
|
||||||
|
transactionState = TransactionHistorySyncState.Loading,
|
||||||
|
goBack = {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val dateFormat: DateFormat by lazy {
|
||||||
|
SimpleDateFormat.getDateTimeInstance(
|
||||||
|
SimpleDateFormat.MEDIUM,
|
||||||
|
SimpleDateFormat.SHORT,
|
||||||
|
Locale.getDefault()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun History(
|
||||||
|
transactionState: TransactionHistorySyncState,
|
||||||
|
goBack: () -> Unit
|
||||||
|
) {
|
||||||
|
Scaffold(topBar = {
|
||||||
|
HistoryTopBar(onBack = goBack)
|
||||||
|
}) { paddingValues ->
|
||||||
|
HistoryMainContent(
|
||||||
|
transactionState = transactionState,
|
||||||
|
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
|
||||||
|
private fun HistoryMainContent(
|
||||||
|
transactionState: TransactionHistorySyncState,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
Box(modifier = modifier.fillMaxSize()) {
|
||||||
|
when (transactionState) {
|
||||||
|
is TransactionHistorySyncState.Loading -> {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
modifier = Modifier
|
||||||
|
.align(alignment = Center)
|
||||||
|
.testTag(HistoryTag.PROGRESS)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
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(transactions = transactionState.transactions)
|
||||||
|
}
|
||||||
|
// 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 -> {
|
||||||
|
if (transactionState.hasNoTransactions()) {
|
||||||
|
Body(
|
||||||
|
text = stringResource(id = R.string.history_empty),
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(all = ZcashTheme.dimens.spacingDefault)
|
||||||
|
.align(alignment = Center)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
HistoryList(transactions = transactionState.transactions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun HistoryList(transactions: ImmutableList<TransactionOverview>) {
|
||||||
|
val currency = ZcashCurrency.fromResources(LocalContext.current)
|
||||||
|
LazyColumn(
|
||||||
|
contentPadding = PaddingValues(all = ZcashTheme.dimens.spacingDefault),
|
||||||
|
modifier = Modifier.testTag(HistoryTag.TRANSACTION_LIST)
|
||||||
|
) {
|
||||||
|
items(transactions) {
|
||||||
|
HistoryItem(
|
||||||
|
transaction = it,
|
||||||
|
currency = currency
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Suppress("LongMethod")
|
||||||
|
fun HistoryItem(
|
||||||
|
transaction: TransactionOverview,
|
||||||
|
currency: ZcashCurrency
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = ZcashTheme.dimens.spacingSmall),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
val transactionText: String
|
||||||
|
val transactionIcon: ImageVector
|
||||||
|
when (transaction.getExtendedState()) {
|
||||||
|
TransactionExtendedState.SENT -> {
|
||||||
|
transactionText = stringResource(id = R.string.history_item_sent)
|
||||||
|
transactionIcon = Icons.TwoTone.ArrowCircleUp
|
||||||
|
}
|
||||||
|
TransactionExtendedState.SENDING -> {
|
||||||
|
transactionText = stringResource(id = R.string.history_item_sending)
|
||||||
|
transactionIcon = Icons.Outlined.ArrowCircleUp
|
||||||
|
}
|
||||||
|
TransactionExtendedState.RECEIVED -> {
|
||||||
|
transactionText = stringResource(id = R.string.history_item_received)
|
||||||
|
transactionIcon = Icons.TwoTone.ArrowCircleDown
|
||||||
|
}
|
||||||
|
TransactionExtendedState.RECEIVING -> {
|
||||||
|
transactionText = stringResource(id = R.string.history_item_receiving)
|
||||||
|
transactionIcon = Icons.Outlined.ArrowCircleDown
|
||||||
|
}
|
||||||
|
TransactionExtendedState.EXPIRED -> {
|
||||||
|
transactionText = stringResource(id = R.string.history_item_expired)
|
||||||
|
transactionIcon = Icons.Filled.Cancel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image(
|
||||||
|
imageVector = transactionIcon,
|
||||||
|
contentDescription = transactionText,
|
||||||
|
modifier = Modifier.padding(all = ZcashTheme.dimens.spacingTiny)
|
||||||
|
)
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
) {
|
||||||
|
Body(
|
||||||
|
text = transactionText,
|
||||||
|
color = Color.Black
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingTiny))
|
||||||
|
|
||||||
|
// * 1000 to covert to millis
|
||||||
|
@Suppress("MagicNumber")
|
||||||
|
val dateString = dateFormat.format(transaction.blockTimeEpochSeconds.times(1000))
|
||||||
|
Body(
|
||||||
|
text = dateString,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
Row(modifier = Modifier.align(alignment = Alignment.End)) {
|
||||||
|
val zecString = if (transaction.isSentTransaction) {
|
||||||
|
"-${transaction.netValue.toZecString()}"
|
||||||
|
} else {
|
||||||
|
transaction.netValue.toZecString()
|
||||||
|
}
|
||||||
|
Body(text = zecString)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.width(ZcashTheme.dimens.spacingTiny))
|
||||||
|
|
||||||
|
Body(text = currency.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class TransactionExtendedState {
|
||||||
|
SENT,
|
||||||
|
SENDING,
|
||||||
|
RECEIVED,
|
||||||
|
RECEIVING,
|
||||||
|
EXPIRED
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun TransactionOverview.getExtendedState(): TransactionExtendedState {
|
||||||
|
return when (transactionState) {
|
||||||
|
TransactionState.Expired -> {
|
||||||
|
TransactionExtendedState.EXPIRED
|
||||||
|
}
|
||||||
|
TransactionState.Confirmed -> {
|
||||||
|
if (isSentTransaction) {
|
||||||
|
TransactionExtendedState.SENT
|
||||||
|
} else {
|
||||||
|
TransactionExtendedState.RECEIVED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TransactionState.Pending -> {
|
||||||
|
if (isSentTransaction) {
|
||||||
|
TransactionExtendedState.SENDING
|
||||||
|
} else {
|
||||||
|
TransactionExtendedState.RECEIVING
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
error("Unexpected transaction state found while calculating its extended state.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,6 +36,7 @@ internal fun MainActivity.WrapHome(
|
||||||
goAbout: () -> Unit,
|
goAbout: () -> Unit,
|
||||||
goReceive: () -> Unit,
|
goReceive: () -> Unit,
|
||||||
goSend: () -> Unit,
|
goSend: () -> Unit,
|
||||||
|
goHistory: () -> Unit
|
||||||
) {
|
) {
|
||||||
WrapHome(
|
WrapHome(
|
||||||
this,
|
this,
|
||||||
|
@ -45,6 +46,7 @@ internal fun MainActivity.WrapHome(
|
||||||
goAbout = goAbout,
|
goAbout = goAbout,
|
||||||
goReceive = goReceive,
|
goReceive = goReceive,
|
||||||
goSend = goSend,
|
goSend = goSend,
|
||||||
|
goHistory = goHistory,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +60,7 @@ internal fun WrapHome(
|
||||||
goAbout: () -> Unit,
|
goAbout: () -> Unit,
|
||||||
goReceive: () -> Unit,
|
goReceive: () -> Unit,
|
||||||
goSend: () -> Unit,
|
goSend: () -> Unit,
|
||||||
|
goHistory: () -> Unit,
|
||||||
) {
|
) {
|
||||||
// we want to show information about app update, if available
|
// we want to show information about app update, if available
|
||||||
val checkUpdateViewModel by activity.viewModels<CheckUpdateViewModel> {
|
val checkUpdateViewModel by activity.viewModels<CheckUpdateViewModel> {
|
||||||
|
@ -91,13 +94,10 @@ internal fun WrapHome(
|
||||||
!FirebaseTestLabUtil.isFirebaseTestLab(context) &&
|
!FirebaseTestLabUtil.isFirebaseTestLab(context) &&
|
||||||
!EmulatorWtfUtil.isEmulatorWtf(context)
|
!EmulatorWtfUtil.isEmulatorWtf(context)
|
||||||
|
|
||||||
val transactionSnapshot = walletViewModel.transactionSnapshot.collectAsStateWithLifecycle().value
|
|
||||||
|
|
||||||
val drawerValues = drawerBackHandler()
|
val drawerValues = drawerBackHandler()
|
||||||
|
|
||||||
Home(
|
Home(
|
||||||
walletSnapshot,
|
walletSnapshot,
|
||||||
transactionSnapshot,
|
|
||||||
isUpdateAvailable = updateAvailable,
|
isUpdateAvailable = updateAvailable,
|
||||||
isKeepScreenOnDuringSync = isKeepScreenOnWhileSyncing,
|
isKeepScreenOnDuringSync = isKeepScreenOnWhileSyncing,
|
||||||
isFiatConversionEnabled = isFiatConversionEnabled,
|
isFiatConversionEnabled = isFiatConversionEnabled,
|
||||||
|
@ -109,6 +109,7 @@ internal fun WrapHome(
|
||||||
goAbout = goAbout,
|
goAbout = goAbout,
|
||||||
goReceive = goReceive,
|
goReceive = goReceive,
|
||||||
goSend = goSend,
|
goSend = goSend,
|
||||||
|
goHistory = goHistory,
|
||||||
resetSdk = {
|
resetSdk = {
|
||||||
walletViewModel.resetSdk()
|
walletViewModel.resetSdk()
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,6 +9,7 @@ import cash.z.ecc.android.sdk.model.Zatoshi
|
||||||
import co.electriccoin.zcash.ui.screen.home.viewmodel.SynchronizerError
|
import co.electriccoin.zcash.ui.screen.home.viewmodel.SynchronizerError
|
||||||
|
|
||||||
// TODO [#292]: Should be moved to SDK-EXT-UI module.
|
// TODO [#292]: Should be moved to SDK-EXT-UI module.
|
||||||
|
// TODO [#292]: https://github.com/zcash/secant-android-wallet/issues/292
|
||||||
data class WalletSnapshot(
|
data class WalletSnapshot(
|
||||||
val status: Synchronizer.Status,
|
val status: Synchronizer.Status,
|
||||||
val processorInfo: CompactBlockProcessor.ProcessorInfo,
|
val processorInfo: CompactBlockProcessor.ProcessorInfo,
|
||||||
|
|
|
@ -13,8 +13,6 @@ 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.layout.wrapContentSize
|
import androidx.compose.foundation.layout.wrapContentSize
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.lazy.items
|
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
@ -58,7 +56,6 @@ import androidx.compose.ui.unit.dp
|
||||||
import cash.z.ecc.android.sdk.Synchronizer
|
import cash.z.ecc.android.sdk.Synchronizer
|
||||||
import cash.z.ecc.android.sdk.model.FiatCurrencyConversionRateState
|
import cash.z.ecc.android.sdk.model.FiatCurrencyConversionRateState
|
||||||
import cash.z.ecc.android.sdk.model.PercentDecimal
|
import cash.z.ecc.android.sdk.model.PercentDecimal
|
||||||
import cash.z.ecc.android.sdk.model.TransactionOverview
|
|
||||||
import co.electriccoin.zcash.crash.android.GlobalCrashReporter
|
import co.electriccoin.zcash.crash.android.GlobalCrashReporter
|
||||||
import co.electriccoin.zcash.ui.R
|
import co.electriccoin.zcash.ui.R
|
||||||
import co.electriccoin.zcash.ui.common.DisableScreenTimeout
|
import co.electriccoin.zcash.ui.common.DisableScreenTimeout
|
||||||
|
@ -69,13 +66,12 @@ import co.electriccoin.zcash.ui.design.component.BodyWithFiatCurrencySymbol
|
||||||
import co.electriccoin.zcash.ui.design.component.GradientSurface
|
import co.electriccoin.zcash.ui.design.component.GradientSurface
|
||||||
import co.electriccoin.zcash.ui.design.component.HeaderWithZecIcon
|
import co.electriccoin.zcash.ui.design.component.HeaderWithZecIcon
|
||||||
import co.electriccoin.zcash.ui.design.component.PrimaryButton
|
import co.electriccoin.zcash.ui.design.component.PrimaryButton
|
||||||
|
import co.electriccoin.zcash.ui.design.component.TertiaryButton
|
||||||
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.home.HomeTag
|
import co.electriccoin.zcash.ui.screen.home.HomeTag
|
||||||
import co.electriccoin.zcash.ui.screen.home.model.WalletDisplayValues
|
import co.electriccoin.zcash.ui.screen.home.model.WalletDisplayValues
|
||||||
import co.electriccoin.zcash.ui.screen.home.model.WalletSnapshot
|
import co.electriccoin.zcash.ui.screen.home.model.WalletSnapshot
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
|
||||||
@Preview("Home")
|
@Preview("Home")
|
||||||
|
@ -85,7 +81,6 @@ private fun ComposablePreview() {
|
||||||
GradientSurface {
|
GradientSurface {
|
||||||
Home(
|
Home(
|
||||||
walletSnapshot = WalletSnapshotFixture.new(),
|
walletSnapshot = WalletSnapshotFixture.new(),
|
||||||
transactionHistory = persistentListOf(),
|
|
||||||
isUpdateAvailable = false,
|
isUpdateAvailable = false,
|
||||||
isKeepScreenOnDuringSync = false,
|
isKeepScreenOnDuringSync = false,
|
||||||
isDebugMenuEnabled = false,
|
isDebugMenuEnabled = false,
|
||||||
|
@ -97,6 +92,7 @@ private fun ComposablePreview() {
|
||||||
goAbout = {},
|
goAbout = {},
|
||||||
goReceive = {},
|
goReceive = {},
|
||||||
goSend = {},
|
goSend = {},
|
||||||
|
goHistory = {},
|
||||||
resetSdk = {},
|
resetSdk = {},
|
||||||
drawerState = rememberDrawerState(DrawerValue.Closed),
|
drawerState = rememberDrawerState(DrawerValue.Closed),
|
||||||
scope = rememberCoroutineScope()
|
scope = rememberCoroutineScope()
|
||||||
|
@ -109,7 +105,6 @@ private fun ComposablePreview() {
|
||||||
@Composable
|
@Composable
|
||||||
fun Home(
|
fun Home(
|
||||||
walletSnapshot: WalletSnapshot,
|
walletSnapshot: WalletSnapshot,
|
||||||
transactionHistory: ImmutableList<TransactionOverview>,
|
|
||||||
isUpdateAvailable: Boolean,
|
isUpdateAvailable: Boolean,
|
||||||
isKeepScreenOnDuringSync: Boolean?,
|
isKeepScreenOnDuringSync: Boolean?,
|
||||||
isFiatConversionEnabled: Boolean,
|
isFiatConversionEnabled: Boolean,
|
||||||
|
@ -121,6 +116,7 @@ fun Home(
|
||||||
goAbout: () -> Unit,
|
goAbout: () -> Unit,
|
||||||
goReceive: () -> Unit,
|
goReceive: () -> Unit,
|
||||||
goSend: () -> Unit,
|
goSend: () -> Unit,
|
||||||
|
goHistory: () -> Unit,
|
||||||
resetSdk: () -> Unit,
|
resetSdk: () -> Unit,
|
||||||
drawerState: DrawerState,
|
drawerState: DrawerState,
|
||||||
scope: CoroutineScope
|
scope: CoroutineScope
|
||||||
|
@ -145,14 +141,14 @@ fun Home(
|
||||||
)
|
)
|
||||||
}) { paddingValues ->
|
}) { paddingValues ->
|
||||||
HomeMainContent(
|
HomeMainContent(
|
||||||
walletSnapshot,
|
walletSnapshot = walletSnapshot,
|
||||||
transactionHistory,
|
|
||||||
isUpdateAvailable = isUpdateAvailable,
|
isUpdateAvailable = isUpdateAvailable,
|
||||||
isKeepScreenOnDuringSync = isKeepScreenOnDuringSync,
|
isKeepScreenOnDuringSync = isKeepScreenOnDuringSync,
|
||||||
isFiatConversionEnabled = isFiatConversionEnabled,
|
isFiatConversionEnabled = isFiatConversionEnabled,
|
||||||
isCircularProgressBarEnabled = isCircularProgressBarEnabled,
|
isCircularProgressBarEnabled = isCircularProgressBarEnabled,
|
||||||
goReceive = goReceive,
|
goReceive = goReceive,
|
||||||
goSend = goSend,
|
goSend = goSend,
|
||||||
|
goHistory = goHistory,
|
||||||
modifier = Modifier.padding(
|
modifier = Modifier.padding(
|
||||||
top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault,
|
top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault,
|
||||||
bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingDefault,
|
bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingDefault,
|
||||||
|
@ -296,13 +292,13 @@ private fun HomeDrawer(
|
||||||
@Composable
|
@Composable
|
||||||
private fun HomeMainContent(
|
private fun HomeMainContent(
|
||||||
walletSnapshot: WalletSnapshot,
|
walletSnapshot: WalletSnapshot,
|
||||||
transactionHistory: ImmutableList<TransactionOverview>,
|
|
||||||
isUpdateAvailable: Boolean,
|
isUpdateAvailable: Boolean,
|
||||||
isKeepScreenOnDuringSync: Boolean?,
|
isKeepScreenOnDuringSync: Boolean?,
|
||||||
isFiatConversionEnabled: Boolean,
|
isFiatConversionEnabled: Boolean,
|
||||||
isCircularProgressBarEnabled: Boolean,
|
isCircularProgressBarEnabled: Boolean,
|
||||||
goReceive: () -> Unit,
|
goReceive: () -> Unit,
|
||||||
goSend: () -> Unit,
|
goSend: () -> Unit,
|
||||||
|
goHistory: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
|
@ -339,7 +335,7 @@ private fun HomeMainContent(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
History(transactionHistory)
|
TertiaryButton(onClick = goHistory, text = stringResource(R.string.home_button_history))
|
||||||
|
|
||||||
if (isKeepScreenOnDuringSync == true && walletSnapshot.status == Synchronizer.Status.SYNCING) {
|
if (isKeepScreenOnDuringSync == true && walletSnapshot.status == Synchronizer.Status.SYNCING) {
|
||||||
DisableScreenTimeout()
|
DisableScreenTimeout()
|
||||||
|
@ -449,25 +445,3 @@ private fun Status(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
@Suppress("MagicNumber")
|
|
||||||
private fun History(transactionHistory: ImmutableList<TransactionOverview>) {
|
|
||||||
if (transactionHistory.isEmpty()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// here we need to use a fixed height to avoid nested columns vertical scrolling problem
|
|
||||||
// we'll refactor this part to a dedicated bottom sheet later
|
|
||||||
val historyPart = LocalConfiguration.current.screenHeightDp / 3
|
|
||||||
|
|
||||||
LazyColumn(
|
|
||||||
Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.height(historyPart.dp)
|
|
||||||
) {
|
|
||||||
items(transactionHistory) {
|
|
||||||
Text(it.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -28,9 +28,8 @@ 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.home.model.WalletSnapshot
|
import co.electriccoin.zcash.ui.screen.home.model.WalletSnapshot
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
|
||||||
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
|
||||||
|
@ -148,22 +147,6 @@ class WalletViewModel(application: Application) : AndroidViewModel(application)
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is not the right API, because the transaction list could be very long and might need UI filtering
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
|
||||||
val transactionSnapshot: StateFlow<ImmutableList<TransactionOverview>> = synchronizer
|
|
||||||
.flatMapLatest {
|
|
||||||
if (null == it) {
|
|
||||||
flowOf(persistentListOf())
|
|
||||||
} else {
|
|
||||||
it.transactions.map { list -> list.toPersistentList() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.stateIn(
|
|
||||||
viewModelScope,
|
|
||||||
SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
|
|
||||||
persistentListOf()
|
|
||||||
)
|
|
||||||
|
|
||||||
val addresses: StateFlow<WalletAddresses?> = synchronizer
|
val addresses: StateFlow<WalletAddresses?> = synchronizer
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
.map {
|
.map {
|
||||||
|
@ -174,6 +157,25 @@ class WalletViewModel(application: Application) : AndroidViewModel(application)
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
val transactionHistoryState = synchronizer
|
||||||
|
.filterNotNull()
|
||||||
|
.flatMapLatest {
|
||||||
|
it.transactions
|
||||||
|
.combine(it.status) { transactions: List<TransactionOverview>, status: Synchronizer.Status ->
|
||||||
|
if (status.isSyncing()) {
|
||||||
|
TransactionHistorySyncState.Syncing(transactions.toPersistentList())
|
||||||
|
} else {
|
||||||
|
TransactionHistorySyncState.Done(transactions.toPersistentList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.stateIn(
|
||||||
|
scope = viewModelScope,
|
||||||
|
started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
|
||||||
|
initialValue = TransactionHistorySyncState.Loading
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a wallet asynchronously and then persists it. Clients observe
|
* Creates a wallet asynchronously and then persists it. Clients observe
|
||||||
* [secretState] to see the side effects. This would be used for a user creating a new wallet.
|
* [secretState] to see the side effects. This would be used for a user creating a new wallet.
|
||||||
|
@ -347,3 +349,5 @@ private fun Synchronizer.toWalletSnapshot() =
|
||||||
flows[6] as SynchronizerError?
|
flows[6] as SynchronizerError?
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Synchronizer.Status.isSyncing() = this == Synchronizer.Status.SYNCING
|
||||||
|
|
|
@ -8,11 +8,11 @@ import androidx.activity.viewModels
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import cash.z.ecc.android.sdk.fixture.WalletFixture
|
||||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||||
import cash.z.ecc.android.sdk.model.PersistableWallet
|
import cash.z.ecc.android.sdk.model.PersistableWallet
|
||||||
import cash.z.ecc.android.sdk.model.SeedPhrase
|
import cash.z.ecc.android.sdk.model.SeedPhrase
|
||||||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||||
import cash.z.ecc.sdk.fixture.SeedPhraseFixture
|
|
||||||
import cash.z.ecc.sdk.type.fromResources
|
import cash.z.ecc.sdk.type.fromResources
|
||||||
import co.electriccoin.zcash.spackle.EmulatorWtfUtil
|
import co.electriccoin.zcash.spackle.EmulatorWtfUtil
|
||||||
import co.electriccoin.zcash.spackle.FirebaseTestLabUtil
|
import co.electriccoin.zcash.spackle.FirebaseTestLabUtil
|
||||||
|
@ -54,8 +54,8 @@ internal fun WrapOnboarding(
|
||||||
persistExistingWalletWithSeedPhrase(
|
persistExistingWalletWithSeedPhrase(
|
||||||
applicationContext,
|
applicationContext,
|
||||||
walletViewModel,
|
walletViewModel,
|
||||||
SeedPhraseFixture.new(),
|
SeedPhrase.new(WalletFixture.Alice.seedPhrase),
|
||||||
birthday = null
|
birthday = WalletFixture.Alice.getBirthday(ZcashNetwork.fromResources(applicationContext))
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
walletViewModel.persistNewWallet()
|
walletViewModel.persistNewWallet()
|
||||||
|
@ -71,8 +71,8 @@ internal fun WrapOnboarding(
|
||||||
persistExistingWalletWithSeedPhrase(
|
persistExistingWalletWithSeedPhrase(
|
||||||
applicationContext,
|
applicationContext,
|
||||||
walletViewModel,
|
walletViewModel,
|
||||||
SeedPhraseFixture.new(),
|
SeedPhrase.new(WalletFixture.Alice.seedPhrase),
|
||||||
birthday = null
|
birthday = WalletFixture.Alice.getBirthday(ZcashNetwork.fromResources(applicationContext))
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
onboardingViewModel.setIsImporting(true)
|
onboardingViewModel.setIsImporting(true)
|
||||||
|
@ -83,8 +83,8 @@ internal fun WrapOnboarding(
|
||||||
persistExistingWalletWithSeedPhrase(
|
persistExistingWalletWithSeedPhrase(
|
||||||
applicationContext,
|
applicationContext,
|
||||||
walletViewModel,
|
walletViewModel,
|
||||||
SeedPhraseFixture.new(),
|
SeedPhrase.new(WalletFixture.Alice.seedPhrase),
|
||||||
birthday = null
|
birthday = WalletFixture.Alice.getBirthday(ZcashNetwork.fromResources(applicationContext))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import cash.z.ecc.android.sdk.fixture.WalletAddressFixture
|
import cash.z.ecc.android.sdk.fixture.WalletAddressFixture
|
||||||
import cash.z.ecc.android.sdk.model.MonetarySeparators
|
import cash.z.ecc.android.sdk.model.MonetarySeparators
|
||||||
|
@ -42,8 +43,9 @@ import co.electriccoin.zcash.ui.design.component.PrimaryButton
|
||||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
|
@Preview("Request")
|
||||||
@Composable
|
@Composable
|
||||||
fun PreviewRequest() {
|
private fun PreviewRequest() {
|
||||||
ZcashTheme(darkTheme = true) {
|
ZcashTheme(darkTheme = true) {
|
||||||
GradientSurface {
|
GradientSurface {
|
||||||
Request(
|
Request(
|
||||||
|
@ -58,7 +60,6 @@ fun PreviewRequest() {
|
||||||
/**
|
/**
|
||||||
* @param myAddress The address that ZEC should be sent to.
|
* @param myAddress The address that ZEC should be sent to.
|
||||||
*/
|
*/
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Request(
|
fun Request(
|
||||||
myAddress: WalletAddress.Unified,
|
myAddress: WalletAddress.Unified,
|
||||||
|
@ -98,7 +99,6 @@ private fun RequestTopAppBar(onBack: () -> Unit) {
|
||||||
// TODO [#217]: Need to handle changing of Locale after user input, but before submitting the button.
|
// TODO [#217]: Need to handle changing of Locale after user input, but before submitting the button.
|
||||||
// TODO [#288]: TextField component can't do long-press backspace.
|
// TODO [#288]: TextField component can't do long-press backspace.
|
||||||
@Composable
|
@Composable
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
private fun RequestMainContent(
|
private fun RequestMainContent(
|
||||||
paddingValues: PaddingValues,
|
paddingValues: PaddingValues,
|
||||||
myAddress: WalletAddress.Unified,
|
myAddress: WalletAddress.Unified,
|
||||||
|
|
|
@ -17,6 +17,7 @@ data class TimeInfo(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// TODO [#388]: Consider fuzzing the times
|
// TODO [#388]: Consider fuzzing the times
|
||||||
|
// TODO [#388]: https://github.com/zcash/secant-android-wallet/issues/388
|
||||||
fun toSupportString() = buildString {
|
fun toSupportString() = buildString {
|
||||||
// Use a slightly more human friendly format instead of ISO, since this will appear in the emails that users see
|
// Use a slightly more human friendly format instead of ISO, since this will appear in the emails that users see
|
||||||
val dateFormat = SimpleDateFormat("yyyy-MM-dd hh:mm:ss a", Locale.US) // $NON-NLS-1$
|
val dateFormat = SimpleDateFormat("yyyy-MM-dd hh:mm:ss a", Locale.US) // $NON-NLS-1$
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
<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_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>
|
||||||
|
|
||||||
|
</resources>
|
|
@ -2,6 +2,7 @@
|
||||||
<string name="home_menu_content_description">Open menu</string>
|
<string name="home_menu_content_description">Open menu</string>
|
||||||
<string name="home_button_receive">Receive</string>
|
<string name="home_button_receive">Receive</string>
|
||||||
<string name="home_button_send">Send</string>
|
<string name="home_button_send">Send</string>
|
||||||
|
<string name="home_button_history">Transaction History</string>
|
||||||
<string name="home_information">You won’t be able to transfer funds until your wallet is finished syncing. Please keep your device plugged in and the app open.</string>
|
<string name="home_information">You won’t be able to transfer funds until your wallet is finished syncing. Please keep your device plugged in and the app open.</string>
|
||||||
|
|
||||||
<string name="home_menu_seed_phrase">My secret phrase</string>
|
<string name="home_menu_seed_phrase">My secret phrase</string>
|
||||||
|
|
|
@ -145,7 +145,7 @@ class ScreenshotTest : UiTestPrerequisites() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO [#859]: Screenshot tests fail on Firebase Test Lab
|
// TODO [#859]: Screenshot tests fail on Firebase Test Lab
|
||||||
// https://github.com/zcash/secant-android-wallet/issues/859
|
// TODO [#859]: https://github.com/zcash/secant-android-wallet/issues/859
|
||||||
// Some of the restore screenshots broke with the Compose 1.4 update and we don't yet know why.
|
// Some of the restore screenshots broke with the Compose 1.4 update and we don't yet know why.
|
||||||
private val isRestoreScreenshotsEnabled = false
|
private val isRestoreScreenshotsEnabled = false
|
||||||
|
|
||||||
|
@ -312,6 +312,9 @@ class ScreenshotTest : UiTestPrerequisites() {
|
||||||
|
|
||||||
navigateTo(NavigationTargets.WALLET_ADDRESS_DETAILS)
|
navigateTo(NavigationTargets.WALLET_ADDRESS_DETAILS)
|
||||||
addressDetailsScreenshots(resContext, tag, composeTestRule)
|
addressDetailsScreenshots(resContext, tag, composeTestRule)
|
||||||
|
|
||||||
|
navigateTo(NavigationTargets.HISTORY)
|
||||||
|
transactionHistoryScreenshots(resContext, tag, composeTestRule)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -534,6 +537,14 @@ private fun addressDetailsScreenshots(resContext: Context, tag: String, composeT
|
||||||
ScreenshotTest.takeScreenshot(tag, "Addresses 1")
|
ScreenshotTest.takeScreenshot(tag, "Addresses 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(resContext: Context, tag: String, composeTestRule: ComposeTestRule) {
|
private fun requestZecScreenshots(resContext: Context, tag: String, composeTestRule: ComposeTestRule) {
|
||||||
|
|
Loading…
Reference in New Issue