diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiSpacer.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiSpacer.kt index 8f4a4e60f..ba9d960d5 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiSpacer.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiSpacer.kt @@ -1,5 +1,7 @@ package co.electriccoin.zcash.ui.design.component +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.width @@ -12,6 +14,16 @@ fun VerticalSpacer(height: Dp) { Spacer(Modifier.height(height)) } +@Composable +fun ColumnScope.VerticalSpacer(weight: Float) { + Spacer(Modifier.weight(weight)) +} + +@Composable +fun RowScope.VerticalSpacer(weight: Float) { + Spacer(Modifier.weight(weight)) +} + @Composable fun HorizontalSpacer(width: Dp) { Spacer(Modifier.width(width)) diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiYearMonthWheelDatePicker.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiYearMonthWheelDatePicker.kt index 119fc90d9..7dea799b9 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiYearMonthWheelDatePicker.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiYearMonthWheelDatePicker.kt @@ -78,9 +78,9 @@ fun ZashiYearMonthWheelDatePicker( Box(modifier = modifier) { Column( modifier = - Modifier - .fillMaxWidth() - .align(Alignment.Center), + Modifier + .fillMaxWidth() + .align(Alignment.Center), ) { ZashiHorizontalDivider(color = ZashiColors.Surfaces.bgQuaternary, thickness = .5.dp) VerticalSpacer(31.dp) @@ -189,15 +189,15 @@ private fun WheelLazyList( } Box( modifier = - modifier - .height(height) - .fillMaxWidth(), + modifier + .height(height) + .fillMaxWidth(), ) { LazyColumn( modifier = - Modifier - .height(height) - .fillMaxWidth(), + Modifier + .height(height) + .fillMaxWidth(), state = state, ) { items(if (isInfiniteScroll) Int.MAX_VALUE else count) { index -> @@ -224,15 +224,15 @@ private fun WheelLazyList( Box( modifier = - Modifier - .height(height / rowCount) - .fillMaxWidth() - .graphicsLayer { - this.alpha = alpha - this.scaleX = scale - this.scaleY = scale - this.translationY = translationY - }, + Modifier + .height(height / rowCount) + .fillMaxWidth() + .graphicsLayer { + this.alpha = alpha + this.scaleX = scale + this.scaleY = scale + this.translationY = translationY + }, contentAlignment = Alignment.Center, ) { if (isInfiniteScroll) { diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/di/ViewModelModule.kt b/ui-lib/src/main/java/co/electriccoin/zcash/di/ViewModelModule.kt index a432e2cc4..a1d3466c6 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/di/ViewModelModule.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/di/ViewModelModule.kt @@ -20,6 +20,7 @@ import co.electriccoin.zcash.ui.screen.qrcode.viewmodel.QrCodeViewModel import co.electriccoin.zcash.ui.screen.receive.viewmodel.ReceiveViewModel import co.electriccoin.zcash.ui.screen.request.viewmodel.RequestViewModel import co.electriccoin.zcash.ui.screen.restore.date.RestoreBDDateViewModel +import co.electriccoin.zcash.ui.screen.restore.estimation.RestoreBDEstimationViewModel import co.electriccoin.zcash.ui.screen.restore.height.RestoreBDHeightViewModel import co.electriccoin.zcash.ui.screen.restore.seed.RestoreSeedViewModel import co.electriccoin.zcash.ui.screen.restoresuccess.viewmodel.RestoreSuccessViewModel @@ -158,4 +159,5 @@ val viewModelModule = viewModelOf(::HomeViewModel) viewModelOf(::RestoreBDHeightViewModel) viewModelOf(::RestoreBDDateViewModel) + viewModelOf(::RestoreBDEstimationViewModel) } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/onboarding/AndroidOnboarding.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/onboarding/AndroidOnboarding.kt index f7f842407..a272da82f 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/onboarding/AndroidOnboarding.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/onboarding/AndroidOnboarding.kt @@ -31,6 +31,8 @@ import co.electriccoin.zcash.ui.screen.flexa.FlexaViewModel import co.electriccoin.zcash.ui.screen.onboarding.view.Onboarding import co.electriccoin.zcash.ui.screen.restore.date.AndroidRestoreBDDate import co.electriccoin.zcash.ui.screen.restore.date.RestoreBDDate +import co.electriccoin.zcash.ui.screen.restore.estimation.AndroidRestoreBDEstimation +import co.electriccoin.zcash.ui.screen.restore.estimation.RestoreBDEstimation import co.electriccoin.zcash.ui.screen.restore.height.AndroidRestoreBDHeight import co.electriccoin.zcash.ui.screen.restore.height.RestoreBDHeight import co.electriccoin.zcash.ui.screen.restore.seed.AndroidRestoreSeed @@ -106,6 +108,9 @@ fun MainActivity.RestoreNavigation() { composable { AndroidRestoreBDDate() } + composable { + AndroidRestoreBDEstimation() + } } } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/qrcode/view/QrCodeView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/qrcode/view/QrCodeView.kt index 2f5379607..62e417abb 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/qrcode/view/QrCodeView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/qrcode/view/QrCodeView.kt @@ -218,7 +218,7 @@ private fun QrCodeBottomBar( ZashiButton( text = stringResource(id = R.string.qr_code_copy_btn), - icon = R.drawable.ic_copy, + icon = R.drawable.ic_qr_copy, onClick = { state.onAddressCopy(state.walletAddress.address) }, colors = ZashiButtonDefaults.secondaryColors(), modifier = diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/date/RestoreBDDateViewModel.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/date/RestoreBDDateViewModel.kt index 0c1a9094b..8ec00ef5e 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/date/RestoreBDDateViewModel.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/date/RestoreBDDateViewModel.kt @@ -9,6 +9,7 @@ import co.electriccoin.zcash.ui.design.component.ButtonState import co.electriccoin.zcash.ui.design.component.IconButtonState import co.electriccoin.zcash.ui.design.util.stringRes import co.electriccoin.zcash.ui.screen.restore.RestoreSeedDialogState +import co.electriccoin.zcash.ui.screen.restore.estimation.RestoreBDEstimation import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -46,7 +47,7 @@ class RestoreBDDateViewModel( ) private fun onEstimateClick() { - // do nothing + navigationRouter.forward(RestoreBDEstimation) } private fun onBack() { diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/estimation/AndroidRestoreBDEstimation.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/estimation/AndroidRestoreBDEstimation.kt new file mode 100644 index 000000000..ed3a7283f --- /dev/null +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/estimation/AndroidRestoreBDEstimation.kt @@ -0,0 +1,24 @@ +package co.electriccoin.zcash.ui.screen.restore.estimation + +import androidx.activity.compose.BackHandler +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import co.electriccoin.zcash.ui.screen.restore.RestoreSeedDialog +import kotlinx.serialization.Serializable +import org.koin.androidx.compose.koinViewModel + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AndroidRestoreBDEstimation() { + val vm = koinViewModel() + val state by vm.state.collectAsStateWithLifecycle() + val dialogState by vm.dialogState.collectAsStateWithLifecycle() + RestoreBDEstimationView(state) + BackHandler { state.onBack() } + RestoreSeedDialog(dialogState) +} + +@Serializable +data object RestoreBDEstimation diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/estimation/RestoreBDEstimationState.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/estimation/RestoreBDEstimationState.kt new file mode 100644 index 000000000..7ccdf2b9b --- /dev/null +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/estimation/RestoreBDEstimationState.kt @@ -0,0 +1,13 @@ +package co.electriccoin.zcash.ui.screen.restore.estimation + +import co.electriccoin.zcash.ui.design.component.ButtonState +import co.electriccoin.zcash.ui.design.component.IconButtonState +import co.electriccoin.zcash.ui.design.util.StringResource + +data class RestoreBDEstimationState( + val text: StringResource, + val onBack: () -> Unit, + val dialogButton: IconButtonState, + val copy: ButtonState, + val restore: ButtonState, +) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/estimation/RestoreBDEstimationView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/estimation/RestoreBDEstimationView.kt new file mode 100644 index 000000000..321941f8d --- /dev/null +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/estimation/RestoreBDEstimationView.kt @@ -0,0 +1,141 @@ +@file:Suppress("TooManyFunctions") + +package co.electriccoin.zcash.ui.screen.restore.estimation + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import co.electriccoin.zcash.ui.R +import co.electriccoin.zcash.ui.common.appbar.ZashiTopAppBarTags +import co.electriccoin.zcash.ui.design.component.BlankBgScaffold +import co.electriccoin.zcash.ui.design.component.ButtonState +import co.electriccoin.zcash.ui.design.component.IconButtonState +import co.electriccoin.zcash.ui.design.component.VerticalSpacer +import co.electriccoin.zcash.ui.design.component.ZashiButton +import co.electriccoin.zcash.ui.design.component.ZashiButtonDefaults +import co.electriccoin.zcash.ui.design.component.ZashiIconButton +import co.electriccoin.zcash.ui.design.component.ZashiSmallTopAppBar +import co.electriccoin.zcash.ui.design.component.ZashiTopAppBarBackNavigation +import co.electriccoin.zcash.ui.design.newcomponent.PreviewScreens +import co.electriccoin.zcash.ui.design.theme.ZcashTheme +import co.electriccoin.zcash.ui.design.theme.colors.ZashiColors +import co.electriccoin.zcash.ui.design.theme.typography.ZashiTypography +import co.electriccoin.zcash.ui.design.util.getValue +import co.electriccoin.zcash.ui.design.util.orDark +import co.electriccoin.zcash.ui.design.util.scaffoldPadding +import co.electriccoin.zcash.ui.design.util.stringRes + +@Composable +fun RestoreBDEstimationView(state: RestoreBDEstimationState) { + BlankBgScaffold( + topBar = { AppBar(state) }, + bottomBar = {}, + content = { padding -> + Content( + state = state, + modifier = + Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .scaffoldPadding(padding) + ) + } + ) +} + +@Composable +private fun Content( + state: RestoreBDEstimationState, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + ) { + Text( + text = stringResource(R.string.restore_bd_estimation_subtitle), + style = ZashiTypography.header6, + color = ZashiColors.Text.textPrimary, + fontWeight = FontWeight.SemiBold + ) + VerticalSpacer(8.dp) + Text( + text = stringResource(R.string.restore_bd_estimation_message), + style = ZashiTypography.textSm, + color = ZashiColors.Text.textPrimary + ) + VerticalSpacer(56.dp) + Text( + modifier = Modifier.fillMaxWidth(), + text = state.text.getValue(), + color = ZashiColors.Text.textPrimary, + style = ZashiTypography.header2, + fontWeight = FontWeight.SemiBold, + textAlign = TextAlign.Center + ) + VerticalSpacer(12.dp) + ZashiButton( + modifier = Modifier.align(Alignment.CenterHorizontally), + state = state.copy, + colors = ZashiButtonDefaults.tertiaryColors() + ) + VerticalSpacer(24.dp) + VerticalSpacer(1f) + ZashiButton( + state = state.restore, + modifier = Modifier.fillMaxWidth(), + ) + } +} + +@Composable +private fun AppBar(state: RestoreBDEstimationState) { + ZashiSmallTopAppBar( + title = stringResource(R.string.restore_title), + navigationAction = { + ZashiTopAppBarBackNavigation( + onBack = state.onBack, + modifier = Modifier.testTag(ZashiTopAppBarTags.BACK) + ) + }, + regularActions = { + ZashiIconButton(state.dialogButton, modifier = Modifier.size(40.dp)) + Spacer(Modifier.width(20.dp)) + }, + colors = + ZcashTheme.colors.topAppBarColors orDark + ZcashTheme.colors.topAppBarColors.copyColors( + containerColor = Color.Transparent + ), + ) +} + +@PreviewScreens +@Composable +private fun Preview() = + ZcashTheme { + RestoreBDEstimationView( + state = + RestoreBDEstimationState( + restore = ButtonState(stringRes("Estimate")) {}, + dialogButton = IconButtonState(R.drawable.ic_restore_dialog) {}, + onBack = {}, + text = stringRes("123456"), + copy = ButtonState(stringRes("Copy"), icon = R.drawable.ic_copy) {} + ) + ) + } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/estimation/RestoreBDEstimationViewModel.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/estimation/RestoreBDEstimationViewModel.kt new file mode 100644 index 000000000..543e6c74e --- /dev/null +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/estimation/RestoreBDEstimationViewModel.kt @@ -0,0 +1,65 @@ +package co.electriccoin.zcash.ui.screen.restore.estimation + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT +import co.electriccoin.zcash.ui.NavigationRouter +import co.electriccoin.zcash.ui.R +import co.electriccoin.zcash.ui.design.component.ButtonState +import co.electriccoin.zcash.ui.design.component.IconButtonState +import co.electriccoin.zcash.ui.design.util.stringRes +import co.electriccoin.zcash.ui.screen.restore.RestoreSeedDialogState +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.WhileSubscribed +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.update + +class RestoreBDEstimationViewModel( + private val navigationRouter: NavigationRouter +) : ViewModel() { + private val isDialogVisible = MutableStateFlow(false) + + val dialogState = + isDialogVisible + .map { isDialogVisible -> + RestoreSeedDialogState( + ::onCloseDialogClick + ).takeIf { isDialogVisible } + } + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), + initialValue = null + ) + + val state: StateFlow = MutableStateFlow(createState()).asStateFlow() + + private fun createState() = + RestoreBDEstimationState( + dialogButton = IconButtonState(icon = R.drawable.ic_info, onClick = ::onInfoButtonClick), + onBack = ::onBack, + text = stringRes("123456"), + copy = ButtonState(stringRes(R.string.restore_bd_estimation_copy), icon = R.drawable.ic_copy) {}, + restore = ButtonState(stringRes(R.string.restore_bd_estimation_restore), onClick = ::onRestoreClick), + ) + + private fun onRestoreClick() { + // do nothing + } + + private fun onBack() { + navigationRouter.back() + } + + private fun onInfoButtonClick() { + isDialogVisible.update { true } + } + + private fun onCloseDialogClick() { + isDialogVisible.update { false } + } +} diff --git a/ui-lib/src/main/res/ui/common/drawable-night/ic_copy.xml b/ui-lib/src/main/res/ui/common/drawable-night/ic_copy.xml new file mode 100644 index 000000000..6df43d482 --- /dev/null +++ b/ui-lib/src/main/res/ui/common/drawable-night/ic_copy.xml @@ -0,0 +1,13 @@ + + + diff --git a/ui-lib/src/main/res/ui/common/drawable/ic_copy.xml b/ui-lib/src/main/res/ui/common/drawable/ic_copy.xml new file mode 100644 index 000000000..1636fcd26 --- /dev/null +++ b/ui-lib/src/main/res/ui/common/drawable/ic_copy.xml @@ -0,0 +1,13 @@ + + + diff --git a/ui-lib/src/main/res/ui/qr_code/drawable-night/ic_copy.xml b/ui-lib/src/main/res/ui/qr_code/drawable-night/ic_qr_copy.xml similarity index 100% rename from ui-lib/src/main/res/ui/qr_code/drawable-night/ic_copy.xml rename to ui-lib/src/main/res/ui/qr_code/drawable-night/ic_qr_copy.xml diff --git a/ui-lib/src/main/res/ui/qr_code/drawable/ic_copy.xml b/ui-lib/src/main/res/ui/qr_code/drawable/ic_qr_copy.xml similarity index 100% rename from ui-lib/src/main/res/ui/qr_code/drawable/ic_copy.xml rename to ui-lib/src/main/res/ui/qr_code/drawable/ic_qr_copy.xml diff --git a/ui-lib/src/main/res/ui/restore/values/strings.xml b/ui-lib/src/main/res/ui/restore/values/strings.xml index 61827fc28..4f0170ef4 100644 --- a/ui-lib/src/main/res/ui/restore/values/strings.xml +++ b/ui-lib/src/main/res/ui/restore/values/strings.xml @@ -32,4 +32,9 @@ If you’re not sure, choose an earlier date. Next + Estimated Block Height + Zashi will scan and recover all transactions made after the following block number. + Copy + Restore +