Year-month date picker implementation
This commit is contained in:
parent
cee77ea0f9
commit
43e1c36dfb
|
@ -0,0 +1,18 @@
|
|||
package co.electriccoin.zcash.ui.design.component
|
||||
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
|
||||
@Composable
|
||||
fun VerticalSpacer(height: Dp) {
|
||||
Spacer(Modifier.height(height))
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HorizontalSpacer(width: Dp) {
|
||||
Spacer(Modifier.width(width))
|
||||
}
|
|
@ -0,0 +1,292 @@
|
|||
package co.electriccoin.zcash.ui.design.component
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyItemScope
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import co.electriccoin.zcash.spackle.Twig
|
||||
import co.electriccoin.zcash.ui.design.theme.colors.ZashiColors
|
||||
import co.electriccoin.zcash.ui.design.theme.typography.ZashiTypography
|
||||
import kotlinx.coroutines.launch
|
||||
import java.text.DateFormatSymbols
|
||||
import java.time.Month
|
||||
import java.time.Year
|
||||
import java.time.YearMonth
|
||||
import kotlin.math.pow
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
fun ZashiYearMonthWheelDatePicker(
|
||||
modifier: Modifier = Modifier,
|
||||
verticallyVisibleItems: Int = 3,
|
||||
startYear: Year = Year.of(2016),
|
||||
endYear: Year = Year.now(),
|
||||
selectedYear: YearMonth = YearMonth.now(),
|
||||
onSelectionChanged: (YearMonth) -> Unit,
|
||||
) {
|
||||
val latestOnSelectionChanged by rememberUpdatedState(onSelectionChanged)
|
||||
var selectedDate by remember { mutableStateOf(selectedYear) }
|
||||
val months =
|
||||
listOf(
|
||||
Month.JANUARY,
|
||||
Month.FEBRUARY,
|
||||
Month.MARCH,
|
||||
Month.APRIL,
|
||||
Month.MAY,
|
||||
Month.JUNE,
|
||||
Month.JULY,
|
||||
Month.AUGUST,
|
||||
Month.SEPTEMBER,
|
||||
Month.OCTOBER,
|
||||
Month.NOVEMBER,
|
||||
Month.DECEMBER
|
||||
)
|
||||
val years = (startYear.value..endYear.value).toList()
|
||||
|
||||
LaunchedEffect(selectedDate) {
|
||||
Twig.debug { "Selection changed: $selectedDate" }
|
||||
latestOnSelectionChanged(selectedDate)
|
||||
}
|
||||
|
||||
Box(modifier = modifier) {
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.align(Alignment.Center),
|
||||
) {
|
||||
ZashiHorizontalDivider(color = ZashiColors.Surfaces.bgQuaternary, thickness = .5.dp)
|
||||
VerticalSpacer(31.dp)
|
||||
ZashiHorizontalDivider(color = ZashiColors.Surfaces.bgQuaternary, thickness = .5.dp)
|
||||
}
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
Spacer(Modifier.weight(.5f))
|
||||
WheelLazyList(
|
||||
modifier = Modifier.weight(1f),
|
||||
selection = maxOf(months.indexOf(selectedDate.month), 0),
|
||||
itemCount = months.size,
|
||||
itemVerticalOffset = verticallyVisibleItems,
|
||||
isInfiniteScroll = true,
|
||||
onFocusItem = { selectedDate = selectedDate.withMonth(months[it].value) },
|
||||
itemContent = {
|
||||
Text(
|
||||
text = DateFormatSymbols().months[months[it].value - 1],
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillParentMaxWidth(),
|
||||
style = ZashiTypography.header6,
|
||||
color = ZashiColors.Text.textPrimary,
|
||||
maxLines = 1
|
||||
)
|
||||
}
|
||||
)
|
||||
WheelLazyList(
|
||||
modifier = Modifier.weight(.75f),
|
||||
selection = years.indexOf(selectedDate.year),
|
||||
itemCount = years.size,
|
||||
itemVerticalOffset = verticallyVisibleItems,
|
||||
isInfiniteScroll = false,
|
||||
onFocusItem = { selectedDate = selectedDate.withYear(years[it]) },
|
||||
itemContent = {
|
||||
Text(
|
||||
text = years[it].toString(),
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillParentMaxWidth(),
|
||||
style = ZashiTypography.header6,
|
||||
color = ZashiColors.Text.textPrimary,
|
||||
maxLines = 1
|
||||
)
|
||||
}
|
||||
)
|
||||
Spacer(Modifier.weight(.5f))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
private fun WheelLazyList(
|
||||
itemCount: Int,
|
||||
selection: Int,
|
||||
itemVerticalOffset: Int,
|
||||
onFocusItem: (Int) -> Unit,
|
||||
isInfiniteScroll: Boolean,
|
||||
itemContent: @Composable LazyItemScope.(index: Int) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val latestOnFocusItem by rememberUpdatedState(onFocusItem)
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val haptic = LocalHapticFeedback.current
|
||||
val count = if (isInfiniteScroll) itemCount else itemCount + 2 * itemVerticalOffset
|
||||
val rowOffsetCount = maxOf(1, minOf(itemVerticalOffset, 4))
|
||||
val rowCount = (rowOffsetCount * 2) + 1
|
||||
val startIndex = if (isInfiniteScroll) selection + (itemCount * 1000) - itemVerticalOffset else selection
|
||||
val state = rememberLazyListState(startIndex)
|
||||
val itemHeightPx = with(LocalDensity.current) { 27.dp.toPx() }
|
||||
val height = 32.dp * rowCount
|
||||
val isScrollInProgress = state.isScrollInProgress
|
||||
|
||||
LaunchedEffect(itemCount) {
|
||||
coroutineScope.launch {
|
||||
state.scrollToItem(startIndex)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(key1 = isScrollInProgress) {
|
||||
if (!isScrollInProgress) {
|
||||
calculateIndexToFocus(state, height).let {
|
||||
val indexToFocus =
|
||||
if (isInfiniteScroll) {
|
||||
(it + rowOffsetCount) % itemCount
|
||||
} else {
|
||||
((it + rowOffsetCount) % count) - itemVerticalOffset
|
||||
}
|
||||
|
||||
latestOnFocusItem(indexToFocus)
|
||||
|
||||
if (state.firstVisibleItemScrollOffset != 0) {
|
||||
coroutineScope.launch {
|
||||
state.animateScrollToItem(it, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(state) {
|
||||
snapshotFlow { state.firstVisibleItemIndex }
|
||||
.collect {
|
||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
}
|
||||
}
|
||||
Box(
|
||||
modifier =
|
||||
modifier
|
||||
.height(height)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier =
|
||||
Modifier
|
||||
.height(height)
|
||||
.fillMaxWidth(),
|
||||
state = state,
|
||||
) {
|
||||
items(if (isInfiniteScroll) Int.MAX_VALUE else count) { index ->
|
||||
val (scale, alpha, translationY) =
|
||||
remember {
|
||||
derivedStateOf {
|
||||
val info = state.layoutInfo
|
||||
val middleOffset = info.viewportSize.height / 2
|
||||
val item = info.visibleItemsInfo.firstOrNull { it.index == index }
|
||||
val scrollOffset = if (item != null) item.offset + item.size / 2 else -1
|
||||
val coefficient = calculateCoefficient(middleOffset = middleOffset, offset = scrollOffset)
|
||||
val scale = calculateScale(coefficient)
|
||||
val alpha = calculateAlpha(coefficient)
|
||||
val translationY =
|
||||
calculateTranslationY(
|
||||
coefficient = coefficient,
|
||||
itemHeightPx = itemHeightPx,
|
||||
middleOffset = middleOffset,
|
||||
offset = scrollOffset
|
||||
)
|
||||
Triple(scale, alpha, translationY)
|
||||
}
|
||||
}.value
|
||||
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.height(height / rowCount)
|
||||
.fillMaxWidth()
|
||||
.graphicsLayer {
|
||||
this.alpha = alpha
|
||||
this.scaleX = scale
|
||||
this.scaleY = scale
|
||||
this.translationY = translationY
|
||||
},
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
if (isInfiniteScroll) {
|
||||
itemContent(index % itemCount)
|
||||
} else if (index >= rowOffsetCount && index < itemCount + rowOffsetCount) {
|
||||
itemContent((index - rowOffsetCount) % itemCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun calculateCoefficient(
|
||||
middleOffset: Int,
|
||||
offset: Int
|
||||
): Float {
|
||||
val diff = if (middleOffset > offset) middleOffset - offset else offset - middleOffset
|
||||
return (1f - (diff.toFloat() / middleOffset.toFloat())).coerceAtLeast(0f)
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun calculateScale(coefficient: Float): Float {
|
||||
return coefficient.coerceAtLeast(.6f)
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun calculateAlpha(coefficient: Float): Float {
|
||||
return coefficient.pow(1.1f)
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun calculateTranslationY(
|
||||
coefficient: Float,
|
||||
itemHeightPx: Float,
|
||||
middleOffset: Int,
|
||||
offset: Int
|
||||
): Float {
|
||||
// if (coefficient in 0.66f..1f) return 0f
|
||||
val exponentialCoefficient = 1.2f - 5f.pow(-(coefficient))
|
||||
val offsetBy = (1 - exponentialCoefficient) * itemHeightPx
|
||||
return if (middleOffset > offset) offsetBy else -offsetBy
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun calculateIndexToFocus(
|
||||
listState: LazyListState,
|
||||
height: Dp
|
||||
): Int {
|
||||
val currentItem = listState.layoutInfo.visibleItemsInfo.firstOrNull()
|
||||
var index = currentItem?.index ?: 0
|
||||
if (currentItem?.offset != 0 && currentItem != null && currentItem.offset <= -height.value * 3 / 10) {
|
||||
index++
|
||||
}
|
||||
return index
|
||||
}
|
|
@ -19,6 +19,7 @@ import co.electriccoin.zcash.ui.screen.integrations.viewmodel.IntegrationsViewMo
|
|||
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.height.RestoreBDHeightViewModel
|
||||
import co.electriccoin.zcash.ui.screen.restore.seed.RestoreSeedViewModel
|
||||
import co.electriccoin.zcash.ui.screen.restoresuccess.viewmodel.RestoreSuccessViewModel
|
||||
|
@ -156,4 +157,5 @@ val viewModelModule =
|
|||
viewModelOf(::BalanceViewModel)
|
||||
viewModelOf(::HomeViewModel)
|
||||
viewModelOf(::RestoreBDHeightViewModel)
|
||||
viewModelOf(::RestoreBDDateViewModel)
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ import co.electriccoin.zcash.ui.design.animation.ScreenAnimation.popEnterTransit
|
|||
import co.electriccoin.zcash.ui.design.animation.ScreenAnimation.popExitTransition
|
||||
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.height.AndroidRestoreBDHeight
|
||||
import co.electriccoin.zcash.ui.screen.restore.height.RestoreBDHeight
|
||||
import co.electriccoin.zcash.ui.screen.restore.seed.AndroidRestoreSeed
|
||||
|
@ -101,6 +103,9 @@ fun MainActivity.RestoreNavigation() {
|
|||
composable<RestoreBDHeight> {
|
||||
AndroidRestoreBDHeight()
|
||||
}
|
||||
composable<RestoreBDDate> {
|
||||
AndroidRestoreBDDate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package co.electriccoin.zcash.ui.screen.restore.date
|
||||
|
||||
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 AndroidRestoreBDDate() {
|
||||
val vm = koinViewModel<RestoreBDDateViewModel>()
|
||||
val state by vm.state.collectAsStateWithLifecycle()
|
||||
val dialogState by vm.dialogState.collectAsStateWithLifecycle()
|
||||
RestoreBDDateView(state)
|
||||
BackHandler { state.onBack() }
|
||||
RestoreSeedDialog(dialogState)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data object RestoreBDDate
|
|
@ -0,0 +1,10 @@
|
|||
package co.electriccoin.zcash.ui.screen.restore.date
|
||||
|
||||
import co.electriccoin.zcash.ui.design.component.ButtonState
|
||||
import co.electriccoin.zcash.ui.design.component.IconButtonState
|
||||
|
||||
data class RestoreBDDateState(
|
||||
val next: ButtonState,
|
||||
val dialogButton: IconButtonState,
|
||||
val onBack: () -> Unit
|
||||
)
|
|
@ -0,0 +1,156 @@
|
|||
@file:Suppress("TooManyFunctions")
|
||||
|
||||
package co.electriccoin.zcash.ui.screen.restore.date
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.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.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
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.ZashiButton
|
||||
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.component.ZashiYearMonthWheelDatePicker
|
||||
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.orDark
|
||||
import co.electriccoin.zcash.ui.design.util.scaffoldPadding
|
||||
import co.electriccoin.zcash.ui.design.util.stringRes
|
||||
|
||||
@Composable
|
||||
fun RestoreBDDateView(state: RestoreBDDateState) {
|
||||
BlankBgScaffold(
|
||||
topBar = { AppBar(state) },
|
||||
bottomBar = {},
|
||||
content = { padding ->
|
||||
Content(
|
||||
state = state,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.scaffoldPadding(padding)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Content(
|
||||
state: RestoreBDDateState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.restore_bd_date_subtitle),
|
||||
style = ZashiTypography.header6,
|
||||
color = ZashiColors.Text.textPrimary,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.restore_bd_date_message),
|
||||
style = ZashiTypography.textSm,
|
||||
color = ZashiColors.Text.textPrimary
|
||||
)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
|
||||
ZashiYearMonthWheelDatePicker(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {}
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
|
||||
Spacer(Modifier.weight(1f))
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
Image(
|
||||
painterResource(R.drawable.ic_info),
|
||||
contentDescription = "",
|
||||
colorFilter = ColorFilter.tint(color = ZashiColors.Utility.Indigo.utilityIndigo700)
|
||||
)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 2.dp),
|
||||
text = stringResource(R.string.restore_bd_date_note),
|
||||
style = ZashiTypography.textXs,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = ZashiColors.Utility.Indigo.utilityIndigo700
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
|
||||
ZashiButton(
|
||||
state.next,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AppBar(state: RestoreBDDateState) {
|
||||
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 {
|
||||
RestoreBDDateView(
|
||||
state =
|
||||
RestoreBDDateState(
|
||||
next = ButtonState(stringRes("Estimate")) {},
|
||||
dialogButton = IconButtonState(R.drawable.ic_restore_dialog) {},
|
||||
onBack = {}
|
||||
)
|
||||
)
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package co.electriccoin.zcash.ui.screen.restore.date
|
||||
|
||||
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 RestoreBDDateViewModel(
|
||||
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<RestoreBDDateState> = MutableStateFlow(createState()).asStateFlow()
|
||||
|
||||
private fun createState() =
|
||||
RestoreBDDateState(
|
||||
next = ButtonState(stringRes(R.string.restore_bd_height_btn), onClick = ::onEstimateClick),
|
||||
dialogButton = IconButtonState(icon = R.drawable.ic_info, onClick = ::onInfoButtonClick),
|
||||
onBack = ::onBack,
|
||||
)
|
||||
|
||||
private fun onEstimateClick() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
private fun onBack() {
|
||||
navigationRouter.back()
|
||||
}
|
||||
|
||||
private fun onInfoButtonClick() {
|
||||
isDialogVisible.update { true }
|
||||
}
|
||||
|
||||
private fun onCloseDialogClick() {
|
||||
isDialogVisible.update { false }
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import co.electriccoin.zcash.ui.design.component.IconButtonState
|
|||
import co.electriccoin.zcash.ui.design.component.TextFieldState
|
||||
import co.electriccoin.zcash.ui.design.util.stringRes
|
||||
import co.electriccoin.zcash.ui.screen.restore.RestoreSeedDialogState
|
||||
import co.electriccoin.zcash.ui.screen.restore.date.RestoreBDDate
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
@ -59,7 +60,7 @@ class RestoreBDHeightViewModel(
|
|||
)
|
||||
|
||||
private fun onEstimateClick() {
|
||||
// do nothing
|
||||
navigationRouter.forward(RestoreBDDate)
|
||||
}
|
||||
|
||||
private fun onRestoreClick() {
|
||||
|
|
|
@ -23,7 +23,6 @@ import kotlinx.coroutines.flow.update
|
|||
class RestoreSeedViewModel(
|
||||
private val navigationRouter: NavigationRouter
|
||||
) : ViewModel() {
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private val seedWords =
|
||||
MutableStateFlow(
|
||||
|
|
|
@ -26,4 +26,10 @@
|
|||
<string name="restore_bd_text_field_hint">Enter number</string>
|
||||
<string name="restore_bd_text_field_note">Wallet Birthday Height is the point in time when your wallet was created.</string>
|
||||
|
||||
<string name="restore_bd_date_subtitle">First Wallet Transaction</string>
|
||||
<string name="restore_bd_date_message">Entering the block height at which your wallet was created reduces the
|
||||
number of blocks that need to be scanned to recover your wallet.</string>
|
||||
<string name="restore_bd_date_note">If you’re not sure, choose an earlier date.</string>
|
||||
<string name="restore_bd_date_next">Next</string>
|
||||
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue