[#808] Use Dimens Across App

* [#808] Use Dimens across the app

- Shorter Onboarding screen design enhanced

* Shorter New Wallet Backup screen UI enhanced

* Home screen UI enhance

* Seed screen UI enhance

* Link SendArgumentsWrapper issue

* Move custom buttons paddings to PaddingValues

- Optional Modifier parameter should have a default value of Modifier
- Thanks to this we introduced the outerPaddingValues parameter to all our buttons with default paddings
- Also notice the difference between e.g. ShortOnboardingView and ShortNewWalletBackupView. In the backup, we have the bottom button to be part of Scaffold’s bottomBar, which is maybe a better pattern than stretching the buttons with spacers in case of part of the screen content view, but it has also its tradeoffs.

* Settings screen enhance

* Support screen UI enhance

* About screen UI enhance

* Scan screen UI enhance

- Move modifiers to the caller’s side
- Match texts to predefined styles
- Add color to Small text component + reorder parameters

* Send screen UI enahnce

- Lifted out some modifiers arguments to the caller’s side
- Fixed button’s horizontal paddings

* Scan move BackHandler to Android class

* Update screen UI enhance

* Receive screen UI enhance

* Address screen UI enhance

* Ignore AndroidUpdate back action test

- Will be refactored soon

* Remove TODO as already implemented

* Restore screens UI enhance

* Create UpdateViewAndroidTest

* [#807][Design system] Remove deprecated Paddings

* [#705] Instrumentation coverage generation fails locally

* [#808] Use Dimens across the app

- Shorter Onboarding screen design enhanced

* Shorter New Wallet Backup screen UI enhanced

* Home screen UI enhance

* Seed screen UI enhance

* Link SendArgumentsWrapper issue

* Move custom buttons paddings to PaddingValues

- Optional Modifier parameter should have a default value of Modifier
- Thanks to this we introduced the outerPaddingValues parameter to all our buttons with default paddings
- Also notice the difference between e.g. ShortOnboardingView and ShortNewWalletBackupView. In the backup, we have the bottom button to be part of Scaffold’s bottomBar, which is maybe a better pattern than stretching the buttons with spacers in case of part of the screen content view, but it has also its tradeoffs.

* Settings screen enhance

* Support screen UI enhance

* About screen UI enhance

* Scan screen UI enhance

- Move modifiers to the caller’s side
- Match texts to predefined styles
- Add color to Small text component + reorder parameters

* Send screen UI enahnce

- Lifted out some modifiers arguments to the caller’s side
- Fixed button’s horizontal paddings

* Scan move BackHandler to Android class

* Update screen UI enhance

* Receive screen UI enhance

* Address screen UI enhance

* Ignore AndroidUpdate back action test

- Will be refactored soon

* Remove TODO as already implemented

* Restore screens UI enhance

* Create UpdateViewAndroidTest

* [#807][Design system] Remove deprecated Paddings

* Address review comments
This commit is contained in:
Honza Rychnovsky 2023-04-04 14:21:18 +02:00 committed by GitHub
parent 859ec1591e
commit cb98cc048f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 662 additions and 311 deletions

View File

@ -1,6 +1,7 @@
package co.electriccoin.zcash.ui.design.component package co.electriccoin.zcash.ui.design.component
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button import androidx.compose.material3.Button
@ -34,6 +35,10 @@ fun PrimaryButton(
onClick: () -> Unit, onClick: () -> Unit,
text: String, text: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
outerPaddingValues: PaddingValues = PaddingValues(
horizontal = ZcashTheme.dimens.spacingDefault,
vertical = ZcashTheme.dimens.spacingSmall
),
enabled: Boolean = true enabled: Boolean = true
) { ) {
Button( Button(
@ -41,7 +46,7 @@ fun PrimaryButton(
modifier = modifier.then( modifier = modifier.then(
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp) .padding(outerPaddingValues)
), ),
enabled = enabled, enabled = enabled,
colors = buttonColors(containerColor = MaterialTheme.colorScheme.primary) colors = buttonColors(containerColor = MaterialTheme.colorScheme.primary)
@ -59,6 +64,10 @@ fun SecondaryButton(
onClick: () -> Unit, onClick: () -> Unit,
text: String, text: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
outerPaddingValues: PaddingValues = PaddingValues(
horizontal = ZcashTheme.dimens.spacingDefault,
vertical = ZcashTheme.dimens.spacingSmall
),
enabled: Boolean = true enabled: Boolean = true
) { ) {
Button( Button(
@ -66,7 +75,7 @@ fun SecondaryButton(
modifier = modifier.then( modifier = modifier.then(
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp) .padding(outerPaddingValues)
), ),
enabled = enabled, enabled = enabled,
colors = buttonColors(containerColor = MaterialTheme.colorScheme.secondary) colors = buttonColors(containerColor = MaterialTheme.colorScheme.secondary)
@ -83,13 +92,17 @@ fun SecondaryButton(
fun NavigationButton( fun NavigationButton(
onClick: () -> Unit, onClick: () -> Unit,
text: String, text: String,
modifier: Modifier = Modifier modifier: Modifier = Modifier,
outerPaddingValues: PaddingValues = PaddingValues(
horizontal = ZcashTheme.dimens.spacingDefault,
vertical = ZcashTheme.dimens.spacingSmall
),
) { ) {
Button( Button(
onClick = onClick, onClick = onClick,
modifier = modifier.then( modifier = modifier.then(
Modifier Modifier
.padding(horizontal = 16.dp, vertical = 8.dp) .padding(outerPaddingValues)
), ),
colors = buttonColors(containerColor = MaterialTheme.colorScheme.secondary) colors = buttonColors(containerColor = MaterialTheme.colorScheme.secondary)
) { ) {
@ -102,6 +115,10 @@ fun TertiaryButton(
onClick: () -> Unit, onClick: () -> Unit,
text: String, text: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
outerPaddingValues: PaddingValues = PaddingValues(
horizontal = ZcashTheme.dimens.spacingDefault,
vertical = ZcashTheme.dimens.spacingSmall
),
enabled: Boolean = true enabled: Boolean = true
) { ) {
Button( Button(
@ -109,7 +126,7 @@ fun TertiaryButton(
modifier = modifier.then( modifier = modifier.then(
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp) .padding(outerPaddingValues)
), ),
enabled = enabled, enabled = enabled,
elevation = ButtonDefaults.buttonElevation(0.dp, 0.dp, 0.dp), elevation = ButtonDefaults.buttonElevation(0.dp, 0.dp, 0.dp),
@ -127,14 +144,18 @@ fun TertiaryButton(
fun DangerousButton( fun DangerousButton(
onClick: () -> Unit, onClick: () -> Unit,
text: String, text: String,
modifier: Modifier = Modifier modifier: Modifier = Modifier,
outerPaddingValues: PaddingValues = PaddingValues(
horizontal = ZcashTheme.dimens.spacingDefault,
vertical = ZcashTheme.dimens.spacingSmall
),
) { ) {
Button( Button(
onClick = onClick, onClick = onClick,
modifier = modifier.then( modifier = modifier.then(
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp) .padding(outerPaddingValues)
), ),
colors = buttonColors(containerColor = ZcashTheme.colors.dangerous) colors = buttonColors(containerColor = ZcashTheme.colors.dangerous)
) { ) {

View File

@ -46,7 +46,7 @@ fun SwitchWithLabel(
) )
Spacer( Spacer(
modifier = Modifier modifier = Modifier
.width(ZcashTheme.paddings.padding) .width(ZcashTheme.dimens.spacingDefault)
.constrainAs(spacer) { .constrainAs(spacer) {
top.linkTo(parent.top) top.linkTo(parent.top)
bottom.linkTo(parent.top) bottom.linkTo(parent.top)

View File

@ -22,10 +22,10 @@ fun Header(
) { ) {
Text( Text(
text = text, text = text,
textAlign = textAlign,
style = MaterialTheme.typography.headlineLarge,
color = color, color = color,
modifier = modifier textAlign = textAlign,
modifier = modifier,
style = MaterialTheme.typography.headlineLarge,
) )
} }
@ -34,14 +34,14 @@ fun Body(
text: String, text: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
textAlign: TextAlign = TextAlign.Start, textAlign: TextAlign = TextAlign.Start,
color: Color = MaterialTheme.colorScheme.onBackground color: Color = MaterialTheme.colorScheme.onBackground,
) { ) {
Text( Text(
text = text, text = text,
textAlign = textAlign,
style = MaterialTheme.typography.bodyLarge,
color = color, color = color,
modifier = modifier textAlign = textAlign,
modifier = modifier,
style = MaterialTheme.typography.bodyLarge,
) )
} }
@ -49,14 +49,15 @@ fun Body(
fun Small( fun Small(
text: String, text: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
textAlign: TextAlign = TextAlign.Start textAlign: TextAlign = TextAlign.Start,
color: Color = MaterialTheme.colorScheme.onBackground,
) { ) {
Text( Text(
text = text, text = text,
style = MaterialTheme.typography.bodyMedium, color = color,
color = MaterialTheme.colorScheme.onBackground, textAlign = textAlign,
modifier = modifier, modifier = modifier,
textAlign = textAlign style = MaterialTheme.typography.bodyMedium,
) )
} }
@ -81,7 +82,7 @@ fun ListHeader(
Text( Text(
text = text, text = text,
style = ZcashTheme.typography.listItem, style = ZcashTheme.typography.listItem,
color = MaterialTheme.colorScheme.onBackground, color = ZcashTheme.colors.onBackgroundHeader,
modifier = modifier modifier = modifier
) )
} }

View File

@ -1,5 +1,6 @@
package co.electriccoin.zcash.ui.design.component package co.electriccoin.zcash.ui.design.component
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.TextField import androidx.compose.material3.TextField
@ -8,6 +9,7 @@ import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
@Suppress("LongParameterList") @Suppress("LongParameterList")
@ -23,7 +25,9 @@ fun FormTextField(
keyboardOptions: KeyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text), keyboardOptions: KeyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text),
colors: TextFieldColors = TextFieldDefaults.textFieldColors( colors: TextFieldColors = TextFieldDefaults.textFieldColors(
containerColor = Color.Transparent containerColor = Color.Transparent
) ),
keyboardActions: KeyboardActions = KeyboardActions.Default,
shape: Shape = TextFieldDefaults.filledShape
) { ) {
TextField( TextField(
value = value, value = value,
@ -33,6 +37,8 @@ fun FormTextField(
colors = colors, colors = colors,
modifier = modifier, modifier = modifier,
leadingIcon = leadingIcon, leadingIcon = leadingIcon,
trailingIcon = trailingIcon trailingIcon = trailingIcon,
keyboardActions = keyboardActions,
shape = shape
) )
} }

View File

@ -10,6 +10,7 @@ import androidx.compose.ui.unit.dp
@Immutable @Immutable
data class Dimens( data class Dimens(
// Default spacings: // Default spacings:
val spacingNone: Dp,
val spacingXtiny: Dp, val spacingXtiny: Dp,
val spacingTiny: Dp, val spacingTiny: Dp,
val spacingSmall: Dp, val spacingSmall: Dp,
@ -22,6 +23,7 @@ data class Dimens(
) )
private val defaultDimens = Dimens( private val defaultDimens = Dimens(
spacingNone = 0.dp,
spacingXtiny = 2.dp, spacingXtiny = 2.dp,
spacingTiny = 4.dp, spacingTiny = 4.dp,
spacingSmall = 8.dp, spacingSmall = 8.dp,

View File

@ -11,7 +11,6 @@ import co.electriccoin.zcash.ui.design.theme.internal.LightColorPalette
import co.electriccoin.zcash.ui.design.theme.internal.LightExtendedColorPalette import co.electriccoin.zcash.ui.design.theme.internal.LightExtendedColorPalette
import co.electriccoin.zcash.ui.design.theme.internal.LocalExtendedColors import co.electriccoin.zcash.ui.design.theme.internal.LocalExtendedColors
import co.electriccoin.zcash.ui.design.theme.internal.LocalExtendedTypography import co.electriccoin.zcash.ui.design.theme.internal.LocalExtendedTypography
import co.electriccoin.zcash.ui.design.theme.internal.Paddings
import co.electriccoin.zcash.ui.design.theme.internal.Typography import co.electriccoin.zcash.ui.design.theme.internal.Typography
@Composable @Composable
@ -57,10 +56,4 @@ object ZcashTheme {
val dimens: Dimens val dimens: Dimens
@Composable @Composable
get() = LocalDimens.current get() = LocalDimens.current
// TODO [#807]: [Design system] Remove deprecated Paddings class
// TODO [#807]: https://github.com/zcash/secant-android-wallet/issues/807
val paddings: Paddings
@Composable
get() = Paddings()
} }

View File

@ -1,10 +0,0 @@
package co.electriccoin.zcash.ui.design.theme.internal
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
data class Paddings(
val paddingHalf: Dp = 8.dp,
val padding: Dp = 16.dp,
val paddingDouble: Dp = 32.dp
)

View File

@ -1,4 +1,4 @@
package co.electriccoin.zcash.ui.screen.update.view package co.electriccoin.zcash.ui.screen.update.util
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context

View File

@ -0,0 +1,75 @@
package co.electriccoin.zcash.ui.screen.update.view
import androidx.activity.ComponentActivity
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.test.espresso.Espresso
import androidx.test.filters.MediumTest
import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.fixture.UpdateInfoFixture
import co.electriccoin.zcash.ui.screen.update.AppUpdateChecker
import co.electriccoin.zcash.ui.screen.update.model.UpdateInfo
import co.electriccoin.zcash.ui.screen.update.model.UpdateState
import co.electriccoin.zcash.ui.test.getStringResource
import org.junit.Rule
import org.junit.Test
// Non-multiplatform tests that require interacting with the Android system (e.g. system back navigation)
// These don't have persistent state, so they are still unit tests.
class UpdateViewAndroidTest : UiTestPrerequisites() {
@get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
private fun newTestSetup(updateInfo: UpdateInfo) = UpdateViewAndroidTestSetup(
composeTestRule,
updateInfo
).apply {
setDefaultContent()
}
@Test
@MediumTest
fun postpone_optional_update_test() {
val updateInfo = UpdateInfoFixture.new(
priority = AppUpdateChecker.Priority.LOW,
force = false,
appUpdateInfo = null,
state = UpdateState.Prepared
)
newTestSetup(updateInfo)
composeTestRule.onNodeWithText(getStringResource(R.string.update_header)).also {
it.assertExists()
}
Espresso.pressBack()
composeTestRule.onNodeWithText(getStringResource(R.string.update_header)).also {
it.assertDoesNotExist()
}
}
@Test
@MediumTest
fun postpone_force_update_test() {
val updateInfo = UpdateInfoFixture.new(
priority = AppUpdateChecker.Priority.HIGH,
force = true,
appUpdateInfo = null,
state = UpdateState.Prepared
)
newTestSetup(updateInfo)
composeTestRule.onNodeWithText(getStringResource(R.string.update_critical_header)).also {
it.assertExists()
}
Espresso.pressBack()
composeTestRule.onNodeWithText(getStringResource(R.string.update_critical_header)).also {
it.assertExists()
}
}
}

View File

@ -0,0 +1,29 @@
package co.electriccoin.zcash.ui.screen.update.view
import androidx.compose.runtime.Composable
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.screen.update.WrapUpdate
import co.electriccoin.zcash.ui.screen.update.model.UpdateInfo
class UpdateViewAndroidTestSetup(
private val composeTestRule: AndroidComposeTestRule<*, *>,
private val updateInfo: UpdateInfo
) {
@Composable
@Suppress("TestFunctionName")
fun DefaultContent() {
WrapUpdate(
composeTestRule.activity,
updateInfo
)
}
fun setDefaultContent() {
composeTestRule.setContent {
ZcashTheme {
DefaultContent()
}
}
}
}

View File

@ -6,7 +6,6 @@ import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performClick
import androidx.test.espresso.Espresso
import androidx.test.filters.MediumTest import androidx.test.filters.MediumTest
import co.electriccoin.zcash.test.UiTestPrerequisites import co.electriccoin.zcash.test.UiTestPrerequisites
import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.R
@ -108,10 +107,6 @@ class UpdateViewTest : UiTestPrerequisites() {
composeTestRule.clickLater() composeTestRule.clickLater()
assertEquals(0, testSetup.getOnLaterCount()) assertEquals(0, testSetup.getOnLaterCount())
Espresso.pressBack()
assertEquals(0, testSetup.getOnLaterCount())
} }
@Test @Test

View File

@ -1,8 +1,8 @@
package co.electriccoin.zcash.ui.screen.about.view package co.electriccoin.zcash.ui.screen.about.view
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
@ -20,7 +20,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import co.electriccoin.zcash.build.gitSha import co.electriccoin.zcash.build.gitSha
import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.component.Body import co.electriccoin.zcash.ui.design.component.Body
@ -57,9 +56,19 @@ fun About(
AboutTopAppBar(onBack = goBack) AboutTopAppBar(onBack = goBack)
}) { paddingValues -> }) { paddingValues ->
AboutMainContent( AboutMainContent(
paddingValues,
versionInfo, versionInfo,
configInfo configInfo,
modifier = Modifier
.fillMaxHeight()
.verticalScroll(
rememberScrollState()
)
.padding(
top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault,
bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingDefault,
start = ZcashTheme.dimens.spacingDefault,
end = ZcashTheme.dimens.spacingDefault
)
) )
} }
} }
@ -83,33 +92,33 @@ private fun AboutTopAppBar(onBack: () -> Unit) {
} }
@Composable @Composable
fun AboutMainContent(paddingValues: PaddingValues, versionInfo: VersionInfo, configInfo: ConfigInfo) { fun AboutMainContent(
Column( versionInfo: VersionInfo,
Modifier configInfo: ConfigInfo,
.verticalScroll(rememberScrollState()) modifier: Modifier = Modifier
.padding(top = paddingValues.calculateTopPadding()) ) {
) { Column(modifier) {
Icon(painterResource(id = R.drawable.ic_launcher_adaptive_foreground), contentDescription = null) Icon(painterResource(id = R.drawable.ic_launcher_adaptive_foreground), contentDescription = null)
Text(stringResource(id = R.string.app_name)) Text(stringResource(id = R.string.app_name))
Spacer(modifier = Modifier.height(24.dp)) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
Header(stringResource(id = R.string.about_version_header)) Header(stringResource(id = R.string.about_version_header))
Body(stringResource(R.string.about_version_format, versionInfo.versionName, versionInfo.versionCode)) Body(stringResource(R.string.about_version_format, versionInfo.versionName, versionInfo.versionCode))
Spacer(modifier = Modifier.height(24.dp)) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
Header(stringResource(id = R.string.about_build_header)) Header(stringResource(id = R.string.about_build_header))
Body(gitSha) Body(gitSha)
Spacer(modifier = Modifier.height(24.dp)) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
configInfo.configurationUpdatedAt?.let { updatedAt -> configInfo.configurationUpdatedAt?.let { updatedAt ->
Header(stringResource(id = R.string.about_build_configuration)) Header(stringResource(id = R.string.about_build_configuration))
Body(updatedAt.toString()) Body(updatedAt.toString())
} }
Spacer(modifier = Modifier.height(24.dp)) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
Header(stringResource(id = R.string.about_legal_header)) Header(stringResource(id = R.string.about_legal_header))
Body(stringResource(id = R.string.about_legal_info)) Body(stringResource(id = R.string.about_legal_info))

View File

@ -12,9 +12,13 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowDropDownCircle import androidx.compose.material.icons.filled.ArrowDropDownCircle
@ -66,7 +70,12 @@ fun ComposablePreview() {
fun WalletAddresses(walletAddresses: WalletAddresses, onBack: () -> Unit) { fun WalletAddresses(walletAddresses: WalletAddresses, onBack: () -> Unit) {
Column { Column {
WalletDetailTopAppBar(onBack) WalletDetailTopAppBar(onBack)
WalletDetailAddresses(walletAddresses) WalletDetailAddresses(
walletAddresses,
Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
)
} }
} }
@ -96,8 +105,11 @@ private val BIG_INDICATOR_WIDTH = 24.dp
private val SMALL_INDICATOR_WIDTH = 16.dp private val SMALL_INDICATOR_WIDTH = 16.dp
@Composable @Composable
private fun WalletDetailAddresses(walletAddresses: WalletAddresses) { private fun WalletDetailAddresses(
Column(Modifier.fillMaxWidth()) { walletAddresses: WalletAddresses,
modifier: Modifier = Modifier
) {
Column(modifier) {
Row( Row(
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()
@ -115,16 +127,36 @@ private fun WalletDetailAddresses(walletAddresses: WalletAddresses) {
ExpandableRow( ExpandableRow(
title = stringResource(R.string.wallet_address_unified), title = stringResource(R.string.wallet_address_unified),
content = walletAddresses.unified.address, content = walletAddresses.unified.address,
isInitiallyExpanded = true isInitiallyExpanded = true,
modifier = Modifier
.fillMaxWidth()
.padding(
horizontal = ZcashTheme.dimens.spacingDefault,
vertical = ZcashTheme.dimens.spacingTiny
)
) )
Box(Modifier.height(IntrinsicSize.Min)) { Box(Modifier.height(IntrinsicSize.Min)) {
Divider(modifier = Modifier.fillMaxHeight()) Divider(modifier = Modifier.fillMaxHeight())
ListHeader(text = stringResource(R.string.wallet_address_header_includes)) ListHeader(
text = stringResource(R.string.wallet_address_header_includes),
modifier = Modifier.padding(all = ZcashTheme.dimens.spacingSmall)
)
} }
SaplingAddress(walletAddresses.sapling.address) SaplingAddress(
TransparentAddress(walletAddresses.transparent.address) saplingAddress = walletAddresses.sapling.address,
modifier = Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min)
)
TransparentAddress(
transparentAddress = walletAddresses.transparent.address,
modifier = Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min)
)
} }
} }
} }
@ -135,34 +167,44 @@ private fun WalletDetailAddresses(walletAddresses: WalletAddresses) {
// of row position will be needed. // of row position will be needed.
@Composable @Composable
private fun SaplingAddress(saplingAddress: String) { private fun SaplingAddress(
Row( saplingAddress: String,
Modifier modifier: Modifier = Modifier
.fillMaxWidth() ) {
.height(IntrinsicSize.Min) Row(modifier) {
) {
SmallIndicator(ZcashTheme.colors.addressHighlightSapling) SmallIndicator(ZcashTheme.colors.addressHighlightSapling)
ExpandableRow( ExpandableRow(
title = stringResource(R.string.wallet_address_sapling), title = stringResource(R.string.wallet_address_sapling),
content = saplingAddress, content = saplingAddress,
isInitiallyExpanded = false isInitiallyExpanded = false,
modifier = Modifier
.fillMaxWidth()
.padding(
horizontal = ZcashTheme.dimens.spacingDefault,
vertical = ZcashTheme.dimens.spacingTiny
)
) )
} }
} }
@Composable @Composable
private fun TransparentAddress(transparentAddress: String) { private fun TransparentAddress(
Row( transparentAddress: String,
Modifier modifier: Modifier = Modifier
.fillMaxWidth() ) {
.height(IntrinsicSize.Min) Row(modifier) {
) {
SmallIndicator(ZcashTheme.colors.addressHighlightTransparent) SmallIndicator(ZcashTheme.colors.addressHighlightTransparent)
ExpandableRow( ExpandableRow(
title = stringResource(R.string.wallet_address_transparent), title = stringResource(R.string.wallet_address_transparent),
content = transparentAddress, content = transparentAddress,
isInitiallyExpanded = false isInitiallyExpanded = false,
modifier = Modifier
.fillMaxWidth()
.padding(
horizontal = ZcashTheme.dimens.spacingDefault,
vertical = ZcashTheme.dimens.spacingTiny
)
) )
} }
} }
@ -177,11 +219,9 @@ private fun ExpandableRow(
var expandedState by rememberSaveable { mutableStateOf(isInitiallyExpanded) } var expandedState by rememberSaveable { mutableStateOf(isInitiallyExpanded) }
Column( Column(
modifier Modifier
.fillMaxWidth() .clickable { expandedState = !expandedState }
.clickable { .then(modifier) // To have proper ripple effect
expandedState = !expandedState
}
) { ) {
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.defaultMinSize(minHeight = 48.dp)) { Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.defaultMinSize(minHeight = 48.dp)) {
ListItem(text = title) ListItem(text = title)

View File

@ -201,7 +201,7 @@ private fun SeedPhrase(persistableWallet: PersistableWallet) {
Column( Column(
Modifier Modifier
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(vertical = ZcashTheme.paddings.padding) .padding(vertical = ZcashTheme.dimens.spacingDefault)
) { ) {
Body(stringResource(R.string.new_wallet_3_body_1)) Body(stringResource(R.string.new_wallet_3_body_1))
ChipGrid(persistableWallet.seedPhrase.split.toPersistentList()) ChipGrid(persistableWallet.seedPhrase.split.toPersistentList())
@ -248,7 +248,7 @@ private fun TestInProgress(
Column( Column(
Modifier Modifier
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.padding(vertical = ZcashTheme.paddings.padding) .padding(vertical = ZcashTheme.dimens.spacingDefault)
) { ) {
splitSeedPhrase.chunked(CHIP_GRID_ROW_SIZE).forEachIndexed { chunkIndex, chunk -> splitSeedPhrase.chunked(CHIP_GRID_ROW_SIZE).forEachIndexed { chunkIndex, chunk ->
Row(Modifier.fillMaxWidth()) { Row(Modifier.fillMaxWidth()) {

View File

@ -3,7 +3,7 @@
package co.electriccoin.zcash.ui.screen.backup.view package co.electriccoin.zcash.ui.screen.backup.view
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
@ -74,23 +74,29 @@ fun ShortNewWalletBackup(
} }
) { paddingValues -> ) { paddingValues ->
ShortNewWalletMainContent( ShortNewWalletMainContent(
paddingValues = paddingValues,
wallet = wallet, wallet = wallet,
modifier = Modifier.padding(
top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault,
bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingDefault,
start = ZcashTheme.dimens.spacingDefault,
end = ZcashTheme.dimens.spacingDefault
)
) )
} }
} }
@Composable @Composable
private fun ShortNewWalletMainContent( private fun ShortNewWalletMainContent(
paddingValues: PaddingValues,
wallet: PersistableWallet, wallet: PersistableWallet,
modifier: Modifier = Modifier,
) { ) {
Column( Column(
Modifier Modifier
.padding( .fillMaxHeight()
top = paddingValues.calculateTopPadding(), .verticalScroll(
bottom = paddingValues.calculateBottomPadding() rememberScrollState()
) )
.then(modifier)
) { ) {
SeedPhrase(wallet) SeedPhrase(wallet)
} }
@ -99,11 +105,7 @@ private fun ShortNewWalletMainContent(
@Composable @Composable
private fun SeedPhrase(persistableWallet: PersistableWallet) { private fun SeedPhrase(persistableWallet: PersistableWallet) {
SecureScreen() SecureScreen()
Column( Column {
Modifier
.verticalScroll(rememberScrollState())
.padding(vertical = ZcashTheme.paddings.padding)
) {
Body(stringResource(R.string.new_wallet_short_body)) Body(stringResource(R.string.new_wallet_short_body))
ChipGrid(persistableWallet.seedPhrase.split.toPersistentList()) ChipGrid(persistableWallet.seedPhrase.split.toPersistentList())
} }

View File

@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -145,7 +146,6 @@ fun Home(
) )
}) { paddingValues -> }) { paddingValues ->
HomeMainContent( HomeMainContent(
paddingValues,
walletSnapshot, walletSnapshot,
transactionHistory, transactionHistory,
isUpdateAvailable = isUpdateAvailable, isUpdateAvailable = isUpdateAvailable,
@ -154,6 +154,12 @@ fun Home(
isCircularProgressBarEnabled = isCircularProgressBarEnabled, isCircularProgressBarEnabled = isCircularProgressBarEnabled,
goReceive = goReceive, goReceive = goReceive,
goSend = goSend, goSend = goSend,
modifier = Modifier.padding(
top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault,
bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingDefault,
start = ZcashTheme.dimens.spacingDefault,
end = ZcashTheme.dimens.spacingDefault
)
) )
} }
} }
@ -241,7 +247,7 @@ private fun HomeDrawer(
ModalDrawerSheet( ModalDrawerSheet(
modifier = Modifier.testTag(HomeTag.DRAWER_MENU) modifier = Modifier.testTag(HomeTag.DRAWER_MENU)
) { ) {
Spacer(Modifier.height(12.dp)) Spacer(Modifier.height(ZcashTheme.dimens.spacingDefault))
NavigationDrawerItem( NavigationDrawerItem(
icon = { Icon(Icons.Default.Password, contentDescription = null) }, icon = { Icon(Icons.Default.Password, contentDescription = null) },
label = { Text(stringResource(id = R.string.home_menu_seed_phrase)) }, label = { Text(stringResource(id = R.string.home_menu_seed_phrase)) },
@ -288,7 +294,6 @@ private fun HomeDrawer(
@Suppress("LongParameterList") @Suppress("LongParameterList")
@Composable @Composable
private fun HomeMainContent( private fun HomeMainContent(
paddingValues: PaddingValues,
walletSnapshot: WalletSnapshot, walletSnapshot: WalletSnapshot,
transactionHistory: ImmutableList<CommonTransaction>, transactionHistory: ImmutableList<CommonTransaction>,
isUpdateAvailable: Boolean, isUpdateAvailable: Boolean,
@ -297,18 +302,41 @@ private fun HomeMainContent(
isCircularProgressBarEnabled: Boolean, isCircularProgressBarEnabled: Boolean,
goReceive: () -> Unit, goReceive: () -> Unit,
goSend: () -> Unit, goSend: () -> Unit,
modifier: Modifier = Modifier
) { ) {
Column( Column(
Modifier Modifier
.verticalScroll(rememberScrollState()) .fillMaxHeight()
.padding(top = paddingValues.calculateTopPadding()) .verticalScroll(
rememberScrollState()
)
.then(modifier)
) { ) {
Status(walletSnapshot, isUpdateAvailable, isFiatConversionEnabled, isCircularProgressBarEnabled) Status(walletSnapshot, isUpdateAvailable, isFiatConversionEnabled, isCircularProgressBarEnabled)
Spacer(modifier = Modifier.height(24.dp)) if (isSyncing(walletSnapshot.status)) {
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
Body(text = stringResource(id = R.string.home_information))
}
PrimaryButton(onClick = goReceive, text = stringResource(R.string.home_button_receive)) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
PrimaryButton(onClick = goSend, text = stringResource(R.string.home_button_send))
PrimaryButton(
onClick = goSend,
text = stringResource(R.string.home_button_send),
outerPaddingValues = PaddingValues(
horizontal = ZcashTheme.dimens.spacingNone,
vertical = ZcashTheme.dimens.spacingSmall
)
)
PrimaryButton(
onClick = goReceive,
text = stringResource(R.string.home_button_receive),
outerPaddingValues = PaddingValues(
horizontal = ZcashTheme.dimens.spacingNone,
vertical = ZcashTheme.dimens.spacingSmall
)
)
History(transactionHistory) History(transactionHistory)
@ -386,7 +414,7 @@ private fun Status(
.wrapContentSize(), .wrapContentSize(),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Spacer(modifier = Modifier.height(24.dp)) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
if (walletDisplayValues.zecAmountText.isNotEmpty()) { if (walletDisplayValues.zecAmountText.isNotEmpty()) {
HeaderWithZecIcon(amount = walletDisplayValues.zecAmountText) HeaderWithZecIcon(amount = walletDisplayValues.zecAmountText)
@ -394,7 +422,7 @@ private fun Status(
if (isFiatConversionEnabled) { if (isFiatConversionEnabled) {
Column(Modifier.testTag(HomeTag.FIAT_CONVERSION)) { Column(Modifier.testTag(HomeTag.FIAT_CONVERSION)) {
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall))
when (walletDisplayValues.fiatCurrencyAmountState) { when (walletDisplayValues.fiatCurrencyAmountState) {
is FiatCurrencyConversionRateState.Current -> { is FiatCurrencyConversionRateState.Current -> {
@ -415,7 +443,7 @@ private fun Status(
} }
} }
Spacer(modifier = Modifier.height(24.dp)) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
if (walletDisplayValues.statusText.isNotEmpty()) { if (walletDisplayValues.statusText.isNotEmpty()) {
Body( Body(

View File

@ -245,16 +245,16 @@ private fun Wallet(paddingValues: PaddingValues) {
Column( Column(
Modifier Modifier
.padding( .padding(
start = ZcashTheme.paddings.padding, start = ZcashTheme.dimens.spacingDefault,
end = ZcashTheme.paddings.padding, end = ZcashTheme.dimens.spacingDefault,
bottom = paddingValues.calculateBottomPadding() bottom = paddingValues.calculateBottomPadding()
) )
.fillMaxWidth() .fillMaxWidth()
) { ) {
Header( Header(
modifier = Modifier.padding( modifier = Modifier.padding(
top = ZcashTheme.paddings.padding, top = ZcashTheme.dimens.spacingDefault,
bottom = ZcashTheme.paddings.paddingHalf bottom = ZcashTheme.dimens.spacingSmall
), ),
text = stringResource(R.string.onboarding_4_header) text = stringResource(R.string.onboarding_4_header)
) )
@ -280,8 +280,8 @@ private fun Content(
) { ) {
Column( Column(
Modifier.padding( Modifier.padding(
start = ZcashTheme.paddings.padding, start = ZcashTheme.dimens.spacingDefault,
end = ZcashTheme.paddings.padding, end = ZcashTheme.dimens.spacingDefault,
bottom = paddingValues.calculateBottomPadding() bottom = paddingValues.calculateBottomPadding()
) )
) { ) {

View File

@ -4,8 +4,12 @@ package co.electriccoin.zcash.ui.screen.onboarding.view
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenu
@ -25,6 +29,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT
import co.electriccoin.zcash.ui.design.component.Body
import co.electriccoin.zcash.ui.design.component.GradientSurface import co.electriccoin.zcash.ui.design.component.GradientSurface
import co.electriccoin.zcash.ui.design.component.Header import co.electriccoin.zcash.ui.design.component.Header
import co.electriccoin.zcash.ui.design.component.PrimaryButton import co.electriccoin.zcash.ui.design.component.PrimaryButton
@ -64,9 +70,14 @@ fun ShortOnboarding(
} }
) { paddingValues -> ) { paddingValues ->
OnboardingMainContent( OnboardingMainContent(
paddingValues,
onImportWallet = onImportWallet, onImportWallet = onImportWallet,
onCreateWallet = onCreateWallet onCreateWallet = onCreateWallet,
modifier = Modifier.padding(
top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault,
bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingDefault,
start = ZcashTheme.dimens.spacingDefault,
end = ZcashTheme.dimens.spacingDefault
)
) )
} }
} }
@ -109,35 +120,47 @@ private fun DebugMenu(onFixtureWallet: () -> Unit) {
@Composable @Composable
private fun OnboardingMainContent( private fun OnboardingMainContent(
paddingValues: PaddingValues,
onImportWallet: () -> Unit, onImportWallet: () -> Unit,
onCreateWallet: () -> Unit, onCreateWallet: () -> Unit,
modifier: Modifier = Modifier
) { ) {
Column( Column(
Modifier.padding(top = paddingValues.calculateTopPadding()) Modifier
.fillMaxHeight()
.verticalScroll(
rememberScrollState()
)
.then(modifier)
) { ) {
Column( Header(text = stringResource(R.string.onboarding_short_header))
Modifier
.padding( Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingXlarge))
start = ZcashTheme.paddings.padding,
end = ZcashTheme.paddings.padding, Body(text = stringResource(R.string.onboarding_short_information))
bottom = paddingValues.calculateBottomPadding()
) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))
.fillMaxWidth()
) { Spacer(
Header( modifier = Modifier
modifier = Modifier.padding( .fillMaxHeight()
top = ZcashTheme.paddings.padding, .weight(MINIMAL_WEIGHT)
bottom = ZcashTheme.paddings.paddingHalf )
),
text = stringResource(R.string.onboarding_short_header) PrimaryButton(
onClick = onCreateWallet,
text = stringResource(R.string.onboarding_short_create_new_wallet),
outerPaddingValues = PaddingValues(
horizontal = ZcashTheme.dimens.spacingNone,
vertical = ZcashTheme.dimens.spacingSmall
) )
PrimaryButton(onCreateWallet, stringResource(R.string.onboarding_short_create_new_wallet), Modifier.fillMaxWidth()) )
TertiaryButton( TertiaryButton(
onImportWallet, onImportWallet,
stringResource(R.string.onboarding_short_import_existing_wallet), stringResource(R.string.onboarding_short_import_existing_wallet),
Modifier.fillMaxWidth() outerPaddingValues = PaddingValues(
horizontal = ZcashTheme.dimens.spacingNone,
vertical = ZcashTheme.dimens.spacingSmall
) )
} )
} }
} }

View File

@ -2,6 +2,11 @@ package co.electriccoin.zcash.ui.screen.receive.view
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@ -26,8 +31,9 @@ import cash.z.ecc.android.sdk.model.WalletAddress
import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.BrightenScreen import co.electriccoin.zcash.ui.common.BrightenScreen
import co.electriccoin.zcash.ui.common.DisableScreenTimeout import co.electriccoin.zcash.ui.common.DisableScreenTimeout
import co.electriccoin.zcash.ui.design.component.Body import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT
import co.electriccoin.zcash.ui.design.component.GradientSurface import co.electriccoin.zcash.ui.design.component.GradientSurface
import co.electriccoin.zcash.ui.design.component.Header
import co.electriccoin.zcash.ui.design.component.PrimaryButton import co.electriccoin.zcash.ui.design.component.PrimaryButton
import co.electriccoin.zcash.ui.design.theme.ZcashTheme import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.screen.receive.util.AndroidQrCodeImageGenerator import co.electriccoin.zcash.ui.screen.receive.util.AndroidQrCodeImageGenerator
@ -61,6 +67,10 @@ fun Receive(
ReceiveContents( ReceiveContents(
walletAddress = walletAddress, walletAddress = walletAddress,
onAddressDetails = onAddressDetails, onAddressDetails = onAddressDetails,
modifier = Modifier
.fillMaxHeight()
.verticalScroll(rememberScrollState())
.padding(all = ZcashTheme.dimens.spacingDefault)
) )
} }
} }
@ -90,21 +100,42 @@ private val DEFAULT_QR_CODE_SIZE = 320.dp
private fun ReceiveContents( private fun ReceiveContents(
walletAddress: WalletAddress, walletAddress: WalletAddress,
onAddressDetails: () -> Unit, onAddressDetails: () -> Unit,
modifier: Modifier = Modifier
) { ) {
Column(Modifier.verticalScroll(rememberScrollState())) { Column(modifier) {
QrCode(data = walletAddress.address, DEFAULT_QR_CODE_SIZE, Modifier.align(Alignment.CenterHorizontally)) QrCode(data = walletAddress.address, DEFAULT_QR_CODE_SIZE, Modifier.align(Alignment.CenterHorizontally))
Body(text = stringResource(id = R.string.wallet_address_unified), Modifier.align(Alignment.CenterHorizontally))
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
Header(
text = stringResource(id = R.string.wallet_address_unified),
Modifier.align(Alignment.CenterHorizontally)
)
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall))
// TODO [#163]: Ellipsize center of the string // TODO [#163]: Ellipsize center of the string
// TODO [#163]: https://github.com/zcash/secant-android-wallet/issues/163 // TODO [#163]: https://github.com/zcash/secant-android-wallet/issues/163
Text( Text(
text = walletAddress.address, text = walletAddress.address,
style = MaterialTheme.typography.headlineLarge, style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onBackground, color = MaterialTheme.colorScheme.onBackground,
modifier = Modifier.align(Alignment.CenterHorizontally), modifier = Modifier.align(Alignment.CenterHorizontally),
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
maxLines = 1 maxLines = 1
) )
PrimaryButton(onClick = onAddressDetails, text = stringResource(id = R.string.receive_see_address_details))
Spacer(
modifier = Modifier
.fillMaxHeight()
.weight(MINIMAL_WEIGHT)
)
PrimaryButton(
onClick = onAddressDetails,
text = stringResource(id = R.string.receive_see_address_details),
outerPaddingValues = PaddingValues(all = ZcashTheme.dimens.spacingNone)
)
} }
} }

View File

@ -5,10 +5,12 @@ package co.electriccoin.zcash.ui.screen.restore.view
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
@ -64,6 +66,7 @@ import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT
import co.electriccoin.zcash.ui.design.component.Body import co.electriccoin.zcash.ui.design.component.Body
import co.electriccoin.zcash.ui.design.component.CHIP_GRID_ROW_SIZE import co.electriccoin.zcash.ui.design.component.CHIP_GRID_ROW_SIZE
import co.electriccoin.zcash.ui.design.component.Chip import co.electriccoin.zcash.ui.design.component.Chip
import co.electriccoin.zcash.ui.design.component.FormTextField
import co.electriccoin.zcash.ui.design.component.GradientSurface import co.electriccoin.zcash.ui.design.component.GradientSurface
import co.electriccoin.zcash.ui.design.component.Header import co.electriccoin.zcash.ui.design.component.Header
import co.electriccoin.zcash.ui.design.component.NavigationButton import co.electriccoin.zcash.ui.design.component.NavigationButton
@ -123,7 +126,6 @@ fun PreviewRestoreComplete() {
} }
} }
// TODO [#409]: https://github.com/zcash/secant-android-wallet/issues/409
/** /**
* Note that the restore review doesn't allow the user to go back once the seed is entered correctly. * Note that the restore review doesn't allow the user to go back once the seed is entered correctly.
* *
@ -182,6 +184,15 @@ fun RestoreWallet(
} }
}, },
content = { paddingValues -> content = { paddingValues ->
val commonModifier = Modifier
// We intentionally set the bottom smaller to save space in case of the software keyboard is visible
.padding(
top = paddingValues.calculateTopPadding() + dimens.spacingDefault,
bottom = paddingValues.calculateBottomPadding() + dimens.spacingSmall,
start = dimens.spacingDefault,
end = dimens.spacingDefault
)
when (currentStage) { when (currentStage) {
RestoreStage.Seed -> { RestoreStage.Seed -> {
SecureScreen() SecureScreen()
@ -194,10 +205,7 @@ fun RestoreWallet(
parseResult = parseResult, parseResult = parseResult,
paste = paste, paste = paste,
goNext = { restoreState.goNext() }, goNext = { restoreState.goNext() },
modifier = Modifier.padding( modifier = commonModifier
top = paddingValues.calculateTopPadding(),
bottom = paddingValues.calculateBottomPadding()
)
) )
} }
RestoreStage.Birthday -> { RestoreStage.Birthday -> {
@ -206,10 +214,7 @@ fun RestoreWallet(
initialRestoreHeight = restoreHeight, initialRestoreHeight = restoreHeight,
setRestoreHeight = setRestoreHeight, setRestoreHeight = setRestoreHeight,
onNext = { restoreState.goNext() }, onNext = { restoreState.goNext() },
modifier = Modifier.padding( modifier = commonModifier
top = paddingValues.calculateTopPadding(),
bottom = paddingValues.calculateBottomPadding()
)
) )
} }
RestoreStage.Complete -> { RestoreStage.Complete -> {
@ -219,10 +224,7 @@ fun RestoreWallet(
RestoreComplete( RestoreComplete(
onComplete = onFinished, onComplete = onFinished,
modifier = Modifier.padding( modifier = commonModifier
top = paddingValues.calculateTopPadding(),
bottom = paddingValues.calculateBottomPadding()
)
) )
} }
} }
@ -281,12 +283,14 @@ private fun RestoreSeedMainContent(
val isSeedValid = userWordList.wordValidation().collectAsState(null).value is SeedPhraseValidation.Valid val isSeedValid = userWordList.wordValidation().collectAsState(null).value is SeedPhraseValidation.Valid
Column( Column(
modifier.then(Modifier.verticalScroll(scrollState)) Modifier
.fillMaxHeight()
.verticalScroll(scrollState)
.then(modifier)
) { ) {
Body( Body(text = stringResource(id = R.string.restore_seed_instructions))
modifier = Modifier.padding(dimens.spacingDefault),
text = stringResource(id = R.string.restore_seed_instructions) Spacer(Modifier.height(dimens.spacingSmall))
)
ChipGridWithText(currentUserWordList) ChipGridWithText(currentUserWordList)
@ -299,12 +303,17 @@ private fun RestoreSeedMainContent(
) )
} }
Spacer(modifier = Modifier.weight(MINIMAL_WEIGHT)) Spacer(
modifier = Modifier
.fillMaxHeight()
.weight(MINIMAL_WEIGHT)
)
PrimaryButton( PrimaryButton(
onClick = goNext, onClick = goNext,
text = stringResource(id = R.string.restore_seed_button_restore), text = stringResource(id = R.string.restore_seed_button_restore),
enabled = isSeedValid enabled = isSeedValid,
outerPaddingValues = PaddingValues(top = dimens.spacingSmall)
) )
} }
@ -338,7 +347,17 @@ private fun RestoreSeedBottomBar(
// the user can hit the clear button // the user can hit the clear button
if (!isSeedValid) { if (!isSeedValid) {
Column(modifier) { Column(modifier) {
Warn(parseResult) Warn(
parseResult = parseResult,
modifier = Modifier
.fillMaxWidth()
// Note we don't set the top, as it's set by the confirm button above
.padding(
bottom = dimens.spacingDefault,
start = dimens.spacingDefault,
end = dimens.spacingDefault
)
)
Autocomplete(parseResult = parseResult, { Autocomplete(parseResult = parseResult, {
setTextState("") setTextState("")
userWordList.append(listOf(it)) userWordList.append(listOf(it))
@ -352,11 +371,7 @@ private fun RestoreSeedBottomBar(
private fun ChipGridWithText( private fun ChipGridWithText(
userWordList: ImmutableList<String> userWordList: ImmutableList<String>
) { ) {
Column( Column(Modifier.testTag(RestoreTag.CHIP_LAYOUT)) {
Modifier
.padding(start = 12.dp, end = 12.dp)
.testTag(RestoreTag.CHIP_LAYOUT)
) {
userWordList.chunked(CHIP_GRID_ROW_SIZE).forEachIndexed { chunkIndex, chunk -> userWordList.chunked(CHIP_GRID_ROW_SIZE).forEachIndexed { chunkIndex, chunk ->
Row(Modifier.fillMaxWidth(), verticalAlignment = CenterVertically) { Row(Modifier.fillMaxWidth(), verticalAlignment = CenterVertically) {
val remainder = (chunk.size % CHIP_GRID_ROW_SIZE) val remainder = (chunk.size % CHIP_GRID_ROW_SIZE)
@ -452,11 +467,20 @@ private fun Autocomplete(
} }
@Suppress("ModifierReused") @Suppress("ModifierReused")
LazyRow(highlightModifier.testTag(RestoreTag.AUTOCOMPLETE_LAYOUT)) { LazyRow(
modifier = highlightModifier.testTag(RestoreTag.AUTOCOMPLETE_LAYOUT),
// Note we don't set the top, as it's set by the confirm button above
// And we also set the bottom smaller, as the keyboard will be always visible
contentPadding = PaddingValues(
bottom = dimens.spacingDefault,
start = dimens.spacingDefault,
end = dimens.spacingSmall
)
) {
items(it) { items(it) {
Chip( Chip(
text = it, text = it,
modifier = modifier modifier = Modifier
.testTag(RestoreTag.AUTOCOMPLETE_ITEM) .testTag(RestoreTag.AUTOCOMPLETE_ITEM)
.clickable { onSuggestionSelected(it) } .clickable { onSuggestionSelected(it) }
) )
@ -466,12 +490,13 @@ private fun Autocomplete(
} }
@Composable @Composable
private fun Warn(parseResult: ParseResult) { private fun Warn(
parseResult: ParseResult,
modifier: Modifier = Modifier
) {
if (parseResult is ParseResult.Warn) { if (parseResult is ParseResult.Warn) {
Surface( Surface(
modifier = Modifier modifier = modifier,
.fillMaxWidth()
.padding(dimens.spacingTiny),
shape = RoundedCornerShape(8.dp), shape = RoundedCornerShape(8.dp),
color = MaterialTheme.colorScheme.secondary, color = MaterialTheme.colorScheme.secondary,
shadowElevation = 4.dp shadowElevation = 4.dp
@ -491,8 +516,8 @@ private fun Warn(parseResult: ParseResult) {
} }
} }
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@Suppress("LongMethod")
private fun RestoreBirthday( private fun RestoreBirthday(
zcashNetwork: ZcashNetwork, zcashNetwork: ZcashNetwork,
initialRestoreHeight: BlockHeight?, initialRestoreHeight: BlockHeight?,
@ -503,12 +528,22 @@ private fun RestoreBirthday(
val (height, setHeight) = rememberSaveable { val (height, setHeight) = rememberSaveable {
mutableStateOf(initialRestoreHeight?.value?.toString() ?: "") mutableStateOf(initialRestoreHeight?.value?.toString() ?: "")
} }
val scrollState = rememberScrollState()
Column(modifier.verticalScroll(scrollState)) { Column(
Modifier
.fillMaxHeight()
.verticalScroll(rememberScrollState())
.then(modifier)
) {
Header(stringResource(R.string.restore_birthday_header)) Header(stringResource(R.string.restore_birthday_header))
Spacer(modifier = Modifier.height(dimens.spacingDefault))
Body(stringResource(R.string.restore_birthday_body)) Body(stringResource(R.string.restore_birthday_body))
TextField(
Spacer(modifier = Modifier.height(dimens.spacingDefault))
FormTextField(
value = height, value = height,
onValueChange = { heightString -> onValueChange = { heightString ->
val filteredHeightString = heightString.filter { it.isDigit() } val filteredHeightString = heightString.filter { it.isDigit() }
@ -528,8 +563,10 @@ private fun RestoreBirthday(
keyboardActions = KeyboardActions(onAny = {}), keyboardActions = KeyboardActions(onAny = {}),
shape = RoundedCornerShape(8.dp), shape = RoundedCornerShape(8.dp),
) )
Spacer( Spacer(
modifier = Modifier modifier = Modifier
.fillMaxHeight()
.weight(MINIMAL_WEIGHT) .weight(MINIMAL_WEIGHT)
) )
@ -543,28 +580,50 @@ private fun RestoreBirthday(
onNext() onNext()
}, },
text = stringResource(R.string.restore_birthday_button_restore), text = stringResource(R.string.restore_birthday_button_restore),
enabled = isBirthdayValid enabled = isBirthdayValid,
outerPaddingValues = PaddingValues(top = dimens.spacingSmall)
) )
TertiaryButton( TertiaryButton(
onClick = { onClick = {
setRestoreHeight(null) setRestoreHeight(null)
onNext() onNext()
}, },
text = stringResource(R.string.restore_birthday_button_skip) text = stringResource(R.string.restore_birthday_button_skip),
outerPaddingValues = PaddingValues(top = dimens.spacingSmall)
) )
} }
} }
@Composable @Composable
private fun RestoreComplete(onComplete: () -> Unit, modifier: Modifier = Modifier) { private fun RestoreComplete(
Column(modifier) { onComplete: () -> Unit,
modifier: Modifier = Modifier
) {
Column(
Modifier
.fillMaxHeight()
.verticalScroll(rememberScrollState())
.then(modifier)
) {
Header(stringResource(R.string.restore_complete_header)) Header(stringResource(R.string.restore_complete_header))
Spacer(modifier = Modifier.height(dimens.spacingDefault))
Body(stringResource(R.string.restore_complete_info)) Body(stringResource(R.string.restore_complete_info))
Spacer(modifier = Modifier.height(dimens.spacingDefault))
Spacer( Spacer(
modifier = Modifier modifier = Modifier
.fillMaxHeight() .fillMaxHeight()
.weight(MINIMAL_WEIGHT) .weight(MINIMAL_WEIGHT)
) )
PrimaryButton(onComplete, stringResource(R.string.restore_button_see_wallet))
PrimaryButton(
onClick = onComplete,
text = stringResource(R.string.restore_button_see_wallet),
outerPaddingValues = PaddingValues(top = dimens.spacingSmall)
)
} }
} }

View File

@ -14,8 +14,10 @@ import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
@ -37,7 +39,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.layout.onSizeChanged
@ -47,19 +48,19 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.platform.testTag import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension import androidx.constraintlayout.compose.Dimension
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import co.electriccoin.zcash.spackle.Twig import co.electriccoin.zcash.spackle.Twig
import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.component.Body
import co.electriccoin.zcash.ui.design.component.GradientSurface import co.electriccoin.zcash.ui.design.component.GradientSurface
import co.electriccoin.zcash.ui.design.component.SecondaryButton import co.electriccoin.zcash.ui.design.component.SecondaryButton
import co.electriccoin.zcash.ui.design.component.Small
import co.electriccoin.zcash.ui.design.theme.ZcashTheme import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.screen.scan.ScanTag import co.electriccoin.zcash.ui.screen.scan.ScanTag
import co.electriccoin.zcash.ui.screen.scan.model.ScanState import co.electriccoin.zcash.ui.screen.scan.model.ScanState
@ -108,12 +109,20 @@ fun Scan(
snackbarHost = { SnackbarHost(snackbarHostState) } snackbarHost = { SnackbarHost(snackbarHostState) }
) { paddingValues -> ) { paddingValues ->
ScanMainContent( ScanMainContent(
paddingValues,
onScanned, onScanned,
onOpenSettings, onOpenSettings,
onBack, onBack,
onScanStateChanged, onScanStateChanged,
snackbarHostState snackbarHostState,
modifier = Modifier
.fillMaxSize()
.background(Color.Black)
.padding(
top = paddingValues.calculateTopPadding(),
bottom = paddingValues.calculateBottomPadding(),
start = ZcashTheme.dimens.spacingNone,
end = ZcashTheme.dimens.spacingNone
)
) )
} }
} }
@ -121,41 +130,37 @@ fun Scan(
@Composable @Composable
fun ScanBottomItems( fun ScanBottomItems(
scanState: ScanState, scanState: ScanState,
onOpenSettings: () -> Unit onOpenSettings: () -> Unit,
modifier: Modifier = Modifier
) { ) {
Column(modifier = Modifier.fillMaxWidth()) { Column(modifier) {
Text( Body(
text = stringResource(id = R.string.scan_hint), text = stringResource(id = R.string.scan_hint),
color = Color.White, color = Color.White
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.fillMaxWidth()
.align(CenterHorizontally)
.padding(horizontal = 24.dp, vertical = 8.dp)
) )
Text( Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall))
Small(
text = when (scanState) { text = when (scanState) {
ScanState.Permission -> stringResource(id = R.string.scan_state_permission) ScanState.Permission -> stringResource(id = R.string.scan_state_permission)
ScanState.Scanning -> stringResource(id = R.string.scan_state_scanning) ScanState.Scanning -> stringResource(id = R.string.scan_state_scanning)
ScanState.Failed -> stringResource(id = R.string.scan_state_failed) ScanState.Failed -> stringResource(id = R.string.scan_state_failed)
}, },
color = Color.White, color = Color.White,
fontSize = 16.sp, modifier = Modifier.testTag(ScanTag.TEXT_STATE)
modifier = Modifier
.fillMaxWidth()
.align(CenterHorizontally)
.padding(horizontal = 24.dp, vertical = 8.dp)
.testTag(ScanTag.TEXT_STATE)
) )
if (scanState == ScanState.Permission) { if (scanState == ScanState.Permission) {
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))
SecondaryButton( SecondaryButton(
onClick = onOpenSettings, onClick = onOpenSettings,
text = stringResource(id = R.string.scan_settings_button), text = stringResource(id = R.string.scan_settings_button),
modifier = Modifier outerPaddingValues = PaddingValues(
.padding(horizontal = 24.dp, vertical = 12.dp) vertical = ZcashTheme.dimens.spacingSmall,
horizontal = ZcashTheme.dimens.spacingNone
)
) )
} }
} }
@ -183,12 +188,12 @@ private fun ScanTopAppBar(onBack: () -> Unit) {
@Suppress("MagicNumber", "LongMethod", "LongParameterList") @Suppress("MagicNumber", "LongMethod", "LongParameterList")
@Composable @Composable
private fun ScanMainContent( private fun ScanMainContent(
paddingValues: PaddingValues,
onScanned: (String) -> Unit, onScanned: (String) -> Unit,
onOpenSettings: () -> Unit, onOpenSettings: () -> Unit,
onBack: () -> Unit, onBack: () -> Unit,
onScanStateChanged: (ScanState) -> Unit, onScanStateChanged: (ScanState) -> Unit,
snackbarHostState: SnackbarHostState snackbarHostState: SnackbarHostState,
modifier: Modifier = Modifier
) { ) {
val context = LocalContext.current val context = LocalContext.current
@ -234,12 +239,7 @@ private fun ScanMainContent(
(framePossibleSize.value.width * 0.7).roundToInt() (framePossibleSize.value.width * 0.7).roundToInt()
} }
ConstraintLayout( ConstraintLayout(modifier) {
modifier = Modifier
.fillMaxSize()
.background(Color.Black)
.padding(top = paddingValues.calculateTopPadding())
) {
val (frame, bottomItems) = createRefs() val (frame, bottomItems) = createRefs()
when (scanState) { when (scanState) {
@ -292,7 +292,16 @@ private fun ScanMainContent(
} }
Box(modifier = Modifier.constrainAs(bottomItems) { bottom.linkTo(parent.bottom) }) { Box(modifier = Modifier.constrainAs(bottomItems) { bottom.linkTo(parent.bottom) }) {
ScanBottomItems(scanState, onOpenSettings) ScanBottomItems(
scanState = scanState,
onOpenSettings = onOpenSettings,
modifier = Modifier
.fillMaxWidth()
.padding(
vertical = ZcashTheme.dimens.spacingDefault,
horizontal = ZcashTheme.dimens.spacingDefault
)
)
} }
} }
} }

View File

@ -2,6 +2,9 @@ package co.electriccoin.zcash.ui.screen.seed.view
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
@ -58,9 +61,14 @@ fun Seed(
SeedTopAppBar(onBack = onBack) SeedTopAppBar(onBack = onBack)
}) { paddingValues -> }) { paddingValues ->
SeedMainContent( SeedMainContent(
paddingValues,
persistableWallet = persistableWallet, persistableWallet = persistableWallet,
onCopyToClipboard = onCopyToClipboard onCopyToClipboard = onCopyToClipboard,
modifier = Modifier.padding(
top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault,
bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingDefault,
start = ZcashTheme.dimens.spacingDefault,
end = ZcashTheme.dimens.spacingDefault
)
) )
} }
} }
@ -85,19 +93,31 @@ private fun SeedTopAppBar(onBack: () -> Unit) {
@Composable @Composable
private fun SeedMainContent( private fun SeedMainContent(
paddingValues: PaddingValues,
persistableWallet: PersistableWallet, persistableWallet: PersistableWallet,
onCopyToClipboard: () -> Unit onCopyToClipboard: () -> Unit,
modifier: Modifier = Modifier
) { ) {
Column( Column(
Modifier Modifier
.verticalScroll(rememberScrollState()) .fillMaxHeight()
.padding(top = paddingValues.calculateTopPadding()) .verticalScroll(
rememberScrollState()
)
.then(modifier)
) { ) {
Body(stringResource(R.string.seed_body)) Body(stringResource(R.string.seed_body))
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault))
ChipGrid(persistableWallet.seedPhrase.split.toPersistentList()) ChipGrid(persistableWallet.seedPhrase.split.toPersistentList())
TertiaryButton(onClick = onCopyToClipboard, text = stringResource(R.string.seed_copy)) TertiaryButton(
onClick = onCopyToClipboard,
text = stringResource(R.string.seed_copy),
outerPaddingValues = PaddingValues(
horizontal = ZcashTheme.dimens.spacingNone,
vertical = ZcashTheme.dimens.spacingSmall
)
)
} }
} }

View File

@ -1,6 +1,7 @@
package co.electriccoin.zcash.ui.screen.send.view package co.electriccoin.zcash.ui.screen.send.view
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
@ -108,6 +109,10 @@ fun Send(
onQrScannerOpen = onQrScannerOpen, onQrScannerOpen = onQrScannerOpen,
hasCameraFeature = hasCameraFeature, hasCameraFeature = hasCameraFeature,
modifier = Modifier modifier = Modifier
.fillMaxHeight()
.verticalScroll(
rememberScrollState()
)
.padding( .padding(
top = paddingValues.calculateTopPadding() + dimens.spacingDefault, top = paddingValues.calculateTopPadding() + dimens.spacingDefault,
bottom = paddingValues.calculateBottomPadding() + dimens.spacingDefault, bottom = paddingValues.calculateBottomPadding() + dimens.spacingDefault,
@ -238,6 +243,8 @@ private fun SendForm(
mutableStateOf<Set<ZecSendExt.ZecSendValidation.Invalid.ValidationError>>(emptySet()) mutableStateOf<Set<ZecSendExt.ZecSendValidation.Invalid.ValidationError>>(emptySet())
} }
// TODO [#826]: SendArgumentsWrapper object properties validation
// TODO [#826]: https://github.com/zcash/secant-android-wallet/issues/826
if (sendArgumentsWrapper?.recipientAddress != null) { if (sendArgumentsWrapper?.recipientAddress != null) {
recipientAddressString = sendArgumentsWrapper.recipientAddress recipientAddressString = sendArgumentsWrapper.recipientAddress
} }
@ -248,11 +255,7 @@ private fun SendForm(
memoString = sendArgumentsWrapper.memo memoString = sendArgumentsWrapper.memo
} }
Column( Column(modifier) {
modifier
.fillMaxHeight()
.verticalScroll(rememberScrollState())
) {
Header( Header(
text = stringResource(id = R.string.send_balance, myBalance.toZecString()), text = stringResource(id = R.string.send_balance, myBalance.toZecString()),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
@ -314,7 +317,11 @@ private fun SendForm(
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
Spacer(Modifier.fillMaxHeight(MINIMAL_WEIGHT)) Spacer(
modifier = Modifier
.fillMaxHeight()
.weight(MINIMAL_WEIGHT)
)
if (validation.isNotEmpty()) { if (validation.isNotEmpty()) {
/* /*
@ -348,7 +355,8 @@ private fun SendForm(
text = stringResource(id = R.string.send_create), text = stringResource(id = R.string.send_create),
// Check for ABBREVIATION_INDEX goes away once proper address validation is in place. // Check for ABBREVIATION_INDEX goes away once proper address validation is in place.
// For now, it just prevents a crash on the confirmation screen. // For now, it just prevents a crash on the confirmation screen.
enabled = amountZecString.isNotBlank() && recipientAddressString.length > ABBREVIATION_INDEX enabled = amountZecString.isNotBlank() && recipientAddressString.length > ABBREVIATION_INDEX,
outerPaddingValues = PaddingValues(top = dimens.spacingNone)
) )
} }
} }
@ -359,14 +367,7 @@ private fun Confirmation(
onConfirmation: () -> Unit, onConfirmation: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
Column( Column(modifier) {
Modifier
.fillMaxHeight()
.verticalScroll(
rememberScrollState()
)
.then(modifier)
) {
Body( Body(
stringResource( stringResource(
R.string.send_confirmation_amount_and_address_format, R.string.send_confirmation_amount_and_address_format,
@ -392,7 +393,8 @@ private fun Confirmation(
PrimaryButton( PrimaryButton(
modifier = Modifier.padding(top = dimens.spacingSmall), modifier = Modifier.padding(top = dimens.spacingSmall),
onClick = onConfirmation, onClick = onConfirmation,
text = stringResource(id = R.string.send_confirmation_button) text = stringResource(id = R.string.send_confirmation_button),
outerPaddingValues = PaddingValues(top = dimens.spacingSmall)
) )
} }
} }
@ -402,14 +404,7 @@ private fun Sending(
zecSend: ZecSend, zecSend: ZecSend,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
Column( Column(modifier) {
Modifier
.fillMaxHeight()
.verticalScroll(
rememberScrollState()
)
.then(modifier)
) {
Header( Header(
text = stringResource( text = stringResource(
R.string.send_in_progress_amount_format, R.string.send_in_progress_amount_format,
@ -458,14 +453,7 @@ private fun SendSuccessful(
onDone: () -> Unit, onDone: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
Column( Column(modifier) {
Modifier
.fillMaxHeight()
.verticalScroll(
rememberScrollState()
)
.then(modifier)
) {
Header( Header(
text = stringResource(R.string.send_successful_title), text = stringResource(R.string.send_successful_title),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
@ -496,7 +484,8 @@ private fun SendSuccessful(
PrimaryButton( PrimaryButton(
modifier = Modifier.padding(top = dimens.spacingSmall), modifier = Modifier.padding(top = dimens.spacingSmall),
text = stringResource(R.string.send_successful_button), text = stringResource(R.string.send_successful_button),
onClick = onDone onClick = onDone,
outerPaddingValues = PaddingValues(top = dimens.spacingSmall)
) )
} }
} }
@ -507,14 +496,7 @@ private fun SendFailure(
onDone: () -> Unit, onDone: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
Column( Column(modifier) {
Modifier
.fillMaxHeight()
.verticalScroll(
rememberScrollState()
)
.then(modifier)
) {
Header( Header(
text = stringResource(R.string.send_failure_title), text = stringResource(R.string.send_failure_title),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
@ -545,7 +527,8 @@ private fun SendFailure(
PrimaryButton( PrimaryButton(
modifier = Modifier.padding(top = dimens.spacingSmall), modifier = Modifier.padding(top = dimens.spacingSmall),
text = stringResource(R.string.send_failure_button), text = stringResource(R.string.send_failure_button),
onClick = onDone onClick = onDone,
outerPaddingValues = PaddingValues(top = dimens.spacingSmall)
) )
} }
} }

View File

@ -85,7 +85,7 @@ fun Settings(
) )
.padding( .padding(
top = paddingValues.calculateTopPadding() + dimens.spacingDefault, top = paddingValues.calculateTopPadding() + dimens.spacingDefault,
bottom = dimens.spacingDefault, bottom = paddingValues.calculateTopPadding() + dimens.spacingDefault,
start = dimens.spacingDefault, start = dimens.spacingDefault,
end = dimens.spacingDefault end = dimens.spacingDefault
) )

View File

@ -1,9 +1,13 @@
package co.electriccoin.zcash.ui.screen.support.view package co.electriccoin.zcash.ui.screen.support.view
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Send import androidx.compose.material.icons.filled.Send
@ -17,7 +21,6 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -26,6 +29,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.component.Body
import co.electriccoin.zcash.ui.design.component.FormTextField
import co.electriccoin.zcash.ui.design.component.GradientSurface import co.electriccoin.zcash.ui.design.component.GradientSurface
import co.electriccoin.zcash.ui.design.theme.ZcashTheme import co.electriccoin.zcash.ui.design.theme.ZcashTheme
@ -68,9 +73,14 @@ fun Support(
} }
) { paddingValues -> ) { paddingValues ->
SupportMainContent( SupportMainContent(
paddingValues, message = message,
message, setMessage = setMessage,
setMessage modifier = Modifier.padding(
top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault,
bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingDefault,
start = ZcashTheme.dimens.spacingDefault,
end = ZcashTheme.dimens.spacingDefault
)
) )
if (isShowingDialog) { if (isShowingDialog) {
@ -101,17 +111,24 @@ private fun SupportTopAppBar(onBack: () -> Unit) {
} }
@Composable @Composable
@OptIn(ExperimentalMaterial3Api::class)
private fun SupportMainContent( private fun SupportMainContent(
paddingValues: PaddingValues,
message: String, message: String,
setMessage: (String) -> Unit setMessage: (String) -> Unit,
modifier: Modifier = Modifier
) { ) {
Column( Column(
Modifier Modifier
.padding(top = paddingValues.calculateTopPadding()) .fillMaxHeight()
.verticalScroll(
rememberScrollState()
)
.then(modifier)
) { ) {
TextField( Body(stringResource(id = R.string.support_information))
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
FormTextField(
value = message, value = message,
onValueChange = setMessage, onValueChange = setMessage,
modifier = Modifier modifier = Modifier
@ -119,7 +136,9 @@ private fun SupportMainContent(
label = { Text(text = stringResource(id = R.string.support_hint)) } label = { Text(text = stringResource(id = R.string.support_hint)) }
) )
Text(stringResource(id = R.string.support_disclaimer)) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge))
Body(stringResource(id = R.string.support_disclaimer))
} }
} }

View File

@ -2,6 +2,7 @@ package co.electriccoin.zcash.ui.screen.update
import android.content.Context import android.content.Context
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.BackHandler
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
@ -28,7 +29,7 @@ internal fun MainActivity.WrapCheckForUpdate() {
@Composable @Composable
private fun WrapCheckForUpdate(activity: ComponentActivity) { private fun WrapCheckForUpdate(activity: ComponentActivity) {
// TODO [#382]: https://github.com/zcash/secant-android-wallet/issues/382 // TODO [#403]: Manual testing of already implemented in-app update mechanisms
// TODO [#403]: https://github.com/zcash/secant-android-wallet/issues/403 // TODO [#403]: https://github.com/zcash/secant-android-wallet/issues/403
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
val checkUpdateViewModel by activity.viewModels<CheckUpdateViewModel> { val checkUpdateViewModel by activity.viewModels<CheckUpdateViewModel> {
@ -53,8 +54,9 @@ private fun WrapCheckForUpdate(activity: ComponentActivity) {
} }
} }
@VisibleForTesting
@Composable @Composable
private fun WrapUpdate( internal fun WrapUpdate(
activity: ComponentActivity, activity: ComponentActivity,
inputUpdateInfo: UpdateInfo inputUpdateInfo: UpdateInfo
) { ) {
@ -85,6 +87,16 @@ private fun WrapUpdate(
} }
} }
val onLaterAction = {
if (!updateInfo.isForce && updateInfo.state != UpdateState.Running) {
viewModel.remindLater()
}
}
BackHandler {
onLaterAction()
}
Update( Update(
snackbarHostState, snackbarHostState,
updateInfo, updateInfo,
@ -97,9 +109,7 @@ private fun WrapUpdate(
updateInfo.appUpdateInfo updateInfo.appUpdateInfo
) )
}, },
onLater = { onLater = onLaterAction,
viewModel.remindLater()
},
onReference = { onReference = {
openPlayStoreAppPage( openPlayStoreAppPage(
activity.applicationContext, activity.applicationContext,

View File

@ -1,6 +1,5 @@
package co.electriccoin.zcash.ui.screen.update.view package co.electriccoin.zcash.ui.screen.update.view
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@ -63,12 +62,6 @@ fun Update(
onLater: () -> Unit, onLater: () -> Unit,
onReference: () -> Unit onReference: () -> Unit
) { ) {
BackHandler(enabled = true) {
if (updateInfo.isForce) {
return@BackHandler
}
onLater()
}
Scaffold( Scaffold(
topBar = { topBar = {
UpdateTopAppBar(updateInfo) UpdateTopAppBar(updateInfo)
@ -80,13 +73,26 @@ fun Update(
UpdateBottomAppBar( UpdateBottomAppBar(
updateInfo, updateInfo,
onDownload, onDownload,
onLater onLater,
modifier = Modifier
.fillMaxWidth()
.padding(
vertical = ZcashTheme.dimens.spacingDefault,
horizontal = ZcashTheme.dimens.spacingDefault
)
) )
} }
) { paddingValues -> ) { paddingValues ->
UpdateContentNormal( UpdateContentNormal(
paddingValues, onReference,
onReference modifier = Modifier
.fillMaxWidth()
.padding(
top = paddingValues.calculateTopPadding(),
bottom = paddingValues.calculateBottomPadding(),
start = ZcashTheme.dimens.spacingDefault,
end = ZcashTheme.dimens.spacingDefault
)
) )
} }
UpdateOverlayRunning(updateInfo) UpdateOverlayRunning(updateInfo)
@ -134,16 +140,16 @@ private fun UpdateTopAppBar(updateInfo: UpdateInfo) {
private fun UpdateBottomAppBar( private fun UpdateBottomAppBar(
updateInfo: UpdateInfo, updateInfo: UpdateInfo,
onDownload: (state: UpdateState) -> Unit, onDownload: (state: UpdateState) -> Unit,
onLater: () -> Unit onLater: () -> Unit,
modifier: Modifier = Modifier
) { ) {
Column { Column(modifier) {
PrimaryButton( PrimaryButton(
onClick = { onDownload(UpdateState.Running) }, onClick = { onDownload(UpdateState.Running) },
text = stringResource(R.string.update_download_button), text = stringResource(R.string.update_download_button),
modifier = Modifier modifier = Modifier.testTag(UpdateTag.BTN_DOWNLOAD),
.fillMaxWidth() enabled = updateInfo.state != UpdateState.Running,
.testTag(UpdateTag.BTN_DOWNLOAD), outerPaddingValues = PaddingValues(all = ZcashTheme.dimens.spacingNone)
enabled = updateInfo.state != UpdateState.Running
) )
TertiaryButton( TertiaryButton(
@ -157,23 +163,20 @@ private fun UpdateBottomAppBar(
} }
} }
), ),
modifier = Modifier modifier = Modifier.testTag(UpdateTag.BTN_LATER),
.fillMaxWidth() enabled = !updateInfo.isForce && updateInfo.state != UpdateState.Running,
.testTag(UpdateTag.BTN_LATER), outerPaddingValues = PaddingValues(top = ZcashTheme.dimens.spacingSmall)
enabled = !updateInfo.isForce && updateInfo.state != UpdateState.Running
) )
} }
} }
@Composable @Composable
private fun UpdateContentNormal( private fun UpdateContentNormal(
paddingValues: PaddingValues, onReference: () -> Unit,
onReference: () -> Unit modifier: Modifier = Modifier
) { ) {
Column( Column(
Modifier modifier = modifier,
.fillMaxWidth()
.padding(top = paddingValues.calculateTopPadding()),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
// TODO [#17]: This suppression and magic number will get replaced once we have real assets // TODO [#17]: This suppression and magic number will get replaced once we have real assets

View File

@ -2,6 +2,7 @@
<string name="home_menu_content_description">Open menu</string> <string name="home_menu_content_description">Open menu</string>
<string name="home_button_receive">Receive</string> <string name="home_button_receive">Receive</string>
<string name="home_button_send">Send</string> <string name="home_button_send">Send</string>
<string name="home_information">You wont be able to transfer funds until your wallet is finished syncing. Please keep your device plugged in and the app open.</string>
<string name="home_menu_seed_phrase">My secret phrase</string> <string name="home_menu_seed_phrase">My secret phrase</string>
<string name="home_menu_settings">Settings</string> <string name="home_menu_settings">Settings</string>

View File

@ -20,6 +20,7 @@
<string name="onboarding_4_import_existing_wallet">Import an Existing Wallet</string> <string name="onboarding_4_import_existing_wallet">Import an Existing Wallet</string>
<string name="onboarding_short_header">Its time to set up your no-frills wallet, powered by Zcash</string> <string name="onboarding_short_header">Its time to set up your no-frills wallet, powered by Zcash</string>
<string name="onboarding_short_information">We need to create a new wallet or restore an existing one. Select your path:</string>
<string name="onboarding_short_create_new_wallet">Create New Wallet</string> <string name="onboarding_short_create_new_wallet">Create New Wallet</string>
<string name="onboarding_short_import_existing_wallet">Import an Existing Wallet</string> <string name="onboarding_short_import_existing_wallet">Import an Existing Wallet</string>
</resources> </resources>

View File

@ -9,6 +9,7 @@
<string name="support_confirmation_explanation"><xliff:g id="app_name" example="Zcash">%1$s</xliff:g> is about to open your email app with a pre-filled message.\n\nBe sure to hit send within your email app.</string> <string name="support_confirmation_explanation"><xliff:g id="app_name" example="Zcash">%1$s</xliff:g> is about to open your email app with a pre-filled message.\n\nBe sure to hit send within your email app.</string>
<!-- This is replaced by a resource overlay via app/build.gradle.kts --> <!-- This is replaced by a resource overlay via app/build.gradle.kts -->
<string name="support_email_address"></string> <string name="support_email_address"></string>
<string name="support_information">Please let us know about any problems you have had, or features you want to see in the future.</string>
<string name="support_disclaimer">Information provided is handled in accordance with our Privacy Policy.</string> <string name="support_disclaimer">Information provided is handled in accordance with our Privacy Policy.</string>
<string name="support_unable_to_open_email">Unable to launch email app.</string> <string name="support_unable_to_open_email">Unable to launch email app.</string>
</resources> </resources>