[#1165] Syncing progressbar
* [#1165] Syncing progressbar - UI + logic + tests - Closes #1165 * Changelog update
This commit is contained in:
parent
1058802b19
commit
cc333ea902
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -9,10 +9,18 @@ directly impact users rather than highlighting other key architectural updates.*
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Changed
|
||||
- Update the Zcash SDK dependency to version 2.0.6, which adds more details on current balances
|
||||
|
||||
### Added
|
||||
- The Balances screen now provides details on current balances like Change pending and Pending transactions
|
||||
- The screen also adds a new Block synchronization progress bar and status, which were initially part of the Account
|
||||
screen and redesigned
|
||||
|
||||
### Fixed
|
||||
- Fixed character replacement in Zcash addresses on the Receive screen caused by ligatures in the app's primary font
|
||||
using the secondary font. This will be revisited once a proper font is added.
|
||||
- Improved spacing of titles of bottom navigation tabs so they work better on smaller screens
|
||||
- Improved spacing of titles of bottom navigation tabs, so they work better on smaller screens
|
||||
|
||||
## [0.2.0 (541)] - 2024-01-30
|
||||
- Update the Zcash SDK dependency to version 2.0.5, which improves the performance of block synchronization
|
||||
|
|
|
@ -3,11 +3,15 @@ package co.electriccoin.zcash.ui.design.component
|
|||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
|
@ -35,7 +39,7 @@ fun CircularScreenProgressIndicator(modifier: Modifier = Modifier) {
|
|||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
color = ZcashTheme.colors.progressBarScreen,
|
||||
color = ZcashTheme.colors.circularProgressBarScreen,
|
||||
modifier =
|
||||
Modifier
|
||||
.size(ZcashTheme.dimens.circularScreenProgressWidth)
|
||||
|
@ -46,7 +50,7 @@ fun CircularScreenProgressIndicator(modifier: Modifier = Modifier) {
|
|||
@Composable
|
||||
fun CircularSmallProgressIndicator(modifier: Modifier = Modifier) {
|
||||
CircularProgressIndicator(
|
||||
color = ZcashTheme.colors.progressBarSmall,
|
||||
color = ZcashTheme.colors.circularProgressBarSmall,
|
||||
strokeWidth = 2.dp,
|
||||
modifier =
|
||||
Modifier
|
||||
|
@ -54,3 +58,32 @@ fun CircularSmallProgressIndicator(modifier: Modifier = Modifier) {
|
|||
.then(modifier)
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun LinearProgressIndicatorComposablePreview() {
|
||||
ZcashTheme(forceDarkMode = false) {
|
||||
GradientSurface {
|
||||
@Suppress("MagicNumber")
|
||||
LinearProgressIndicator(0.75f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LinearProgressIndicator(
|
||||
progress: Float,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
LinearProgressIndicator(
|
||||
progress = progress,
|
||||
color = ZcashTheme.colors.linearProgressBarBackground,
|
||||
trackColor = ZcashTheme.colors.linearProgressBarTrack,
|
||||
strokeCap = StrokeCap.Butt,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.height(ZcashTheme.dimens.linearProgressHeight)
|
||||
.then(modifier)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -125,6 +125,7 @@ fun BodySmall(
|
|||
maxLines: Int = Int.MAX_VALUE,
|
||||
overflow: TextOverflow = TextOverflow.Clip,
|
||||
textAlign: TextAlign = TextAlign.Start,
|
||||
textFontWeight: FontWeight = FontWeight.Normal,
|
||||
color: Color = MaterialTheme.colorScheme.onBackground,
|
||||
) {
|
||||
Text(
|
||||
|
@ -135,6 +136,7 @@ fun BodySmall(
|
|||
textAlign = textAlign,
|
||||
modifier = modifier,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = textFontWeight
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ data class Dimens(
|
|||
// Progress
|
||||
val circularScreenProgressWidth: Dp,
|
||||
val circularSmallProgressWidth: Dp,
|
||||
val linearProgressHeight: Dp,
|
||||
// TopAppBar:
|
||||
val topAppBarZcashLogoHeight: Dp,
|
||||
val topAppBarActionRippleCorner: Dp,
|
||||
|
@ -67,6 +68,7 @@ private val defaultDimens =
|
|||
chipStroke = 0.5.dp,
|
||||
circularScreenProgressWidth = 48.dp,
|
||||
circularSmallProgressWidth = 14.dp,
|
||||
linearProgressHeight = 14.dp,
|
||||
topAppBarZcashLogoHeight = 24.dp,
|
||||
topAppBarActionRippleCorner = 28.dp,
|
||||
textFieldDefaultHeight = 64.dp,
|
||||
|
|
|
@ -14,8 +14,10 @@ data class ExtendedColors(
|
|||
val onTertiary: Color,
|
||||
val callout: Color,
|
||||
val onCallout: Color,
|
||||
val progressBarSmall: Color,
|
||||
val progressBarScreen: Color,
|
||||
val circularProgressBarSmall: Color,
|
||||
val circularProgressBarScreen: Color,
|
||||
val linearProgressBarTrack: Color,
|
||||
val linearProgressBarBackground: Color,
|
||||
val chipIndex: Color,
|
||||
val textCommon: Color,
|
||||
val textFieldHint: Color,
|
||||
|
@ -37,6 +39,7 @@ data class ExtendedColors(
|
|||
val dividerColor: Color,
|
||||
val darkDividerColor: Color,
|
||||
val tabTextColor: Color,
|
||||
val panelBackgroundColor: Color,
|
||||
) {
|
||||
@Composable
|
||||
fun surfaceGradient() =
|
||||
|
|
|
@ -30,7 +30,15 @@ internal object Dark {
|
|||
val textDescription = Color(0xFF777777)
|
||||
val textProgress = Color(0xFF8B8A8A)
|
||||
|
||||
val aboutTextColor = Color.Unspecified
|
||||
val screenTitleColor = Color(0xFF040404)
|
||||
val welcomeAnimationColor = Color(0xFF231F20)
|
||||
val complementaryColor = Color(0xFFF4B728)
|
||||
val dividerColor = Color(0xFFDDDDDD)
|
||||
val darkDividerColor = Color(0xFF000000)
|
||||
val tabTextColor = Color(0xFF040404)
|
||||
val layoutStroke = Color(0xFFFFFFFF)
|
||||
val panelBackgroundColor = Color(0xFFEAEAEA)
|
||||
|
||||
val primaryButton = Color(0xFFFFFFFF)
|
||||
val primaryButtonPressed = Color(0xFFFFFFFF)
|
||||
|
@ -46,8 +54,10 @@ internal object Dark {
|
|||
val navigationButton = Color(0xFFFFFFFF)
|
||||
val navigationButtonPressed = Color(0xFFFFFFFF)
|
||||
|
||||
val progressBarSmall = Color(0xFF8B8A8A)
|
||||
val progressBarScreen = Color(0xFFFFFFFF)
|
||||
val circularProgressBarSmall = Color(0xFF8B8A8A)
|
||||
val circularProgressBarScreen = Color(0xFFFFFFFF)
|
||||
val linearProgressBarTrack = Color(0xFFD9D9D9)
|
||||
val linearProgressBarBackground = Light.complementaryColor
|
||||
|
||||
val callout = Color(0xFFFFFFFF)
|
||||
val onCallout = Color(0xFFFFFFFF)
|
||||
|
@ -64,15 +74,6 @@ internal object Dark {
|
|||
val disabledButtonTextColor = Color(0xFFDDDDDD)
|
||||
|
||||
val buttonShadowColor = Color(0xFFFFFFFF)
|
||||
|
||||
// Proper values will be added later, see #998
|
||||
val aboutTextColor = Color.Unspecified
|
||||
val screenTitleColor = Color(0xFF040404)
|
||||
val welcomeAnimationColor = Color(0xFF231F20)
|
||||
val complementaryColor = Color(0xFFF4B728)
|
||||
val dividerColor = Color(0xFFDDDDDD)
|
||||
val darkDividerColor = Color(0xFF000000)
|
||||
val tabTextColor = Color(0xFF040404)
|
||||
}
|
||||
|
||||
internal object Light {
|
||||
|
@ -91,7 +92,15 @@ internal object Light {
|
|||
val textDescription = Color(0xFF777777)
|
||||
val textProgress = Color(0xFF8B8A8A)
|
||||
|
||||
val screenTitleColor = Color(0xFF040404)
|
||||
val aboutTextColor = Color(0xFF4E4E4E)
|
||||
val welcomeAnimationColor = Color(0xFF231F20)
|
||||
val complementaryColor = Color(0xFFF4B728)
|
||||
val dividerColor = Color(0xFFDDDDDD)
|
||||
val darkDividerColor = Color(0xFF000000)
|
||||
val tabTextColor = Color(0xFF040404)
|
||||
val layoutStroke = Color(0xFF000000)
|
||||
val panelBackgroundColor = Color(0xFFEAEAEA)
|
||||
|
||||
val primaryButton = Color(0xFF000000)
|
||||
val primaryButtonPressed = Color(0xFF000000)
|
||||
|
@ -107,8 +116,10 @@ internal object Light {
|
|||
val navigationButton = Color(0xFFFFFFFF)
|
||||
val navigationButtonPressed = Color(0xFFFFFFFF)
|
||||
|
||||
val progressBarSmall = Color(0xFF8B8A8A)
|
||||
val progressBarScreen = Color(0xFF000000)
|
||||
val circularProgressBarSmall = Color(0xFF8B8A8A)
|
||||
val circularProgressBarScreen = Color(0xFF000000)
|
||||
val linearProgressBarTrack = Color(0xFFD9D9D9)
|
||||
val linearProgressBarBackground = complementaryColor
|
||||
|
||||
val callout = Color(0xFFFFFFFF)
|
||||
val onCallout = Color(0xFFFFFFFF)
|
||||
|
@ -124,14 +135,6 @@ internal object Light {
|
|||
val disabledButtonColor = Color(0xFFB7B7B7)
|
||||
val disabledButtonTextColor = Color(0xFFDDDDDD)
|
||||
val buttonShadowColor = Color(0xFF000000)
|
||||
|
||||
val screenTitleColor = Color(0xFF040404)
|
||||
val aboutTextColor = Color(0xFF4E4E4E)
|
||||
val welcomeAnimationColor = Color(0xFF231F20)
|
||||
val complementaryColor = Color(0xFFF4B728)
|
||||
val dividerColor = Color(0xFFDDDDDD)
|
||||
val darkDividerColor = Color(0xFF000000)
|
||||
val tabTextColor = Color(0xFF040404)
|
||||
}
|
||||
|
||||
internal val DarkColorPalette =
|
||||
|
@ -166,8 +169,10 @@ internal val DarkExtendedColorPalette =
|
|||
onTertiary = Dark.textTertiaryButton,
|
||||
callout = Dark.callout,
|
||||
onCallout = Dark.onCallout,
|
||||
progressBarSmall = Dark.progressBarSmall,
|
||||
progressBarScreen = Dark.progressBarScreen,
|
||||
circularProgressBarSmall = Dark.circularProgressBarSmall,
|
||||
circularProgressBarScreen = Dark.circularProgressBarScreen,
|
||||
linearProgressBarTrack = Dark.linearProgressBarTrack,
|
||||
linearProgressBarBackground = Dark.linearProgressBarBackground,
|
||||
chipIndex = Dark.textChipIndex,
|
||||
textCommon = Dark.textCommon,
|
||||
textFieldHint = Dark.textFieldHint,
|
||||
|
@ -189,6 +194,7 @@ internal val DarkExtendedColorPalette =
|
|||
dividerColor = Dark.dividerColor,
|
||||
darkDividerColor = Dark.darkDividerColor,
|
||||
tabTextColor = Dark.tabTextColor,
|
||||
panelBackgroundColor = Dark.panelBackgroundColor,
|
||||
)
|
||||
|
||||
internal val LightExtendedColorPalette =
|
||||
|
@ -199,8 +205,10 @@ internal val LightExtendedColorPalette =
|
|||
onTertiary = Light.textTertiaryButton,
|
||||
callout = Light.callout,
|
||||
onCallout = Light.onCallout,
|
||||
progressBarScreen = Light.progressBarScreen,
|
||||
progressBarSmall = Light.progressBarSmall,
|
||||
circularProgressBarScreen = Light.circularProgressBarScreen,
|
||||
circularProgressBarSmall = Light.circularProgressBarSmall,
|
||||
linearProgressBarTrack = Light.linearProgressBarTrack,
|
||||
linearProgressBarBackground = Light.linearProgressBarBackground,
|
||||
chipIndex = Light.textChipIndex,
|
||||
textCommon = Light.textCommon,
|
||||
textFieldHint = Light.textFieldHint,
|
||||
|
@ -222,6 +230,7 @@ internal val LightExtendedColorPalette =
|
|||
dividerColor = Light.dividerColor,
|
||||
darkDividerColor = Light.darkDividerColor,
|
||||
tabTextColor = Light.tabTextColor,
|
||||
panelBackgroundColor = Light.panelBackgroundColor,
|
||||
)
|
||||
|
||||
@Suppress("CompositionLocalAllowlist")
|
||||
|
@ -234,8 +243,10 @@ internal val LocalExtendedColors =
|
|||
onTertiary = Color.Unspecified,
|
||||
callout = Color.Unspecified,
|
||||
onCallout = Color.Unspecified,
|
||||
progressBarScreen = Color.Unspecified,
|
||||
progressBarSmall = Color.Unspecified,
|
||||
circularProgressBarScreen = Color.Unspecified,
|
||||
circularProgressBarSmall = Color.Unspecified,
|
||||
linearProgressBarTrack = Color.Unspecified,
|
||||
linearProgressBarBackground = Color.Unspecified,
|
||||
chipIndex = Color.Unspecified,
|
||||
textCommon = Color.Unspecified,
|
||||
textFieldHint = Color.Unspecified,
|
||||
|
@ -256,6 +267,7 @@ internal val LocalExtendedColors =
|
|||
complementaryColor = Color.Unspecified,
|
||||
dividerColor = Color.Unspecified,
|
||||
darkDividerColor = Color.Unspecified,
|
||||
tabTextColor = Color.Unspecified
|
||||
tabTextColor = Color.Unspecified,
|
||||
panelBackgroundColor = Color.Unspecified,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import java.util.concurrent.atomic.AtomicInteger
|
|||
class AccountTestSetup(
|
||||
private val composeTestRule: ComposeContentTestRule,
|
||||
private val walletSnapshot: WalletSnapshot,
|
||||
private val isShowFiatConversion: Boolean
|
||||
) {
|
||||
private val onSettingsCount = AtomicInteger(0)
|
||||
private val onReceiveCount = AtomicInteger(0)
|
||||
|
@ -46,10 +45,8 @@ class AccountTestSetup(
|
|||
@Suppress("TestFunctionName")
|
||||
fun DefaultContent() {
|
||||
Account(
|
||||
walletSnapshot,
|
||||
isUpdateAvailable = false,
|
||||
isKeepScreenOnDuringSync = false,
|
||||
isFiatConversionEnabled = isShowFiatConversion,
|
||||
walletSnapshot = walletSnapshot,
|
||||
isKeepScreenOnWhileSyncing = false,
|
||||
goSettings = {
|
||||
onSettingsCount.incrementAndGet()
|
||||
},
|
||||
|
|
|
@ -7,15 +7,12 @@ import androidx.compose.ui.test.junit4.createComposeRule
|
|||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.test.filters.MediumTest
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.model.PercentDecimal
|
||||
import co.electriccoin.zcash.test.UiTestPrerequisites
|
||||
import co.electriccoin.zcash.ui.common.model.WalletSnapshot
|
||||
import co.electriccoin.zcash.ui.fixture.WalletSnapshotFixture
|
||||
import co.electriccoin.zcash.ui.screen.account.AccountTag
|
||||
import co.electriccoin.zcash.ui.screen.account.AccountTestSetup
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotEquals
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
|
@ -27,7 +24,6 @@ class AccountViewIntegrationTest : UiTestPrerequisites() {
|
|||
AccountTestSetup(
|
||||
composeTestRule,
|
||||
walletSnapshot,
|
||||
isShowFiatConversion = false
|
||||
)
|
||||
|
||||
// This is just basic sanity check that we still have UI set up as expected after the state restore
|
||||
|
@ -37,8 +33,9 @@ class AccountViewIntegrationTest : UiTestPrerequisites() {
|
|||
val restorationTester = StateRestorationTester(composeTestRule)
|
||||
val walletSnapshot =
|
||||
WalletSnapshotFixture.new(
|
||||
status = Synchronizer.Status.SYNCING,
|
||||
progress = PercentDecimal(0.5f)
|
||||
saplingBalance = WalletSnapshotFixture.SAPLING_BALANCE,
|
||||
orchardBalance = WalletSnapshotFixture.ORCHARD_BALANCE,
|
||||
transparentBalance = WalletSnapshotFixture.TRANSPARENT_BALANCE
|
||||
)
|
||||
val testSetup = newTestSetup(walletSnapshot)
|
||||
|
||||
|
@ -46,21 +43,17 @@ class AccountViewIntegrationTest : UiTestPrerequisites() {
|
|||
testSetup.DefaultContent()
|
||||
}
|
||||
|
||||
assertNotEquals(WalletSnapshotFixture.STATUS, testSetup.getWalletSnapshot().status)
|
||||
assertEquals(Synchronizer.Status.SYNCING, testSetup.getWalletSnapshot().status)
|
||||
|
||||
assertNotEquals(WalletSnapshotFixture.PROGRESS, testSetup.getWalletSnapshot().progress)
|
||||
assertEquals(0.5f, testSetup.getWalletSnapshot().progress.decimal)
|
||||
assertEquals(WalletSnapshotFixture.SAPLING_BALANCE, testSetup.getWalletSnapshot().saplingBalance)
|
||||
assertEquals(WalletSnapshotFixture.ORCHARD_BALANCE, testSetup.getWalletSnapshot().orchardBalance)
|
||||
assertEquals(WalletSnapshotFixture.TRANSPARENT_BALANCE, testSetup.getWalletSnapshot().transparentBalance)
|
||||
|
||||
restorationTester.emulateSavedInstanceStateRestore()
|
||||
|
||||
assertNotEquals(WalletSnapshotFixture.STATUS, testSetup.getWalletSnapshot().status)
|
||||
assertEquals(Synchronizer.Status.SYNCING, testSetup.getWalletSnapshot().status)
|
||||
assertEquals(WalletSnapshotFixture.SAPLING_BALANCE, testSetup.getWalletSnapshot().saplingBalance)
|
||||
assertEquals(WalletSnapshotFixture.ORCHARD_BALANCE, testSetup.getWalletSnapshot().orchardBalance)
|
||||
assertEquals(WalletSnapshotFixture.TRANSPARENT_BALANCE, testSetup.getWalletSnapshot().transparentBalance)
|
||||
|
||||
assertNotEquals(WalletSnapshotFixture.PROGRESS, testSetup.getWalletSnapshot().progress)
|
||||
assertEquals(0.5f, testSetup.getWalletSnapshot().progress.decimal)
|
||||
|
||||
composeTestRule.onNodeWithTag(AccountTag.SINGLE_LINE_TEXT).also {
|
||||
composeTestRule.onNodeWithTag(AccountTag.BALANCE_VIEWS).also {
|
||||
it.assertIsDisplayed()
|
||||
it.assertWidthIsAtLeast(1.dp)
|
||||
}
|
||||
|
|
|
@ -38,11 +38,7 @@ class AccountViewTest : UiTestPrerequisites() {
|
|||
it.assertIsDisplayed()
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithTag(AccountTag.STATUS_VIEWS).also {
|
||||
it.assertIsDisplayed()
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithTag(AccountTag.FIAT_CONVERSION).also {
|
||||
composeTestRule.onNodeWithTag(AccountTag.BALANCE_VIEWS).also {
|
||||
it.assertIsDisplayed()
|
||||
}
|
||||
|
||||
|
@ -51,16 +47,6 @@ class AccountViewTest : UiTestPrerequisites() {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun hide_fiat_conversion() {
|
||||
newTestSetup(isShowFiatConversion = false)
|
||||
|
||||
composeTestRule.onNodeWithTag(AccountTag.FIAT_CONVERSION).also {
|
||||
it.assertDoesNotExist()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun click_history_button() {
|
||||
|
@ -78,23 +64,20 @@ class AccountViewTest : UiTestPrerequisites() {
|
|||
fun hamburger_settings_test() {
|
||||
val testSetup = newTestSetup()
|
||||
|
||||
Assert.assertEquals(0, testSetup.getOnReceiveCount())
|
||||
Assert.assertEquals(0, testSetup.getOnSettingsCount())
|
||||
|
||||
composeTestRule.clickSettingsTopAppBarMenu()
|
||||
|
||||
Assert.assertEquals(1, testSetup.getOnSettingsCount())
|
||||
}
|
||||
|
||||
private fun newTestSetup(
|
||||
isShowFiatConversion: Boolean = true,
|
||||
walletSnapshot: WalletSnapshot = WalletSnapshotFixture.new()
|
||||
) = AccountTestSetup(
|
||||
composeTestRule,
|
||||
walletSnapshot = walletSnapshot,
|
||||
isShowFiatConversion = isShowFiatConversion
|
||||
).apply {
|
||||
setDefaultContent()
|
||||
}
|
||||
private fun newTestSetup(walletSnapshot: WalletSnapshot = WalletSnapshotFixture.new()) =
|
||||
AccountTestSetup(
|
||||
composeTestRule,
|
||||
walletSnapshot = walletSnapshot,
|
||||
).apply {
|
||||
setDefaultContent()
|
||||
}
|
||||
}
|
||||
|
||||
private fun ComposeContentTestRule.clickHistory() {
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package co.electriccoin.zcash.ui.screen.balances
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
||||
import co.electriccoin.zcash.ui.common.model.WalletSnapshot
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
import co.electriccoin.zcash.ui.screen.balances.view.Balances
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class BalancesTestSetup(
|
||||
private val composeTestRule: ComposeContentTestRule,
|
||||
private val walletSnapshot: WalletSnapshot,
|
||||
private val isShowFiatConversion: Boolean
|
||||
) {
|
||||
private val onSettingsCount = AtomicInteger(0)
|
||||
|
||||
fun getOnSettingsCount(): Int {
|
||||
composeTestRule.waitForIdle()
|
||||
return onSettingsCount.get()
|
||||
}
|
||||
|
||||
fun getWalletSnapshot(): WalletSnapshot {
|
||||
composeTestRule.waitForIdle()
|
||||
return walletSnapshot
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Suppress("TestFunctionName")
|
||||
fun DefaultContent() {
|
||||
Balances(
|
||||
isFiatConversionEnabled = isShowFiatConversion,
|
||||
isKeepScreenOnWhileSyncing = false,
|
||||
isUpdateAvailable = false,
|
||||
onSettings = {
|
||||
onSettingsCount.incrementAndGet()
|
||||
},
|
||||
walletSnapshot = walletSnapshot,
|
||||
)
|
||||
}
|
||||
|
||||
fun setDefaultContent() {
|
||||
composeTestRule.setContent {
|
||||
ZcashTheme {
|
||||
DefaultContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package co.electriccoin.zcash.ui.screen.balances.integration
|
||||
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.assertWidthIsAtLeast
|
||||
import androidx.compose.ui.test.junit4.StateRestorationTester
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.test.filters.MediumTest
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.model.PercentDecimal
|
||||
import co.electriccoin.zcash.test.UiTestPrerequisites
|
||||
import co.electriccoin.zcash.ui.common.model.WalletSnapshot
|
||||
import co.electriccoin.zcash.ui.fixture.WalletSnapshotFixture
|
||||
import co.electriccoin.zcash.ui.screen.balances.BalancesTag
|
||||
import co.electriccoin.zcash.ui.screen.balances.BalancesTestSetup
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotEquals
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class BalancesViewIntegrationTest : UiTestPrerequisites() {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private fun newTestSetup(walletSnapshot: WalletSnapshot) =
|
||||
BalancesTestSetup(
|
||||
composeTestRule,
|
||||
walletSnapshot,
|
||||
isShowFiatConversion = true
|
||||
)
|
||||
|
||||
// This is just basic sanity check that we still have UI set up as expected after the state restore
|
||||
@Test
|
||||
@MediumTest
|
||||
fun wallet_snapshot_restoration() {
|
||||
val restorationTester = StateRestorationTester(composeTestRule)
|
||||
val walletSnapshot =
|
||||
WalletSnapshotFixture.new(
|
||||
status = Synchronizer.Status.SYNCING,
|
||||
progress = PercentDecimal(0.5f)
|
||||
)
|
||||
val testSetup = newTestSetup(walletSnapshot)
|
||||
|
||||
restorationTester.setContent {
|
||||
testSetup.DefaultContent()
|
||||
}
|
||||
|
||||
assertNotEquals(WalletSnapshotFixture.STATUS, testSetup.getWalletSnapshot().status)
|
||||
assertEquals(Synchronizer.Status.SYNCING, testSetup.getWalletSnapshot().status)
|
||||
|
||||
assertNotEquals(WalletSnapshotFixture.PROGRESS, testSetup.getWalletSnapshot().progress)
|
||||
assertEquals(0.5f, testSetup.getWalletSnapshot().progress.decimal)
|
||||
|
||||
restorationTester.emulateSavedInstanceStateRestore()
|
||||
|
||||
assertNotEquals(WalletSnapshotFixture.STATUS, testSetup.getWalletSnapshot().status)
|
||||
assertEquals(Synchronizer.Status.SYNCING, testSetup.getWalletSnapshot().status)
|
||||
|
||||
assertNotEquals(WalletSnapshotFixture.PROGRESS, testSetup.getWalletSnapshot().progress)
|
||||
assertEquals(0.5f, testSetup.getWalletSnapshot().progress.decimal)
|
||||
|
||||
composeTestRule.onNodeWithTag(BalancesTag.STATUS).also {
|
||||
it.assertIsDisplayed()
|
||||
it.assertWidthIsAtLeast(1.dp)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package co.electriccoin.zcash.ui.screen.account.model
|
||||
package co.electriccoin.zcash.ui.screen.balances.model
|
||||
|
||||
import androidx.test.filters.SmallTest
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
|
@ -37,7 +37,7 @@ class WalletDisplayValuesTest {
|
|||
assertNotNull(values)
|
||||
assertEquals(1f, values.progress.decimal)
|
||||
assertEquals(walletSnapshot.totalBalance().toZecString(), values.zecAmountText)
|
||||
assertTrue(values.statusText.startsWith(getStringResource(R.string.account_status_syncing_catchup)))
|
||||
assertTrue(values.statusText.startsWith(getStringResource(R.string.balances_status_syncing)))
|
||||
// TODO [#578]: Provide Zatoshi -> USD fiat currency formatting
|
||||
// TODO [#578]: https://github.com/Electric-Coin-Company/zcash-android-wallet-sdk/issues/578
|
||||
assertEquals(FiatCurrencyConversionRateState.Unavailable, values.fiatCurrencyAmountState)
|
|
@ -0,0 +1,69 @@
|
|||
package co.electriccoin.zcash.ui.screen.balances.view
|
||||
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.test.filters.MediumTest
|
||||
import co.electriccoin.zcash.test.UiTestPrerequisites
|
||||
import co.electriccoin.zcash.ui.common.model.WalletSnapshot
|
||||
import co.electriccoin.zcash.ui.design.component.CommonTag
|
||||
import co.electriccoin.zcash.ui.fixture.WalletSnapshotFixture
|
||||
import co.electriccoin.zcash.ui.screen.balances.BalancesTag
|
||||
import co.electriccoin.zcash.ui.screen.balances.BalancesTestSetup
|
||||
import co.electriccoin.zcash.ui.screen.send.clickSettingsTopAppBarMenu
|
||||
import org.junit.Assert
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import kotlin.test.DefaultAsserter.assertEquals
|
||||
|
||||
// TODO [#1227]: Cover Balances UI and logic with tests
|
||||
// TODO [#1227]: https://github.com/Electric-Coin-Company/zashi-android/issues/1227
|
||||
|
||||
class BalancesViewTest : UiTestPrerequisites() {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun check_all_elementary_ui_elements_displayed() {
|
||||
newTestSetup()
|
||||
|
||||
composeTestRule.onNodeWithTag(CommonTag.TOP_APP_BAR)
|
||||
.also {
|
||||
it.assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun hide_fiat_conversion() {
|
||||
newTestSetup(isShowFiatConversion = false)
|
||||
|
||||
composeTestRule.onNodeWithTag(BalancesTag.FIAT_CONVERSION).also {
|
||||
it.assertDoesNotExist()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun hamburger_settings_test() {
|
||||
val testSetup = newTestSetup()
|
||||
|
||||
assertEquals("Failed in comparison", 0, testSetup.getOnSettingsCount())
|
||||
|
||||
composeTestRule.clickSettingsTopAppBarMenu()
|
||||
|
||||
Assert.assertEquals(1, testSetup.getOnSettingsCount())
|
||||
}
|
||||
|
||||
private fun newTestSetup(
|
||||
isShowFiatConversion: Boolean = true,
|
||||
walletSnapshot: WalletSnapshot = WalletSnapshotFixture.new()
|
||||
) = BalancesTestSetup(
|
||||
composeTestRule,
|
||||
walletSnapshot = walletSnapshot,
|
||||
isShowFiatConversion = isShowFiatConversion
|
||||
).apply {
|
||||
setDefaultContent()
|
||||
}
|
||||
}
|
|
@ -4,7 +4,5 @@ package co.electriccoin.zcash.ui.screen.account
|
|||
* These are only used for automated testing.
|
||||
*/
|
||||
object AccountTag {
|
||||
const val STATUS_VIEWS = "status_views"
|
||||
const val SINGLE_LINE_TEXT = "single_line_text"
|
||||
const val FIAT_CONVERSION = "fiat_conversion"
|
||||
const val BALANCE_VIEWS = "balance_views"
|
||||
}
|
||||
|
|
|
@ -6,15 +6,10 @@ import androidx.activity.ComponentActivity
|
|||
import androidx.activity.viewModels
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.CheckUpdateViewModel
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
|
||||
import co.electriccoin.zcash.ui.configuration.ConfigurationEntries
|
||||
import co.electriccoin.zcash.ui.configuration.RemoteConfig
|
||||
import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator
|
||||
import co.electriccoin.zcash.ui.screen.account.view.Account
|
||||
import co.electriccoin.zcash.ui.screen.settings.viewmodel.SettingsViewModel
|
||||
import co.electriccoin.zcash.ui.screen.update.AppUpdateCheckerImp
|
||||
import co.electriccoin.zcash.ui.screen.update.model.UpdateState
|
||||
|
||||
@Composable
|
||||
internal fun WrapAccount(
|
||||
|
@ -23,25 +18,12 @@ internal fun WrapAccount(
|
|||
goBalances: () -> Unit,
|
||||
goSettings: () -> Unit,
|
||||
) {
|
||||
// Show information about the app update, if available
|
||||
val checkUpdateViewModel by activity.viewModels<CheckUpdateViewModel> {
|
||||
CheckUpdateViewModel.CheckUpdateViewModelFactory(
|
||||
activity.application,
|
||||
AppUpdateCheckerImp.new()
|
||||
)
|
||||
}
|
||||
val updateAvailable =
|
||||
checkUpdateViewModel.updateInfo.collectAsStateWithLifecycle().value.let {
|
||||
it?.appUpdateInfo != null && it.state == UpdateState.Prepared
|
||||
}
|
||||
|
||||
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||
val walletSnapshot = walletViewModel.walletSnapshot.collectAsStateWithLifecycle().value
|
||||
|
||||
val settingsViewModel by activity.viewModels<SettingsViewModel>()
|
||||
|
||||
val isKeepScreenOnWhileSyncing = settingsViewModel.isKeepScreenOnWhileSyncing.collectAsStateWithLifecycle().value
|
||||
val isFiatConversionEnabled = ConfigurationEntries.IS_FIAT_CONVERSION_ENABLED.getValue(RemoteConfig.current)
|
||||
|
||||
if (null == walletSnapshot) {
|
||||
// TODO [#1146]: Consider moving CircularScreenProgressIndicator from Android layer to View layer
|
||||
|
@ -51,9 +33,7 @@ internal fun WrapAccount(
|
|||
} else {
|
||||
Account(
|
||||
walletSnapshot = walletSnapshot,
|
||||
isUpdateAvailable = updateAvailable,
|
||||
isKeepScreenOnDuringSync = isKeepScreenOnWhileSyncing,
|
||||
isFiatConversionEnabled = isFiatConversionEnabled,
|
||||
isKeepScreenOnWhileSyncing = isKeepScreenOnWhileSyncing,
|
||||
goBalances = goBalances,
|
||||
goHistory = goHistory,
|
||||
goSettings = goSettings,
|
||||
|
|
|
@ -14,28 +14,23 @@ import androidx.compose.material3.Scaffold
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.model.FiatCurrencyConversionRateState
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.BalanceWidget
|
||||
import co.electriccoin.zcash.ui.common.DisableScreenTimeout
|
||||
import co.electriccoin.zcash.ui.common.model.WalletSnapshot
|
||||
import co.electriccoin.zcash.ui.common.test.CommonTag
|
||||
import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT
|
||||
import co.electriccoin.zcash.ui.design.component.Body
|
||||
import co.electriccoin.zcash.ui.design.component.BodyWithFiatCurrencySymbol
|
||||
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.theme.ZcashTheme
|
||||
import co.electriccoin.zcash.ui.fixture.WalletSnapshotFixture
|
||||
import co.electriccoin.zcash.ui.screen.account.AccountTag
|
||||
import co.electriccoin.zcash.ui.screen.account.model.WalletDisplayValues
|
||||
|
||||
@Preview("Account")
|
||||
@Composable
|
||||
|
@ -44,9 +39,7 @@ private fun ComposablePreview() {
|
|||
GradientSurface {
|
||||
Account(
|
||||
walletSnapshot = WalletSnapshotFixture.new(),
|
||||
isUpdateAvailable = false,
|
||||
isKeepScreenOnDuringSync = false,
|
||||
isFiatConversionEnabled = false,
|
||||
isKeepScreenOnWhileSyncing = false,
|
||||
goHistory = {},
|
||||
goBalances = {},
|
||||
goSettings = {},
|
||||
|
@ -55,13 +48,10 @@ private fun ComposablePreview() {
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
fun Account(
|
||||
walletSnapshot: WalletSnapshot,
|
||||
isUpdateAvailable: Boolean,
|
||||
isKeepScreenOnDuringSync: Boolean?,
|
||||
isFiatConversionEnabled: Boolean,
|
||||
isKeepScreenOnWhileSyncing: Boolean?,
|
||||
goBalances: () -> Unit,
|
||||
goHistory: () -> Unit,
|
||||
goSettings: () -> Unit,
|
||||
|
@ -71,9 +61,7 @@ fun Account(
|
|||
}) { paddingValues ->
|
||||
AccountMainContent(
|
||||
walletSnapshot = walletSnapshot,
|
||||
isUpdateAvailable = isUpdateAvailable,
|
||||
isKeepScreenOnDuringSync = isKeepScreenOnDuringSync,
|
||||
isFiatConversionEnabled = isFiatConversionEnabled,
|
||||
isKeepScreenOnWhileSyncing = isKeepScreenOnWhileSyncing,
|
||||
goHistory = goHistory,
|
||||
goBalances = goBalances,
|
||||
modifier =
|
||||
|
@ -105,13 +93,10 @@ private fun AccountTopAppBar(onSettings: () -> Unit) {
|
|||
)
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
private fun AccountMainContent(
|
||||
walletSnapshot: WalletSnapshot,
|
||||
isUpdateAvailable: Boolean,
|
||||
isKeepScreenOnDuringSync: Boolean?,
|
||||
isFiatConversionEnabled: Boolean,
|
||||
isKeepScreenOnWhileSyncing: Boolean?,
|
||||
goBalances: () -> Unit,
|
||||
goHistory: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
|
@ -125,7 +110,7 @@ private fun AccountMainContent(
|
|||
) {
|
||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))
|
||||
|
||||
Status(walletSnapshot, isUpdateAvailable, isFiatConversionEnabled, goBalances)
|
||||
BalancesStatus(walletSnapshot, goBalances)
|
||||
|
||||
Spacer(
|
||||
modifier =
|
||||
|
@ -138,72 +123,29 @@ private fun AccountMainContent(
|
|||
|
||||
PrimaryButton(onClick = goHistory, text = stringResource(R.string.account_button_history))
|
||||
|
||||
if (isKeepScreenOnDuringSync == true && walletSnapshot.status == Synchronizer.Status.SYNCING) {
|
||||
if (isKeepScreenOnWhileSyncing == true && walletSnapshot.status == Synchronizer.Status.SYNCING) {
|
||||
DisableScreenTimeout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Suppress("LongMethod", "MagicNumber")
|
||||
private fun Status(
|
||||
@Suppress("LongMethod")
|
||||
private fun BalancesStatus(
|
||||
walletSnapshot: WalletSnapshot,
|
||||
updateAvailable: Boolean,
|
||||
isFiatConversionEnabled: Boolean,
|
||||
goBalances: () -> Unit
|
||||
) {
|
||||
val walletDisplayValues =
|
||||
WalletDisplayValues.getNextValues(
|
||||
LocalContext.current,
|
||||
walletSnapshot,
|
||||
updateAvailable
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.testTag(AccountTag.STATUS_VIEWS),
|
||||
.testTag(AccountTag.BALANCE_VIEWS),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
if (walletDisplayValues.zecAmountText.isNotEmpty()) {
|
||||
BalanceWidget(
|
||||
walletSnapshot = walletSnapshot,
|
||||
isReferenceToBalances = true,
|
||||
onReferenceClick = goBalances
|
||||
)
|
||||
}
|
||||
|
||||
if (isFiatConversionEnabled) {
|
||||
Column(Modifier.testTag(AccountTag.FIAT_CONVERSION)) {
|
||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall))
|
||||
|
||||
when (walletDisplayValues.fiatCurrencyAmountState) {
|
||||
is FiatCurrencyConversionRateState.Current -> {
|
||||
BodyWithFiatCurrencySymbol(
|
||||
amount = walletDisplayValues.fiatCurrencyAmountText
|
||||
)
|
||||
}
|
||||
is FiatCurrencyConversionRateState.Stale -> {
|
||||
// Note: we should show information about staleness too
|
||||
BodyWithFiatCurrencySymbol(
|
||||
amount = walletDisplayValues.fiatCurrencyAmountText
|
||||
)
|
||||
}
|
||||
is FiatCurrencyConversionRateState.Unavailable -> {
|
||||
Body(text = walletDisplayValues.fiatCurrencyAmountText)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
|
||||
|
||||
if (walletDisplayValues.statusText.isNotEmpty()) {
|
||||
Body(
|
||||
text = walletDisplayValues.statusText,
|
||||
modifier = Modifier.testTag(AccountTag.SINGLE_LINE_TEXT)
|
||||
)
|
||||
}
|
||||
BalanceWidget(
|
||||
walletSnapshot = walletSnapshot,
|
||||
isReferenceToBalances = true,
|
||||
onReferenceClick = goBalances
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,19 +6,45 @@ import androidx.activity.ComponentActivity
|
|||
import androidx.activity.viewModels
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.CheckUpdateViewModel
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
|
||||
import co.electriccoin.zcash.ui.configuration.ConfigurationEntries
|
||||
import co.electriccoin.zcash.ui.configuration.RemoteConfig
|
||||
import co.electriccoin.zcash.ui.screen.balances.view.Balances
|
||||
import co.electriccoin.zcash.ui.screen.settings.viewmodel.SettingsViewModel
|
||||
import co.electriccoin.zcash.ui.screen.update.AppUpdateCheckerImp
|
||||
import co.electriccoin.zcash.ui.screen.update.model.UpdateState
|
||||
|
||||
@Composable
|
||||
internal fun WrapBalances(
|
||||
activity: ComponentActivity,
|
||||
goSettings: () -> Unit,
|
||||
) {
|
||||
// Show information about the app update, if available
|
||||
val checkUpdateViewModel by activity.viewModels<CheckUpdateViewModel> {
|
||||
CheckUpdateViewModel.CheckUpdateViewModelFactory(
|
||||
activity.application,
|
||||
AppUpdateCheckerImp.new()
|
||||
)
|
||||
}
|
||||
val isUpdateAvailable =
|
||||
checkUpdateViewModel.updateInfo.collectAsStateWithLifecycle().value.let {
|
||||
it?.appUpdateInfo != null && it.state == UpdateState.Prepared
|
||||
}
|
||||
|
||||
val settingsViewModel by activity.viewModels<SettingsViewModel>()
|
||||
|
||||
val isKeepScreenOnWhileSyncing = settingsViewModel.isKeepScreenOnWhileSyncing.collectAsStateWithLifecycle().value
|
||||
val isFiatConversionEnabled = ConfigurationEntries.IS_FIAT_CONVERSION_ENABLED.getValue(RemoteConfig.current)
|
||||
|
||||
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||
val walletSnapshot = walletViewModel.walletSnapshot.collectAsStateWithLifecycle().value
|
||||
|
||||
Balances(
|
||||
walletSnapshot = walletSnapshot,
|
||||
isFiatConversionEnabled = isFiatConversionEnabled,
|
||||
isKeepScreenOnWhileSyncing = isKeepScreenOnWhileSyncing,
|
||||
isUpdateAvailable = isUpdateAvailable,
|
||||
onSettings = goSettings,
|
||||
walletSnapshot = walletSnapshot,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package co.electriccoin.zcash.ui.screen.balances
|
||||
|
||||
/**
|
||||
* These are only used for automated testing.
|
||||
*/
|
||||
object BalancesTag {
|
||||
const val STATUS = "status"
|
||||
const val FIAT_CONVERSION = "fiat_conversion"
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package co.electriccoin.zcash.ui.screen.account.model
|
||||
package co.electriccoin.zcash.ui.screen.balances.model
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.ui.text.intl.Locale
|
||||
|
@ -8,7 +8,6 @@ import cash.z.ecc.android.sdk.model.MonetarySeparators
|
|||
import cash.z.ecc.android.sdk.model.PercentDecimal
|
||||
import cash.z.ecc.android.sdk.model.toFiatCurrencyState
|
||||
import cash.z.ecc.android.sdk.model.toZecString
|
||||
import cash.z.ecc.sdk.extension.toPercentageWithDecimal
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.model.WalletSnapshot
|
||||
import co.electriccoin.zcash.ui.common.model.spendableBalance
|
||||
|
@ -48,47 +47,43 @@ data class WalletDisplayValues(
|
|||
when (walletSnapshot.status) {
|
||||
Synchronizer.Status.SYNCING -> {
|
||||
progress = walletSnapshot.progress
|
||||
// we add "so far" to the amount
|
||||
// We add "so far" to the amount
|
||||
if (fiatCurrencyAmountState != FiatCurrencyConversionRateState.Unavailable) {
|
||||
fiatCurrencyAmountText =
|
||||
context.getString(
|
||||
R.string.account_status_syncing_amount_suffix,
|
||||
R.string.balances_status_syncing_amount_suffix,
|
||||
fiatCurrencyAmountText
|
||||
)
|
||||
}
|
||||
statusText =
|
||||
context.getString(
|
||||
R.string.account_status_syncing_format,
|
||||
walletSnapshot.progress.toPercentageWithDecimal()
|
||||
)
|
||||
statusText = context.getString(R.string.balances_status_syncing)
|
||||
}
|
||||
Synchronizer.Status.SYNCED -> {
|
||||
statusText =
|
||||
if (updateAvailable) {
|
||||
context.getString(R.string.account_status_update)
|
||||
context.getString(R.string.balances_status_update)
|
||||
} else {
|
||||
context.getString(R.string.account_status_up_to_date)
|
||||
context.getString(R.string.balances_status_synced)
|
||||
}
|
||||
}
|
||||
Synchronizer.Status.DISCONNECTED -> {
|
||||
statusText =
|
||||
context.getString(
|
||||
R.string.account_status_error,
|
||||
context.getString(R.string.account_status_error_connection)
|
||||
R.string.balances_status_error,
|
||||
context.getString(R.string.balances_status_error_connection)
|
||||
)
|
||||
}
|
||||
Synchronizer.Status.STOPPED -> {
|
||||
statusText = context.getString(R.string.account_status_stopped)
|
||||
statusText = context.getString(R.string.balances_status_stopped)
|
||||
}
|
||||
}
|
||||
|
||||
// more detailed error message
|
||||
// More detailed error message
|
||||
walletSnapshot.synchronizerError?.let {
|
||||
statusText =
|
||||
context.getString(
|
||||
R.string.account_status_error,
|
||||
R.string.balances_status_error,
|
||||
walletSnapshot.synchronizerError.getCauseMessage()
|
||||
?: context.getString(R.string.account_status_error_unknown)
|
||||
?: context.getString(R.string.balances_status_error_unknown)
|
||||
)
|
||||
}
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
package co.electriccoin.zcash.ui.screen.balances.view
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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
|
||||
|
@ -26,12 +26,17 @@ import androidx.compose.ui.platform.testTag
|
|||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.model.FiatCurrencyConversionRateState
|
||||
import cash.z.ecc.android.sdk.model.toZecString
|
||||
import cash.z.ecc.sdk.extension.toPercentageWithDecimal
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.BalanceWidget
|
||||
import co.electriccoin.zcash.ui.common.DisableScreenTimeout
|
||||
import co.electriccoin.zcash.ui.common.model.WalletSnapshot
|
||||
import co.electriccoin.zcash.ui.common.model.changePendingBalance
|
||||
import co.electriccoin.zcash.ui.common.model.spendableBalance
|
||||
|
@ -39,14 +44,17 @@ import co.electriccoin.zcash.ui.common.model.valuePendingBalance
|
|||
import co.electriccoin.zcash.ui.common.test.CommonTag
|
||||
import co.electriccoin.zcash.ui.design.component.Body
|
||||
import co.electriccoin.zcash.ui.design.component.BodySmall
|
||||
import co.electriccoin.zcash.ui.design.component.BodyWithFiatCurrencySymbol
|
||||
import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator
|
||||
import co.electriccoin.zcash.ui.design.component.CircularSmallProgressIndicator
|
||||
import co.electriccoin.zcash.ui.design.component.GradientSurface
|
||||
import co.electriccoin.zcash.ui.design.component.LinearProgressIndicator
|
||||
import co.electriccoin.zcash.ui.design.component.SmallTopAppBar
|
||||
import co.electriccoin.zcash.ui.design.component.StyledBalance
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
import co.electriccoin.zcash.ui.fixture.WalletSnapshotFixture
|
||||
import co.electriccoin.zcash.ui.screen.account.model.WalletDisplayValues
|
||||
import co.electriccoin.zcash.ui.screen.balances.BalancesTag
|
||||
import co.electriccoin.zcash.ui.screen.balances.model.WalletDisplayValues
|
||||
|
||||
@Preview("Balances")
|
||||
@Composable
|
||||
|
@ -54,20 +62,23 @@ private fun ComposablePreview() {
|
|||
ZcashTheme(forceDarkMode = false) {
|
||||
GradientSurface {
|
||||
Balances(
|
||||
walletSnapshot = WalletSnapshotFixture.new(),
|
||||
onSettings = {},
|
||||
isFiatConversionEnabled = false,
|
||||
isKeepScreenOnWhileSyncing = false,
|
||||
isUpdateAvailable = false,
|
||||
walletSnapshot = WalletSnapshotFixture.new(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO [#1127]: Implement Balances screen
|
||||
// TODO [#1127]: https://github.com/Electric-Coin-Company/zashi-android/issues/1127
|
||||
|
||||
@Composable
|
||||
fun Balances(
|
||||
onSettings: () -> Unit,
|
||||
isFiatConversionEnabled: Boolean,
|
||||
isKeepScreenOnWhileSyncing: Boolean?,
|
||||
isUpdateAvailable: Boolean,
|
||||
walletSnapshot: WalletSnapshot?,
|
||||
onSettings: () -> Unit
|
||||
) {
|
||||
Scaffold(topBar = {
|
||||
BalancesTopAppBar(onSettings = onSettings)
|
||||
|
@ -76,6 +87,9 @@ fun Balances(
|
|||
CircularScreenProgressIndicator()
|
||||
} else {
|
||||
BalancesMainContent(
|
||||
isFiatConversionEnabled = isFiatConversionEnabled,
|
||||
isKeepScreenOnWhileSyncing = isKeepScreenOnWhileSyncing,
|
||||
isUpdateAvailable = isUpdateAvailable,
|
||||
walletSnapshot = walletSnapshot,
|
||||
modifier =
|
||||
Modifier.padding(
|
||||
|
@ -110,15 +124,12 @@ private fun BalancesTopAppBar(onSettings: () -> Unit) {
|
|||
|
||||
@Composable
|
||||
private fun BalancesMainContent(
|
||||
isFiatConversionEnabled: Boolean,
|
||||
isKeepScreenOnWhileSyncing: Boolean?,
|
||||
isUpdateAvailable: Boolean,
|
||||
walletSnapshot: WalletSnapshot,
|
||||
modifier: Modifier = Modifier
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val walletDisplayValues =
|
||||
WalletDisplayValues.getNextValues(
|
||||
LocalContext.current,
|
||||
walletSnapshot
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
|
@ -129,13 +140,11 @@ private fun BalancesMainContent(
|
|||
) {
|
||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))
|
||||
|
||||
if (walletDisplayValues.zecAmountText.isNotEmpty()) {
|
||||
BalanceWidget(
|
||||
walletSnapshot = walletSnapshot,
|
||||
isReferenceToBalances = false,
|
||||
onReferenceClick = {}
|
||||
)
|
||||
}
|
||||
BalanceWidget(
|
||||
walletSnapshot = walletSnapshot,
|
||||
isReferenceToBalances = false,
|
||||
onReferenceClick = {}
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingHuge))
|
||||
|
||||
|
@ -146,27 +155,48 @@ private fun BalancesMainContent(
|
|||
|
||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))
|
||||
|
||||
BalancesOverview(walletSnapshot)
|
||||
BalancesOverview(
|
||||
walletSnapshot = walletSnapshot,
|
||||
isFiatConversionEnabled = isFiatConversionEnabled,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))
|
||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.height(166.dp)
|
||||
.background(color = ZcashTheme.colors.panelBackgroundColor),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingHuge))
|
||||
|
||||
Body(
|
||||
text = stringResource(id = R.string.balances_coming_soon),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f, true))
|
||||
|
||||
SyncStatus(
|
||||
walletSnapshot = walletSnapshot,
|
||||
isUpdateAvailable = isUpdateAvailable,
|
||||
)
|
||||
|
||||
if (isKeepScreenOnWhileSyncing == true && walletSnapshot.status == Synchronizer.Status.SYNCING) {
|
||||
DisableScreenTimeout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BalancesOverview(walletSnapshot: WalletSnapshot) {
|
||||
fun BalancesOverview(
|
||||
walletSnapshot: WalletSnapshot,
|
||||
isFiatConversionEnabled: Boolean,
|
||||
) {
|
||||
Column {
|
||||
SpendableBalanceRow(walletSnapshot)
|
||||
|
||||
|
@ -178,6 +208,36 @@ fun BalancesOverview(walletSnapshot: WalletSnapshot) {
|
|||
|
||||
// aka value pending
|
||||
PendingTransactionsRow(walletSnapshot)
|
||||
|
||||
if (isFiatConversionEnabled) {
|
||||
val walletDisplayValues =
|
||||
WalletDisplayValues.getNextValues(
|
||||
LocalContext.current,
|
||||
walletSnapshot,
|
||||
false
|
||||
)
|
||||
|
||||
Column(Modifier.testTag(BalancesTag.FIAT_CONVERSION)) {
|
||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall))
|
||||
|
||||
when (walletDisplayValues.fiatCurrencyAmountState) {
|
||||
is FiatCurrencyConversionRateState.Current -> {
|
||||
BodyWithFiatCurrencySymbol(
|
||||
amount = walletDisplayValues.fiatCurrencyAmountText
|
||||
)
|
||||
}
|
||||
is FiatCurrencyConversionRateState.Stale -> {
|
||||
// Note: we should show information about staleness too
|
||||
BodyWithFiatCurrencySymbol(
|
||||
amount = walletDisplayValues.fiatCurrencyAmountText
|
||||
)
|
||||
}
|
||||
is FiatCurrencyConversionRateState.Unavailable -> {
|
||||
Body(text = walletDisplayValues.fiatCurrencyAmountText)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -289,3 +349,48 @@ fun PendingTransactionsRow(walletSnapshot: WalletSnapshot) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SyncStatus(
|
||||
isUpdateAvailable: Boolean,
|
||||
walletSnapshot: WalletSnapshot,
|
||||
) {
|
||||
val walletDisplayValues =
|
||||
WalletDisplayValues.getNextValues(
|
||||
LocalContext.current,
|
||||
walletSnapshot,
|
||||
isUpdateAvailable
|
||||
)
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
if (walletDisplayValues.statusText.isNotEmpty()) {
|
||||
BodySmall(
|
||||
text = walletDisplayValues.statusText,
|
||||
modifier = Modifier.testTag(BalancesTag.STATUS)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall))
|
||||
}
|
||||
|
||||
BodySmall(
|
||||
text =
|
||||
stringResource(
|
||||
id = R.string.balances_status_syncing_percentage,
|
||||
walletSnapshot.progress.toPercentageWithDecimal()
|
||||
),
|
||||
textFontWeight = FontWeight.Black
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall))
|
||||
|
||||
LinearProgressIndicator(
|
||||
progress = walletSnapshot.progress.decimal,
|
||||
modifier =
|
||||
Modifier.padding(
|
||||
horizontal = ZcashTheme.dimens.spacingXlarge
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,6 @@ import co.electriccoin.zcash.ui.design.component.SmallTopAppBar
|
|||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme.dimens
|
||||
import co.electriccoin.zcash.ui.fixture.WalletSnapshotFixture
|
||||
import co.electriccoin.zcash.ui.screen.account.model.WalletDisplayValues
|
||||
import co.electriccoin.zcash.ui.screen.send.SendTag
|
||||
import co.electriccoin.zcash.ui.screen.send.ext.ABBREVIATION_INDEX
|
||||
import co.electriccoin.zcash.ui.screen.send.ext.abbreviated
|
||||
|
@ -361,21 +360,13 @@ private fun SendForm(
|
|||
.then(modifier),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
val walletDisplayValues =
|
||||
WalletDisplayValues.getNextValues(
|
||||
context = LocalContext.current,
|
||||
walletSnapshot = walletSnapshot
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(dimens.spacingDefault))
|
||||
|
||||
if (walletDisplayValues.zecAmountText.isNotEmpty()) {
|
||||
BalanceWidget(
|
||||
walletSnapshot = walletSnapshot,
|
||||
isReferenceToBalances = true,
|
||||
onReferenceClick = goBalances
|
||||
)
|
||||
}
|
||||
BalanceWidget(
|
||||
walletSnapshot = walletSnapshot,
|
||||
isReferenceToBalances = true,
|
||||
onReferenceClick = goBalances
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(dimens.spacingXlarge))
|
||||
|
||||
|
|
|
@ -1,21 +1,3 @@
|
|||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="account_button_history">Transaction History</string>
|
||||
|
||||
<string name="account_status_syncing_format" formatted="true">Syncing - <xliff:g id="synced_percent" example="50.25">
|
||||
%1$s</xliff:g>%%</string> <!-- double %% for escaping -->
|
||||
<string name="account_status_syncing_catchup">Syncing</string>
|
||||
<string name="account_status_syncing_amount_suffix" formatted="true"><xliff:g id="amount_prefix" example="123$">%1$s</xliff:g> so far</string>
|
||||
<string name="account_status_syncing_additional_information">We will show you funds as we discover them.</string>
|
||||
<string name="account_status_up_to_date">Up-to-date</string>
|
||||
<string name="account_status_sending_format" formatted="true">Sending <xliff:g id="sending_amount" example=".023">%1$s</xliff:g></string>
|
||||
<string name="account_status_receiving_format" formatted="true">Receiving <xliff:g id="receiving_amount" example=".023">%1$s</xliff:g> ZEC</string>
|
||||
<string name="account_status_shielding_format" formatted="true">Shielding <xliff:g id="shielding_amount" example=".023">%1$s</xliff:g> ZEC</string>
|
||||
<string name="account_status_update">Please Update</string>
|
||||
<string name="account_status_error" formatted="true">Error: <xliff:g id="error_type" example="Lost connection">%1$s</xliff:g></string>
|
||||
<string name="account_status_error_connection">Disconnected</string>
|
||||
<string name="account_status_error_unknown">Unknown cause</string>
|
||||
<string name="account_status_stopped">Synchronizer stopped</string>
|
||||
<string name="account_status_updating_blockheight">Updating blockheight</string>
|
||||
<string name="account_status_fiat_currency_price_out_of_date" formatted="true"><xliff:g id="fiat_currency" example="USD">%1$s</xliff:g> price out-of-date</string>
|
||||
<string name="account_status_spendable" formatted="true">Fully spendable in <xliff:g id="spendable_time" example="2 minutes">%1$s</xliff:g></string>
|
||||
</resources>
|
||||
|
|
|
@ -3,6 +3,22 @@
|
|||
<string name="balances_shielded_spendable">Shielded zec (spendable)</string>
|
||||
<string name="balances_change_pending">Change pending</string>
|
||||
<string name="balances_pending_transactions">Pending transactions</string>
|
||||
<string name="balances_coming_soon">Coming soon:\n\nTransparent
|
||||
funds shielding,\nBlock synchronization indicator</string>
|
||||
<string name="balances_coming_soon">Transparent funds shielding\n(coming soon)</string>
|
||||
|
||||
<string name="balances_status_syncing" formatted="true">Syncing…</string>
|
||||
<string name="balances_status_syncing_amount_suffix" formatted="true"><xliff:g id="amount_prefix" example="123$">%1$s</xliff:g> so far</string>
|
||||
<string name="balances_status_syncing_percentage" formatted="true"><xliff:g id="synced_percent" example="50.25">
|
||||
%1$s</xliff:g>%%</string> <!-- double %% for escaping -->
|
||||
<string name="balances_status_synced">Synced</string>
|
||||
<string name="balances_status_sending_format" formatted="true">Sending <xliff:g id="sending_amount" example=".023">%1$s</xliff:g></string>
|
||||
<string name="balances_status_receiving_format" formatted="true">Receiving <xliff:g id="receiving_amount" example=".023">%1$s</xliff:g> ZEC</string>
|
||||
<string name="balances_status_shielding_format" formatted="true">Shielding <xliff:g id="shielding_amount" example=".023">%1$s</xliff:g> ZEC</string>
|
||||
<string name="balances_status_update">Please Update via Play Store</string>
|
||||
<string name="balances_status_error" formatted="true">Error: <xliff:g id="error_type" example="Lost connection">%1$s</xliff:g></string>
|
||||
<string name="balances_status_error_connection">Disconnected</string>
|
||||
<string name="balances_status_error_unknown">Unknown cause</string>
|
||||
<string name="balances_status_stopped">Synchronizer stopped</string>
|
||||
<string name="balances_status_updating_blockheight">Updating blockheight</string>
|
||||
<string name="balances_status_fiat_currency_price_out_of_date" formatted="true"><xliff:g id="fiat_currency" example="USD">%1$s</xliff:g> price out-of-date</string>
|
||||
<string name="balances_status_spendable" formatted="true">Fully spendable in <xliff:g id="spendable_time" example="2 minutes">%1$s</xliff:g></string>
|
||||
</resources>
|
||||
|
|
|
@ -384,7 +384,7 @@ private fun accountScreenshots(
|
|||
composeTestRule.activity.walletViewModel.walletSnapshot.value != null
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithTag(AccountTag.STATUS_VIEWS).also {
|
||||
composeTestRule.onNodeWithTag(AccountTag.BALANCE_VIEWS).also {
|
||||
it.assertExists()
|
||||
ScreenshotTest.takeScreenshot(tag, "Account 1")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue