Updated on 2026-08-14
This commit is contained in:
parent
4b3d59fa0f
commit
e6c8647f03
14 changed files with 1132 additions and 27 deletions
|
|
@ -4,7 +4,9 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.arkivanov.decompose.FaultyDecomposeApi
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.stack.Children
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.stack.animation.fade
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.stack.animation.slide
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.stack.animation.stackAnimation
|
||||
import com.arkivanov.decompose.extensions.compose.jetpack.subscribeAsState
|
||||
|
|
@ -22,6 +24,7 @@ import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChild
|
|||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.MultiWalletBackupComponent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.chooseoption.Wallet1ChooseOptionComponent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.createwallet.MultiWalletCreateWalletComponent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.MultiWalletSeedPhraseComponent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletModel
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletState
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletState.Step.*
|
||||
|
|
@ -30,6 +33,7 @@ import com.tangem.features.onboarding.v2.multiwallet.impl.ui.WalletArtworksState
|
|||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor(
|
||||
|
|
@ -44,7 +48,7 @@ internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor
|
|||
when (model.state.value.currentStep) {
|
||||
CreateWallet -> WalletArtworksState.Folded
|
||||
ChooseBackupOption -> WalletArtworksState.Fan
|
||||
SeedPhrase -> WalletArtworksState.Hidden
|
||||
SeedPhrase -> WalletArtworksState.Folded
|
||||
AddBackupDevice -> WalletArtworksState.Unfold(WalletArtworksState.Unfold.Step.First)
|
||||
FinishBackup -> WalletArtworksState.Stack()
|
||||
Done -> error("Done state should not be used as starting state")
|
||||
|
|
@ -95,7 +99,15 @@ internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor
|
|||
context = childContext,
|
||||
onNextStep = ::handleNavigationEvent,
|
||||
)
|
||||
SeedPhrase -> TODO()
|
||||
SeedPhrase -> MultiWalletSeedPhraseComponent(
|
||||
context = childContext,
|
||||
params = MultiWalletChildParams(
|
||||
multiWalletState = model.state,
|
||||
parentParams = params,
|
||||
),
|
||||
onNextStep = ::handleNavigationEvent,
|
||||
backButtonClickFlow = MutableSharedFlow(),
|
||||
)
|
||||
AddBackupDevice -> MultiWalletBackupComponent(
|
||||
context = childContext,
|
||||
params = MultiWalletChildParams(
|
||||
|
|
@ -115,7 +127,6 @@ internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor
|
|||
artworksState.value = WalletArtworksState.Fan
|
||||
}
|
||||
SeedPhrase -> {
|
||||
artworksState.value = WalletArtworksState.Hidden
|
||||
}
|
||||
AddBackupDevice -> {
|
||||
artworksState.value = WalletArtworksState.Unfold(WalletArtworksState.Unfold.Step.First)
|
||||
|
|
@ -144,6 +155,7 @@ internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(FaultyDecomposeApi::class)
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val stackState by childStack.subscribeAsState()
|
||||
|
|
@ -154,10 +166,19 @@ internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor
|
|||
state = state,
|
||||
modifier = modifier,
|
||||
artworksState = artworksState,
|
||||
isSeedPhraseState = stackState.active.configuration == SeedPhrase,
|
||||
childContent = { mdfr ->
|
||||
Children(
|
||||
stack = stackState,
|
||||
animation = stackAnimation(slide()),
|
||||
animation = stackAnimation(
|
||||
selector = { one, two, direction ->
|
||||
if (one.configuration == SeedPhrase || two.configuration == SeedPhrase) {
|
||||
fade()
|
||||
} else {
|
||||
slide()
|
||||
}
|
||||
},
|
||||
),
|
||||
) {
|
||||
it.instance.Content(mdfr)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildComponent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.model.MultiWalletSeedPhraseModel
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.ui.MultiWalletSeedPhrase
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletState
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
class MultiWalletSeedPhraseComponent(
|
||||
context: AppComponentContext,
|
||||
params: MultiWalletChildParams,
|
||||
onNextStep: (OnboardingMultiWalletState.Step) -> Unit,
|
||||
backButtonClickFlow: SharedFlow<Unit>,
|
||||
) : AppComponentContext by context, MultiWalletChildComponent {
|
||||
|
||||
private val model: MultiWalletSeedPhraseModel = getOrCreateModel()
|
||||
|
||||
init {
|
||||
componentScope.launch {
|
||||
backButtonClickFlow.collect {
|
||||
model.onBack()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
MultiWalletSeedPhrase(
|
||||
modifier = modifier,
|
||||
state = state,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.model
|
||||
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.ui.state.MultiWalletSeedPhraseUM
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import javax.inject.Inject
|
||||
|
||||
@ComponentScoped
|
||||
class MultiWalletSeedPhraseModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
) : Model() {
|
||||
|
||||
private val state = MutableStateFlow(SeedPhraseState())
|
||||
|
||||
private val _uiState = MutableStateFlow<MultiWalletSeedPhraseUM>(
|
||||
MultiWalletSeedPhraseUM.Start(
|
||||
onImportSeedPhraseClicked = {},
|
||||
onGenerateSeedPhraseClicked = {
|
||||
openGeneratedSeedPhrase()
|
||||
},
|
||||
onLearnMoreClicked = {},
|
||||
),
|
||||
)
|
||||
|
||||
val uiState = _uiState.asStateFlow()
|
||||
|
||||
fun onBack() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
private fun openGeneratedSeedPhrase() {
|
||||
// TODO state.value.generatedWords
|
||||
val words = listOf("word, word")
|
||||
|
||||
_uiState.value = MultiWalletSeedPhraseUM.GenerateSeedPhrase(
|
||||
words24OptionSelected = false,
|
||||
words = words.mapIndexed { index, s ->
|
||||
MultiWalletSeedPhraseUM.GenerateSeedPhrase.MnemonicGridItem(index, s)
|
||||
}.toImmutableList(),
|
||||
onWords24OptionSwitch = {
|
||||
_uiState.update {
|
||||
if (it !is MultiWalletSeedPhraseUM.GenerateSeedPhrase) return@update it
|
||||
it.copy(words24OptionSelected = it.words24OptionSelected.not())
|
||||
}
|
||||
state.update { it.copy(words24Option = it.words24Option.not()) }
|
||||
},
|
||||
onContinueClick = {
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.model
|
||||
|
||||
data class SeedPhraseState(
|
||||
val generatedWords: List<String>? = null,
|
||||
val words24Option: Boolean = false,
|
||||
)
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.ui
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedContentTransitionScope
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.ui.state.MultiWalletSeedPhraseUM
|
||||
|
||||
@Composable
|
||||
fun MultiWalletSeedPhrase(state: MultiWalletSeedPhraseUM, modifier: Modifier = Modifier) {
|
||||
AnimatedContent(
|
||||
modifier = modifier.navigationBarsPadding(),
|
||||
targetState = state,
|
||||
transitionSpec = {
|
||||
val direction = if (initialState.order < targetState.order) {
|
||||
AnimatedContentTransitionScope.SlideDirection.Start
|
||||
} else {
|
||||
AnimatedContentTransitionScope.SlideDirection.End
|
||||
}
|
||||
|
||||
slideIntoContainer(towards = direction, animationSpec = tween())
|
||||
.togetherWith(slideOutOfContainer(towards = direction, animationSpec = tween()))
|
||||
},
|
||||
contentKey = { st -> st::class.java },
|
||||
label = "animatedContent",
|
||||
) { st ->
|
||||
when (st) {
|
||||
is MultiWalletSeedPhraseUM.Start -> MultiWalletSeedPhraseStart(state = st)
|
||||
is MultiWalletSeedPhraseUM.GenerateSeedPhrase -> MultiWalletSeedPhraseWords(state = st)
|
||||
is MultiWalletSeedPhraseUM.GeneratedWordsCheck -> MultiWalletSeedPhraseWordsCheck(state = st)
|
||||
is MultiWalletSeedPhraseUM.Import -> MultiWalletSeedPhraseImport(state = st)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.ui
|
||||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.ui.state.MultiWalletSeedPhraseUM
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.ui.utils.InvalidWordsColorTransformation
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun MultiWalletSeedPhraseImport(state: MultiWalletSeedPhraseUM.Import, modifier: Modifier = Modifier) {
|
||||
Box(modifier.fillMaxSize()) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.onboarding_seed_import_message),
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.padding(start = 36.dp, end = 36.dp, top = 24.dp, bottom = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
PhraseBlock(
|
||||
state = state,
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
||||
OutlineTextFieldWithIcon(
|
||||
modifier = modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
value = state.passPhrase,
|
||||
onValueChange = state.passPhraseChange,
|
||||
iconResId = R.drawable.ic_information_24,
|
||||
iconColor = TangemTheme.colors.icon.informative,
|
||||
label = stringResource(id = R.string.common_passphrase),
|
||||
placeholder = stringResource(id = R.string.send_optional_field),
|
||||
onIconClick = state.onPassphraseInfoClick,
|
||||
)
|
||||
|
||||
Box(Modifier.weight(1f)) {
|
||||
PrimaryButtonIconEnd(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomStart)
|
||||
.padding(16.dp)
|
||||
.fillMaxWidth(),
|
||||
text = stringResource(id = R.string.common_import),
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
enabled = state.createWalletEnabled,
|
||||
showProgress = state.createWalletProgress,
|
||||
onClick = state.createWalletClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val keyboard by keyboardAsState()
|
||||
if (keyboard is Keyboard.Opened) {
|
||||
SuggestionsBlock(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.imePadding(),
|
||||
suggestionsList = state.suggestionsList,
|
||||
onClick = { index -> state.onSuggestionClick(state.suggestionsList[index]) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
PassphraseInfoBottomSheet(state.infoBottomSheetConfig)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PhraseBlock(state: MultiWalletSeedPhraseUM.Import, modifier: Modifier = Modifier) {
|
||||
val warningColor = TangemTheme.colors.text.warning
|
||||
val invalidWordsColorTransformation = remember(state.invalidWords, warningColor) {
|
||||
InvalidWordsColorTransformation(
|
||||
wordsToBrush = state.invalidWords,
|
||||
style = SpanStyle(color = warningColor),
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(TangemTheme.dimens.size142),
|
||||
value = state.passPhrase,
|
||||
onValueChange = state.passPhraseChange,
|
||||
textStyle = TangemTheme.typography.body1,
|
||||
singleLine = false,
|
||||
visualTransformation = invalidWordsColorTransformation,
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.height(TangemTheme.dimens.size32),
|
||||
) {
|
||||
if (state.wordsErrorText != null) {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
text = state.wordsErrorText.resolveReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.warning,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SuggestionsBlock(
|
||||
suggestionsList: ImmutableList<String>,
|
||||
onClick: (Int) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
modifier = modifier,
|
||||
enter = fadeIn() + slideIn(initialOffset = { IntOffset(x = 200, y = 0) }),
|
||||
exit = slideOut(targetOffset = { IntOffset(x = -200, y = 0) }) + fadeOut(),
|
||||
visible = suggestionsList.isNotEmpty(),
|
||||
) {
|
||||
LazyRow(
|
||||
contentPadding = PaddingValues(horizontal = TangemTheme.dimens.size16),
|
||||
) {
|
||||
items(suggestionsList.size) { index ->
|
||||
SuggestionButton(
|
||||
modifier = Modifier.rowPadding(
|
||||
index = index,
|
||||
rowSize = suggestionsList.size,
|
||||
outSide = TangemTheme.dimens.size0,
|
||||
inSide = TangemTheme.dimens.size4,
|
||||
),
|
||||
text = suggestionsList[index],
|
||||
onClick = { onClick(index) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SuggestionButton(text: String, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Box(modifier = modifier.clickable(onClick = onClick)) {
|
||||
Notifier(
|
||||
text = text,
|
||||
backgroundColor = TangemTheme.colors.icon.primary1,
|
||||
textColor = TangemTheme.colors.text.primary2,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Modifier.rowPadding(index: Int, rowSize: Int, outSide: Dp, inSide: Dp): Modifier = when (index) {
|
||||
0 -> this.padding(start = outSide, end = inSide)
|
||||
rowSize - 1 -> this.padding(start = inSide, end = outSide)
|
||||
else -> this.padding(horizontal = inSide)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
TangemThemePreview(
|
||||
alwaysShowBottomSheets = false,
|
||||
) {
|
||||
MultiWalletSeedPhraseImport(
|
||||
state = MultiWalletSeedPhraseUM.Import(
|
||||
wordsErrorText = stringReference("Error"),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.ui
|
||||
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.style.LineBreak
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.SecondaryButton
|
||||
import com.tangem.core.ui.components.SpacerH32
|
||||
import com.tangem.core.ui.components.SpacerW8
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.ui.state.MultiWalletSeedPhraseUM
|
||||
|
||||
@Composable
|
||||
internal fun MultiWalletSeedPhraseStart(state: MultiWalletSeedPhraseUM.Start, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier.padding(top = 48.dp),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.weight(1f),
|
||||
verticalArrangement = Arrangement.spacedBy(28.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(56.dp),
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_onboarding_text_edit_56),
|
||||
contentDescription = null,
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.icon.warning.copy(alpha = 0.12f),
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius8),
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.size12, vertical = TangemTheme.dimens.size4),
|
||||
text = stringResource(id = R.string.onboarding_seed_phrase_intro_legacy),
|
||||
color = TangemTheme.colors.text.warning,
|
||||
style = TangemTheme.typography.body1,
|
||||
)
|
||||
}
|
||||
BodyContent(state)
|
||||
}
|
||||
SpacerH32()
|
||||
Buttons(state)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BodyContent(state: MultiWalletSeedPhraseUM.Start, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.onboarding_seed_intro_title),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.onboarding_seed_intro_message),
|
||||
style = TangemTheme.typography.body1
|
||||
.copy(lineBreak = LineBreak.Paragraph),
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 32.dp)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
ReadMoreBlock(state)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ReadMoreBlock(state: MultiWalletSeedPhraseUM.Start, modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp),
|
||||
) {
|
||||
val shape = TangemTheme.shapes.roundedCornersLarge
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.clip(shape)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = TangemTheme.colors.stroke.primary,
|
||||
shape = shape,
|
||||
)
|
||||
.clickable(onClick = state.onLearnMoreClicked)
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_arrow_top_right_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
)
|
||||
SpacerW8()
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
style = TangemTheme.typography.button,
|
||||
text = stringResource(id = R.string.onboarding_seed_button_read_more),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Buttons(state: MultiWalletSeedPhraseUM.Start, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
|
||||
.imePadding(),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
SecondaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(id = R.string.onboarding_seed_intro_button_generate),
|
||||
onClick = state.onGenerateSeedPhraseClicked,
|
||||
)
|
||||
SecondaryButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
text = stringResource(id = R.string.onboarding_seed_intro_button_import),
|
||||
onClick = state.onImportSeedPhraseClicked,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
TangemThemePreview {
|
||||
MultiWalletSeedPhraseStart(
|
||||
state = MultiWalletSeedPhraseUM.Start(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.ui
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
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.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.buttons.segmentedbutton.SegmentedButtons
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.ui.state.MultiWalletSeedPhraseUM
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.ui.state.MultiWalletSeedPhraseUM.GenerateSeedPhrase.MnemonicGridItem
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
@Composable
|
||||
fun MultiWalletSeedPhraseWords(state: MultiWalletSeedPhraseUM.GenerateSeedPhrase, modifier: Modifier = Modifier) {
|
||||
Column(modifier.fillMaxSize()) {
|
||||
SegmentSeedBlock(
|
||||
modifier = Modifier.padding(vertical = 20.dp),
|
||||
state = state,
|
||||
)
|
||||
|
||||
TitleBlock(state)
|
||||
|
||||
SeedPhraseGridBlock(
|
||||
mnemonicGridItems = state.words,
|
||||
modifier = Modifier
|
||||
.padding(top = 20.dp, bottom = 92.dp),
|
||||
)
|
||||
|
||||
Box(Modifier.weight(1f)) {
|
||||
PrimaryButton(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
|
||||
text = stringResource(id = R.string.common_continue),
|
||||
onClick = state.onContinueClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("ComplexCondition", "MagicNumber")
|
||||
@Composable
|
||||
private fun SegmentSeedBlock(state: MultiWalletSeedPhraseUM.GenerateSeedPhrase, modifier: Modifier = Modifier) {
|
||||
SegmentedButtons(
|
||||
modifier = modifier.padding(horizontal = TangemTheme.dimens.spacing76),
|
||||
config = persistentListOf(0, 1),
|
||||
initialSelectedItem = if (state.words24OptionSelected.not()) 0 else 1,
|
||||
onClick = {
|
||||
if (state.words24OptionSelected && it == 1 || state.words24OptionSelected.not() && it == 0) {
|
||||
state.onWords24OptionSwitch()
|
||||
}
|
||||
},
|
||||
) {
|
||||
Text(
|
||||
text = pluralStringResource(
|
||||
id = R.plurals.onboarding_seed_generate_words_count,
|
||||
count = if (it == 0) 12 else 24,
|
||||
if (it == 0) 12 else 24,
|
||||
),
|
||||
modifier = Modifier
|
||||
.padding(vertical = TangemTheme.dimens.spacing10)
|
||||
.fillMaxWidth(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
private fun TitleBlock(state: MultiWalletSeedPhraseUM.GenerateSeedPhrase, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.padding(horizontal = TangemTheme.dimens.size36)
|
||||
.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.onboarding_seed_generate_title),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
Text(
|
||||
text = pluralStringResource(
|
||||
id = R.plurals.onboarding_seed_generate_message_words_count,
|
||||
count = if (state.words24OptionSelected.not()) 12 else 24,
|
||||
if (state.words24OptionSelected.not()) 12 else 24,
|
||||
),
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SeedPhraseGridBlock(mnemonicGridItems: ImmutableList<MnemonicGridItem>, modifier: Modifier = Modifier) {
|
||||
VerticalGrid(
|
||||
modifier = modifier
|
||||
.verticalScroll(rememberScrollState()),
|
||||
items = mnemonicGridItems,
|
||||
) { item ->
|
||||
Row(
|
||||
modifier = Modifier.padding(all = TangemTheme.dimens.size8),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (LocalLayoutDirection.current == LayoutDirection.Ltr) {
|
||||
Text(
|
||||
modifier = Modifier.width(TangemTheme.dimens.size40),
|
||||
text = "${item.index}.",
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
)
|
||||
Text(
|
||||
text = item.mnemonic,
|
||||
style = TangemTheme.typography.button,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = item.mnemonic,
|
||||
style = TangemTheme.typography.button,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.width(TangemTheme.dimens.size40),
|
||||
text = "${item.index}.",
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private inline fun <T> VerticalGrid(
|
||||
items: ImmutableList<T>,
|
||||
modifier: Modifier = Modifier,
|
||||
crossinline content: @Composable (T) -> Unit,
|
||||
) {
|
||||
val columnLength = items.size / 2
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
) {
|
||||
for (index in 0 until 2) {
|
||||
Column {
|
||||
for (i in 0 until columnLength) {
|
||||
val item = items[index * columnLength + i]
|
||||
content(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
TangemThemePreview {
|
||||
MultiWalletSeedPhraseWords(
|
||||
state = MultiWalletSeedPhraseUM.GenerateSeedPhrase(
|
||||
words = persistentListOf(
|
||||
MnemonicGridItem(
|
||||
index = 0,
|
||||
mnemonic = "word1",
|
||||
),
|
||||
MnemonicGridItem(
|
||||
index = 1,
|
||||
mnemonic = "word1",
|
||||
),
|
||||
MnemonicGridItem(
|
||||
index = 2,
|
||||
mnemonic = "word1",
|
||||
),
|
||||
MnemonicGridItem(
|
||||
index = 3,
|
||||
mnemonic = "word1",
|
||||
),
|
||||
MnemonicGridItem(
|
||||
index = 4,
|
||||
mnemonic = "word1",
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.ui
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
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.res.stringResource
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.OutlineTextField
|
||||
import com.tangem.core.ui.components.PrimaryButtonIconEnd
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.ui.state.MultiWalletSeedPhraseUM
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
@Composable
|
||||
fun MultiWalletSeedPhraseWordsCheck(
|
||||
state: MultiWalletSeedPhraseUM.GeneratedWordsCheck,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.onboarding_seed_user_validation_title),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.padding(top = 48.dp, start = 36.dp, end = 36.dp, bottom = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.onboarding_seed_user_validation_message),
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 36.dp)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Fields(
|
||||
state = state,
|
||||
modifier = Modifier
|
||||
.padding(vertical = 30.dp, horizontal = 16.dp),
|
||||
)
|
||||
|
||||
Box(
|
||||
Modifier.weight(1f),
|
||||
) {
|
||||
PrimaryButtonIconEnd(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomStart)
|
||||
.padding(16.dp)
|
||||
.fillMaxWidth(),
|
||||
text = stringResource(id = R.string.onboarding_create_wallet_button_create_wallet),
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
enabled = state.createWalletButtonEnabled,
|
||||
showProgress = state.createWalletButtonProgress,
|
||||
onClick = state.onCreateWalletButtonClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Fields(state: MultiWalletSeedPhraseUM.GeneratedWordsCheck, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
state.wordFields.forEach { field ->
|
||||
OutlineTextField(
|
||||
value = field.word,
|
||||
onValueChange = field.onChange,
|
||||
label = field.index.toString(),
|
||||
isError = field.error,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
TangemThemePreview {
|
||||
MultiWalletSeedPhraseWordsCheck(
|
||||
state = MultiWalletSeedPhraseUM.GeneratedWordsCheck(
|
||||
wordFields = persistentListOf(
|
||||
MultiWalletSeedPhraseUM.GeneratedWordsCheck.WordField(
|
||||
index = 2,
|
||||
word = TextFieldValue(text = "word"),
|
||||
onChange = {},
|
||||
error = false,
|
||||
),
|
||||
MultiWalletSeedPhraseUM.GeneratedWordsCheck.WordField(
|
||||
index = 7,
|
||||
word = TextFieldValue(text = "wor"),
|
||||
onChange = {},
|
||||
error = true,
|
||||
),
|
||||
MultiWalletSeedPhraseUM.GeneratedWordsCheck.WordField(
|
||||
index = 11,
|
||||
word = TextFieldValue(text = "word"),
|
||||
onChange = {},
|
||||
error = false,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
|
||||
@Composable
|
||||
fun PassphraseInfoBottomSheet(config: TangemBottomSheetConfig) {
|
||||
TangemBottomSheet(
|
||||
config = config,
|
||||
containerColor = TangemTheme.colors.background.primary,
|
||||
) { _: TangemBottomSheetConfigContent.Empty ->
|
||||
PassphraseInfoBottomSheetContent(config.onDismissRequest)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PassphraseInfoBottomSheetContent(onDismiss: () -> Unit) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(color = TangemTheme.colors.background.primary)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.padding(top = TangemTheme.dimens.size40)
|
||||
.size(TangemTheme.dimens.size48),
|
||||
painter = painterResource(id = R.drawable.ic_information_24),
|
||||
tint = TangemTheme.colors.icon.accent,
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.common_passphrase),
|
||||
modifier = Modifier
|
||||
.padding(top = TangemTheme.dimens.size40)
|
||||
.align(Alignment.CenterHorizontally),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.h2,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.onboarding_bottom_sheet_passphrase_description),
|
||||
modifier = Modifier
|
||||
.padding(top = TangemTheme.dimens.size16)
|
||||
.padding(horizontal = TangemTheme.dimens.size24)
|
||||
.align(Alignment.CenterHorizontally),
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
style = TangemTheme.typography.body2,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
|
||||
PrimaryButton(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.size16)
|
||||
.padding(top = TangemTheme.dimens.size40)
|
||||
.padding(bottom = TangemTheme.dimens.size32)
|
||||
.fillMaxWidth(),
|
||||
text = stringResource(id = R.string.common_ok),
|
||||
onClick = onDismiss,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PassphraseInfoBottomSheetContentPreview() {
|
||||
TangemThemePreview {
|
||||
PassphraseInfoBottomSheetContent({ })
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.ui.state
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
@Immutable
|
||||
sealed class MultiWalletSeedPhraseUM(
|
||||
val order: Int,
|
||||
) {
|
||||
|
||||
data class Start(
|
||||
val onLearnMoreClicked: () -> Unit = {},
|
||||
val onGenerateSeedPhraseClicked: () -> Unit = {},
|
||||
val onImportSeedPhraseClicked: () -> Unit = {},
|
||||
) : MultiWalletSeedPhraseUM(order = 0)
|
||||
|
||||
data class GenerateSeedPhrase(
|
||||
val words24OptionSelected: Boolean = false,
|
||||
val words: ImmutableList<MnemonicGridItem> = persistentListOf(),
|
||||
val onWords24OptionSwitch: () -> Unit = {},
|
||||
val onContinueClick: () -> Unit = {},
|
||||
) : MultiWalletSeedPhraseUM(order = 1) {
|
||||
data class MnemonicGridItem(
|
||||
val index: Int,
|
||||
val mnemonic: String,
|
||||
)
|
||||
}
|
||||
|
||||
data class GeneratedWordsCheck(
|
||||
val wordFields: ImmutableList<WordField> = persistentListOf(),
|
||||
val createWalletButtonEnabled: Boolean = false,
|
||||
val createWalletButtonProgress: Boolean = false,
|
||||
val onCreateWalletButtonClick: () -> Unit = {},
|
||||
) : MultiWalletSeedPhraseUM(order = 2) {
|
||||
data class WordField(
|
||||
val index: Int,
|
||||
val word: TextFieldValue,
|
||||
val error: Boolean,
|
||||
val onChange: (TextFieldValue) -> Unit,
|
||||
)
|
||||
}
|
||||
|
||||
data class Import(
|
||||
val words: TextFieldValue = TextFieldValue(""),
|
||||
val wordsChange: (TextFieldValue) -> Unit = {},
|
||||
val passPhrase: TextFieldValue = TextFieldValue(""),
|
||||
val passPhraseChange: (TextFieldValue) -> Unit = {},
|
||||
val onPassphraseInfoClick: () -> Unit = {},
|
||||
val wordsErrorText: TextReference? = null,
|
||||
val invalidWords: ImmutableList<String> = persistentListOf(),
|
||||
val createWalletEnabled: Boolean = false,
|
||||
val createWalletProgress: Boolean = false,
|
||||
val createWalletClick: () -> Unit = {},
|
||||
val suggestionsList: ImmutableList<String> = persistentListOf(),
|
||||
val onSuggestionClick: (String) -> Unit = {},
|
||||
val infoBottomSheetConfig: TangemBottomSheetConfig = TangemBottomSheetConfig.Empty,
|
||||
) : MultiWalletSeedPhraseUM(order = 1)
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.ui.utils
|
||||
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.input.OffsetMapping
|
||||
import androidx.compose.ui.text.input.TransformedText
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import com.tangem.utils.extensions.isSingleItem
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
class InvalidWordsColorTransformation(
|
||||
private val wordsToBrush: ImmutableList<String>,
|
||||
private val style: SpanStyle,
|
||||
) : VisualTransformation {
|
||||
|
||||
override fun filter(text: AnnotatedString): TransformedText {
|
||||
val plainText = text.text
|
||||
if (wordsToBrush.isSingleItem() && wordsToBrush.first() == plainText) {
|
||||
return plainText.annotate().toTransformedText()
|
||||
}
|
||||
|
||||
val wordList = makeSeparatedWordlist(plainText)
|
||||
val annotatedString = makeAnnotatedString(wordList)
|
||||
|
||||
return annotatedString.toTransformedText()
|
||||
}
|
||||
|
||||
private fun makeSeparatedWordlist(plainText: String): List<String> {
|
||||
val plainTextCharArray = plainText.toCharArray()
|
||||
val wordlist = mutableListOf<String>()
|
||||
var wordBuilder = StringBuilder()
|
||||
|
||||
for (index in plainTextCharArray.indices) {
|
||||
val char = plainTextCharArray[index]
|
||||
if (char.isWhitespace()) {
|
||||
if (wordBuilder.isEmpty()) {
|
||||
wordlist.add(WORD_SEPARATOR)
|
||||
} else {
|
||||
wordlist.add(wordBuilder.toString())
|
||||
wordBuilder = StringBuilder()
|
||||
wordlist.add(WORD_SEPARATOR)
|
||||
}
|
||||
} else {
|
||||
if (index == plainTextCharArray.lastIndex) {
|
||||
wordBuilder.append(char)
|
||||
wordlist.add(wordBuilder.toString())
|
||||
wordBuilder = StringBuilder()
|
||||
} else {
|
||||
wordBuilder.append(char)
|
||||
}
|
||||
}
|
||||
}
|
||||
return wordlist.toList()
|
||||
}
|
||||
|
||||
private fun makeAnnotatedString(wordList: List<String>): AnnotatedString = buildAnnotatedString {
|
||||
wordList.forEach {
|
||||
if (it == WORD_SEPARATOR) {
|
||||
append(WHITE_SPACE)
|
||||
} else if (wordsToBrush.contains(it)) {
|
||||
append(it.annotate())
|
||||
} else {
|
||||
append(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.annotate(): AnnotatedString = AnnotatedString(this, style)
|
||||
|
||||
private fun AnnotatedString.toTransformedText(): TransformedText = TransformedText(this, OffsetMapping.Identity)
|
||||
|
||||
companion object {
|
||||
private const val WHITE_SPACE = " "
|
||||
private const val WORD_SEPARATOR = "_$@-*"
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletCo
|
|||
import com.tangem.features.onboarding.v2.multiwallet.impl.DefaultOnboardingMultiWalletComponent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.model.MultiWalletBackupModel
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.createwallet.model.MultiWalletCreateWalletModel
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.model.MultiWalletSeedPhraseModel
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
|
|
@ -42,4 +43,9 @@ internal interface ModelModule {
|
|||
@IntoMap
|
||||
@ClassKey(MultiWalletCreateWalletModel::class)
|
||||
fun provideModel3(model: MultiWalletCreateWalletModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(MultiWalletSeedPhraseModel::class)
|
||||
fun provideModel4(model: MultiWalletSeedPhraseModel): Model
|
||||
}
|
||||
|
|
@ -2,8 +2,6 @@ package com.tangem.features.onboarding.v2.multiwallet.impl.ui
|
|||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -18,33 +16,32 @@ import com.tangem.features.onboarding.v2.multiwallet.impl.ui.state.OnboardingMul
|
|||
internal fun OnboardingMultiWallet(
|
||||
state: OnboardingMultiWalletUM,
|
||||
artworksState: WalletArtworksState,
|
||||
isSeedPhraseState: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
childContent: @Composable (Modifier) -> Unit = {},
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
modifier = modifier.fillMaxSize(),
|
||||
) {
|
||||
WalletArtworks(
|
||||
url = state.artworkUrl,
|
||||
modifier = Modifier
|
||||
.weight(.56f)
|
||||
.padding(horizontal = 34.dp)
|
||||
.padding(top = 20.dp)
|
||||
.fillMaxWidth(),
|
||||
state = artworksState,
|
||||
)
|
||||
if (isSeedPhraseState.not()) {
|
||||
WalletArtworks(
|
||||
url = state.artworkUrl,
|
||||
modifier = Modifier
|
||||
.weight(.56f)
|
||||
.padding(horizontal = 34.dp)
|
||||
.padding(top = 20.dp)
|
||||
.fillMaxWidth(),
|
||||
state = artworksState,
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = if (artworksState == WalletArtworksState.Hidden) {
|
||||
Modifier.fillMaxSize()
|
||||
} else {
|
||||
Modifier.weight(.44f)
|
||||
},
|
||||
contentAlignment = Alignment.BottomStart,
|
||||
) {
|
||||
childContent(Modifier)
|
||||
Box(
|
||||
modifier = Modifier.weight(.44f),
|
||||
contentAlignment = Alignment.BottomStart,
|
||||
) {
|
||||
childContent(Modifier)
|
||||
}
|
||||
} else {
|
||||
childContent(Modifier.fillMaxSize())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -56,6 +53,7 @@ private fun Preview() {
|
|||
OnboardingMultiWallet(
|
||||
state = OnboardingMultiWalletUM(artworkUrl = null),
|
||||
artworksState = WalletArtworksState.Folded,
|
||||
isSeedPhraseState = false,
|
||||
childContent = { md ->
|
||||
Box(
|
||||
modifier = md
|
||||
|
|
@ -66,4 +64,23 @@ private fun Preview() {
|
|||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360, heightDp = 640)
|
||||
@Composable
|
||||
private fun Preview2() {
|
||||
TangemThemePreview {
|
||||
OnboardingMultiWallet(
|
||||
state = OnboardingMultiWalletUM(artworkUrl = null),
|
||||
artworksState = WalletArtworksState.Folded,
|
||||
isSeedPhraseState = true,
|
||||
childContent = { md ->
|
||||
Box(
|
||||
modifier = md
|
||||
.background(Color.DarkGray)
|
||||
.fillMaxSize(),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue