[#1165] Syncing progressbar

* [#1165] Syncing progressbar

-  UI + logic + tests
- Closes  #1165

* Changelog update
This commit is contained in:
Honza Rychnovský 2024-02-08 11:28:00 +01:00 committed by GitHub
parent 1058802b19
commit cc333ea902
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 521 additions and 259 deletions

View File

@ -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

View File

@ -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)
)
}

View File

@ -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
)
}

View File

@ -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,

View File

@ -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() =

View File

@ -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,
)
}

View File

@ -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()
},

View File

@ -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)
}

View File

@ -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() {

View File

@ -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()
}
}
}
}

View File

@ -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)
}
}
}

View File

@ -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)

View File

@ -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()
}
}

View File

@ -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"
}

View File

@ -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,

View File

@ -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
)
}
}

View File

@ -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,
)
}

View File

@ -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"
}

View File

@ -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)
)
}

View File

@ -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
)
)
}
}

View File

@ -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))

View File

@ -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>

View File

@ -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>

View File

@ -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")
}