Updated on 2026-08-14
This commit is contained in:
parent
5a6dfd50e4
commit
c6497a0abc
19 changed files with 448 additions and 54 deletions
32
.idea/inspectionProfiles/Project_Default.xml
generated
32
.idea/inspectionProfiles/Project_Default.xml
generated
|
|
@ -4,8 +4,36 @@
|
||||||
<inspection_tool class="CanBeParameter" enabled="true" level="ERROR" enabled_by_default="true" />
|
<inspection_tool class="CanBeParameter" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||||
<inspection_tool class="CanBePrimaryConstructorProperty" enabled="true" level="ERROR" enabled_by_default="true" />
|
<inspection_tool class="CanBePrimaryConstructorProperty" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||||
<inspection_tool class="ComplexRedundantLet" enabled="true" level="ERROR" enabled_by_default="true" />
|
<inspection_tool class="ComplexRedundantLet" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
<inspection_tool class="ConstantConditionIf" enabled="true" level="ERROR" enabled_by_default="true" />
|
<inspection_tool class="ConstantConditionIf" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||||
<inspection_tool class="ExplicitThis" enabled="false" level="ERROR" enabled_by_default="false" />
|
<inspection_tool class="ExplicitThis" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
<inspection_tool class="KotlinDoubleNegation" enabled="true" level="ERROR" enabled_by_default="true" />
|
<inspection_tool class="KotlinDoubleNegation" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||||
<inspection_tool class="KotlinRedundantOverride" enabled="true" level="ERROR" enabled_by_default="true" />
|
<inspection_tool class="KotlinRedundantOverride" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||||
<inspection_tool class="KotlinUnusedImport" enabled="true" level="ERROR" enabled_by_default="true" />
|
<inspection_tool class="KotlinUnusedImport" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||||
|
|
@ -19,6 +47,10 @@
|
||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
<option name="previewFile" value="true" />
|
<option name="previewFile" value="true" />
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewDeviceShouldUseNewSpec" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
<option name="previewFile" value="true" />
|
<option name="previewFile" value="true" />
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,9 @@ dependencies {
|
||||||
implementation(projects.core.decompose)
|
implementation(projects.core.decompose)
|
||||||
implementation(projects.core.navigation)
|
implementation(projects.core.navigation)
|
||||||
|
|
||||||
|
/** Common */
|
||||||
|
implementation(projects.common.ui)
|
||||||
|
|
||||||
/** Domain */
|
/** Domain */
|
||||||
implementation(projects.domain.models)
|
implementation(projects.domain.models)
|
||||||
implementation(projects.domain.feedback)
|
implementation(projects.domain.feedback)
|
||||||
|
|
@ -44,7 +47,7 @@ dependencies {
|
||||||
implementation(deps.lifecycle.runtime.ktx)
|
implementation(deps.lifecycle.runtime.ktx)
|
||||||
|
|
||||||
/** Compose libraries */
|
/** Compose libraries */
|
||||||
implementation(deps.compose.material) // to use buttons
|
implementation(deps.compose.material) // to use buttons and text field in MultiWalletSeedPhraseImport.kt
|
||||||
implementation(deps.compose.material3)
|
implementation(deps.compose.material3)
|
||||||
implementation(deps.compose.animation)
|
implementation(deps.compose.animation)
|
||||||
implementation(deps.compose.foundation)
|
implementation(deps.compose.foundation)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import com.tangem.features.onboarding.v2.multiwallet.impl.child.createwallet.ui.
|
||||||
import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletState
|
import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletState
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class MultiWalletCreateWalletComponent(
|
internal class MultiWalletCreateWalletComponent(
|
||||||
context: AppComponentContext,
|
context: AppComponentContext,
|
||||||
params: MultiWalletChildParams,
|
params: MultiWalletChildParams,
|
||||||
onNextStep: (OnboardingMultiWalletState.Step) -> Unit,
|
onNextStep: (OnboardingMultiWalletState.Step) -> Unit,
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||||
import com.tangem.features.onboarding.v2.impl.R
|
import com.tangem.features.onboarding.v2.impl.R
|
||||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams
|
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams
|
||||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.createwallet.ui.state.MultiWalletCreateWalletUM
|
import com.tangem.features.onboarding.v2.multiwallet.impl.child.createwallet.ui.state.MultiWalletCreateWalletUM
|
||||||
|
import com.tangem.features.onboarding.v2.multiwallet.impl.common.ui.resetCardDialog
|
||||||
import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletState
|
import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletState
|
||||||
import com.tangem.sdk.api.TangemSdkManager
|
import com.tangem.sdk.api.TangemSdkManager
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
|
@ -24,7 +25,7 @@ import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ComponentScoped
|
@ComponentScoped
|
||||||
class MultiWalletCreateWalletModel @Inject constructor(
|
internal class MultiWalletCreateWalletModel @Inject constructor(
|
||||||
paramsContainer: ParamsContainer,
|
paramsContainer: ParamsContainer,
|
||||||
override val dispatchers: CoroutineDispatcherProvider,
|
override val dispatchers: CoroutineDispatcherProvider,
|
||||||
private val tangemSdkManager: TangemSdkManager,
|
private val tangemSdkManager: TangemSdkManager,
|
||||||
|
|
|
||||||
|
|
@ -21,14 +21,14 @@ import com.tangem.features.onboarding.v2.impl.R
|
||||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.createwallet.ui.state.MultiWalletCreateWalletUM
|
import com.tangem.features.onboarding.v2.multiwallet.impl.child.createwallet.ui.state.MultiWalletCreateWalletUM
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MultiWalletCreateWallet(state: MultiWalletCreateWalletUM, modifier: Modifier = Modifier) {
|
internal fun MultiWalletCreateWallet(state: MultiWalletCreateWalletUM, modifier: Modifier = Modifier) {
|
||||||
if (state.dialog != null) {
|
if (state.dialog != null) {
|
||||||
BasicDialog(
|
BasicDialog(
|
||||||
title = state.dialog.title.resolveReference(),
|
title = state.dialog.title.resolveReference(),
|
||||||
message = state.dialog.description.resolveReference(),
|
message = state.dialog.message.resolveReference(),
|
||||||
confirmButton = DialogButtonUM(
|
confirmButton = DialogButtonUM(
|
||||||
title = state.dialog.confirmButtonText.resolveReference(),
|
title = state.dialog.confirmButtonText.resolveReference(),
|
||||||
onClick = state.dialog.onConfirm,
|
onClick = state.dialog.onConfirmClick,
|
||||||
),
|
),
|
||||||
dismissButton = DialogButtonUM(
|
dismissButton = DialogButtonUM(
|
||||||
title = state.dialog.dismissButtonText.resolveReference(),
|
title = state.dialog.dismissButtonText.resolveReference(),
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,13 @@
|
||||||
package com.tangem.features.onboarding.v2.multiwallet.impl.child.createwallet.ui.state
|
package com.tangem.features.onboarding.v2.multiwallet.impl.child.createwallet.ui.state
|
||||||
|
|
||||||
import com.tangem.core.ui.extensions.TextReference
|
import com.tangem.core.ui.extensions.TextReference
|
||||||
|
import com.tangem.features.onboarding.v2.multiwallet.impl.common.ui.OnboardingDialogUM
|
||||||
|
|
||||||
data class MultiWalletCreateWalletUM(
|
internal data class MultiWalletCreateWalletUM(
|
||||||
val title: TextReference,
|
val title: TextReference,
|
||||||
val bodyText: TextReference,
|
val bodyText: TextReference,
|
||||||
val showOtherOptionsButton: Boolean,
|
val showOtherOptionsButton: Boolean,
|
||||||
val onCreateWalletClick: () -> Unit,
|
val onCreateWalletClick: () -> Unit,
|
||||||
val onOtherOptionsClick: () -> Unit,
|
val onOtherOptionsClick: () -> Unit,
|
||||||
val dialog: Dialog?,
|
val dialog: OnboardingDialogUM?,
|
||||||
) {
|
)
|
||||||
data class Dialog(
|
|
||||||
val title: TextReference,
|
|
||||||
val description: TextReference,
|
|
||||||
val dismissButtonText: TextReference,
|
|
||||||
val confirmButtonText: TextReference,
|
|
||||||
val dismissWarningColor: Boolean = false,
|
|
||||||
val onConfirm: () -> Unit,
|
|
||||||
val onDismissButtonClick: () -> Unit,
|
|
||||||
val onDismiss: () -> Unit,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +1,15 @@
|
||||||
package com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase
|
package com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.ContextWrapper
|
||||||
|
import android.view.WindowManager
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.tangem.core.decompose.context.AppComponentContext
|
import com.tangem.core.decompose.context.AppComponentContext
|
||||||
import com.tangem.core.decompose.model.getOrCreateModel
|
import com.tangem.core.decompose.model.getOrCreateModel
|
||||||
|
|
@ -16,20 +22,24 @@ import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Suppress("UnusedPrivateMember")
|
@Suppress("UnusedPrivateMember")
|
||||||
class MultiWalletSeedPhraseComponent(
|
internal class MultiWalletSeedPhraseComponent(
|
||||||
context: AppComponentContext,
|
context: AppComponentContext,
|
||||||
params: MultiWalletChildParams,
|
params: MultiWalletChildParams,
|
||||||
onNextStep: (OnboardingMultiWalletState.Step) -> Unit,
|
onNextStep: (OnboardingMultiWalletState.Step) -> Unit,
|
||||||
backButtonClickFlow: SharedFlow<Unit>,
|
backButtonClickFlow: SharedFlow<Unit>,
|
||||||
) : AppComponentContext by context, MultiWalletChildComponent {
|
) : AppComponentContext by context, MultiWalletChildComponent {
|
||||||
|
|
||||||
private val model: MultiWalletSeedPhraseModel = getOrCreateModel()
|
private val model: MultiWalletSeedPhraseModel = getOrCreateModel(params)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
componentScope.launch {
|
componentScope.launch {
|
||||||
backButtonClickFlow.collect {
|
backButtonClickFlow.collect { model.onBack() }
|
||||||
model.onBack()
|
}
|
||||||
}
|
componentScope.launch {
|
||||||
|
model.onDone.collect { onNextStep(OnboardingMultiWalletState.Step.AddBackupDevice) }
|
||||||
|
}
|
||||||
|
componentScope.launch {
|
||||||
|
model.navigateBack.collect { onNextStep(OnboardingMultiWalletState.Step.CreateWallet) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -41,9 +51,30 @@ class MultiWalletSeedPhraseComponent(
|
||||||
model.onBack()
|
model.onBack()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// disable screenshots
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
DisposableEffect(activity) {
|
||||||
|
activity.window.setFlags(
|
||||||
|
WindowManager.LayoutParams.FLAG_SECURE,
|
||||||
|
WindowManager.LayoutParams.FLAG_SECURE,
|
||||||
|
)
|
||||||
|
onDispose {
|
||||||
|
activity.window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MultiWalletSeedPhrase(
|
MultiWalletSeedPhrase(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
state = state,
|
state = state,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Context.findActivity(): Activity {
|
||||||
|
var context = this
|
||||||
|
while (context is ContextWrapper) {
|
||||||
|
if (context is Activity) return context
|
||||||
|
context = context.baseContext
|
||||||
|
}
|
||||||
|
error("")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,46 +1,78 @@
|
||||||
package com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.model
|
package com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.model
|
||||||
|
|
||||||
|
import com.tangem.common.CompletionResult
|
||||||
|
import com.tangem.common.core.TangemSdkError
|
||||||
import com.tangem.core.decompose.di.ComponentScoped
|
import com.tangem.core.decompose.di.ComponentScoped
|
||||||
import com.tangem.core.decompose.model.Model
|
import com.tangem.core.decompose.model.Model
|
||||||
|
import com.tangem.core.decompose.model.ParamsContainer
|
||||||
import com.tangem.core.navigation.url.UrlOpener
|
import com.tangem.core.navigation.url.UrlOpener
|
||||||
|
import com.tangem.domain.card.repository.CardRepository
|
||||||
|
import com.tangem.domain.feedback.GetCardInfoUseCase
|
||||||
|
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||||
|
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||||
|
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams
|
||||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.model.builder.GenerateSeedPhraseUiStateBuilder
|
import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.model.builder.GenerateSeedPhraseUiStateBuilder
|
||||||
|
import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.model.builder.ImportSeedPhraseUiStateBuilder
|
||||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.model.builder.SeedPhraseCheckUiStateBuilder
|
import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.model.builder.SeedPhraseCheckUiStateBuilder
|
||||||
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
|
||||||
|
import com.tangem.features.onboarding.v2.multiwallet.impl.common.ui.OnboardingDialogUM
|
||||||
|
import com.tangem.features.onboarding.v2.multiwallet.impl.common.ui.resetCardDialog
|
||||||
|
import com.tangem.sdk.api.TangemSdkManager
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@Suppress("LongParameterList")
|
||||||
@ComponentScoped
|
@ComponentScoped
|
||||||
class MultiWalletSeedPhraseModel @Inject constructor(
|
internal class MultiWalletSeedPhraseModel @Inject constructor(
|
||||||
|
paramsContainer: ParamsContainer,
|
||||||
override val dispatchers: CoroutineDispatcherProvider,
|
override val dispatchers: CoroutineDispatcherProvider,
|
||||||
private val mnemonicRepository: MnemonicRepository,
|
private val mnemonicRepository: MnemonicRepository,
|
||||||
private val urlOpener: UrlOpener,
|
private val urlOpener: UrlOpener,
|
||||||
|
private val tangemSdkManager: TangemSdkManager,
|
||||||
|
private val cardRepository: CardRepository,
|
||||||
|
private val getCardInfoUseCase: GetCardInfoUseCase,
|
||||||
|
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||||
) : Model() {
|
) : Model() {
|
||||||
|
|
||||||
|
private val params = paramsContainer.require<MultiWalletChildParams>()
|
||||||
|
private val multiWalletState get() = params.multiWalletState
|
||||||
private val state = MutableStateFlow(SeedPhraseState())
|
private val state = MutableStateFlow(SeedPhraseState())
|
||||||
private val _uiState = MutableStateFlow(getStartState())
|
private val _uiState = MutableStateFlow(getStartState())
|
||||||
|
|
||||||
private val generateSeedPhraseUiStateBuilder = GenerateSeedPhraseUiStateBuilder(
|
private val generateSeedPhraseUiStateBuilder = GenerateSeedPhraseUiStateBuilder(
|
||||||
state = state,
|
state = state,
|
||||||
updateUiState = { block -> updateUiStateSpecific(block) },
|
updateUiState = { block -> updateUiStateSpecific(block) },
|
||||||
onContinue = { openWordsCheck() },
|
onContinue = ::openWordsCheck,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val seedPhraseCheckUiStateBuilder = SeedPhraseCheckUiStateBuilder(
|
private val seedPhraseCheckUiStateBuilder = SeedPhraseCheckUiStateBuilder(
|
||||||
state = state,
|
state = state,
|
||||||
currentUiState = { _uiState.value },
|
currentUiState = { _uiState.value },
|
||||||
updateUiState = { block -> updateUiStateSpecific(block) },
|
updateUiState = { block -> updateUiStateSpecific(block) },
|
||||||
importWallet = { },
|
importWallet = ::importWallet,
|
||||||
|
)
|
||||||
|
|
||||||
|
private val importSeedPhraseUiStateBuilder = ImportSeedPhraseUiStateBuilder(
|
||||||
|
modelScope = modelScope,
|
||||||
|
state = state,
|
||||||
|
mnemonicRepository = mnemonicRepository,
|
||||||
|
updateUiState = { block -> updateUiStateSpecific(block) },
|
||||||
|
importWallet = ::importWallet,
|
||||||
)
|
)
|
||||||
|
|
||||||
val uiState = _uiState.asStateFlow()
|
val uiState = _uiState.asStateFlow()
|
||||||
|
val onDone = MutableSharedFlow<Unit>()
|
||||||
|
val navigateBack = MutableSharedFlow<Unit>()
|
||||||
|
|
||||||
fun onBack() {
|
fun onBack() {
|
||||||
when (_uiState.value) {
|
when (_uiState.value) {
|
||||||
is MultiWalletSeedPhraseUM.Start -> {
|
is MultiWalletSeedPhraseUM.Start -> {
|
||||||
// TODO navigate back
|
modelScope.launch { navigateBack.emit(Unit) }
|
||||||
}
|
}
|
||||||
is MultiWalletSeedPhraseUM.Import,
|
is MultiWalletSeedPhraseUM.Import,
|
||||||
is MultiWalletSeedPhraseUM.GenerateSeedPhrase,
|
is MultiWalletSeedPhraseUM.GenerateSeedPhrase,
|
||||||
|
|
@ -56,7 +88,9 @@ class MultiWalletSeedPhraseModel @Inject constructor(
|
||||||
|
|
||||||
private fun getStartState(): MultiWalletSeedPhraseUM {
|
private fun getStartState(): MultiWalletSeedPhraseUM {
|
||||||
return MultiWalletSeedPhraseUM.Start(
|
return MultiWalletSeedPhraseUM.Start(
|
||||||
onImportSeedPhraseClicked = {},
|
onImportSeedPhraseClicked = {
|
||||||
|
openImportSeedPhrase()
|
||||||
|
},
|
||||||
onGenerateSeedPhraseClicked = {
|
onGenerateSeedPhraseClicked = {
|
||||||
generateSeedPhrase()
|
generateSeedPhrase()
|
||||||
openGeneratedSeedPhrase()
|
openGeneratedSeedPhrase()
|
||||||
|
|
@ -73,8 +107,6 @@ class MultiWalletSeedPhraseModel @Inject constructor(
|
||||||
val words12 = state.value.generatedWords12 ?: return
|
val words12 = state.value.generatedWords12 ?: return
|
||||||
val words24 = state.value.generatedWords24 ?: return
|
val words24 = state.value.generatedWords24 ?: return
|
||||||
|
|
||||||
// TODO disallow screenshots
|
|
||||||
|
|
||||||
_uiState.value = generateSeedPhraseUiStateBuilder.getState(
|
_uiState.value = generateSeedPhraseUiStateBuilder.getState(
|
||||||
generatedWords12 = words12,
|
generatedWords12 = words12,
|
||||||
generatedWords24 = words24,
|
generatedWords24 = words24,
|
||||||
|
|
@ -85,6 +117,10 @@ class MultiWalletSeedPhraseModel @Inject constructor(
|
||||||
_uiState.value = seedPhraseCheckUiStateBuilder.getState()
|
_uiState.value = seedPhraseCheckUiStateBuilder.getState()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun openImportSeedPhrase() {
|
||||||
|
_uiState.value = importSeedPhraseUiStateBuilder.getState()
|
||||||
|
}
|
||||||
|
|
||||||
private fun generateSeedPhrase() {
|
private fun generateSeedPhrase() {
|
||||||
val words12 = mnemonicRepository.generateMnemonic(MnemonicRepository.MnemonicType.Words12)
|
val words12 = mnemonicRepository.generateMnemonic(MnemonicRepository.MnemonicType.Words12)
|
||||||
val words24 = mnemonicRepository.generateMnemonic(MnemonicRepository.MnemonicType.Words24)
|
val words24 = mnemonicRepository.generateMnemonic(MnemonicRepository.MnemonicType.Words24)
|
||||||
|
|
@ -97,6 +133,79 @@ class MultiWalletSeedPhraseModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun importWallet(shouldReset: Boolean = false) {
|
||||||
|
// TODO make stateless [REDACTED_TASK_KEY]
|
||||||
|
val currentState = state.value
|
||||||
|
val mnemonic = currentState.importedMnemonic
|
||||||
|
?: (if (currentState.words24Option) currentState.generatedWords24 else currentState.generatedWords12)
|
||||||
|
?: return
|
||||||
|
|
||||||
|
val scanResponse = params.parentParams.scanResponse
|
||||||
|
|
||||||
|
modelScope.launch {
|
||||||
|
val result = tangemSdkManager.importWallet(
|
||||||
|
scanResponse = scanResponse,
|
||||||
|
mnemonic = mnemonic.mnemonicComponents.joinToString(" "),
|
||||||
|
passphrase = currentState.passphrase,
|
||||||
|
shouldReset = shouldReset,
|
||||||
|
)
|
||||||
|
|
||||||
|
when (result) {
|
||||||
|
is CompletionResult.Success -> {
|
||||||
|
// TODO on success send analytics event
|
||||||
|
multiWalletState.update {
|
||||||
|
it.copy(
|
||||||
|
currentScanResponse = it.currentScanResponse.copy(
|
||||||
|
card = result.data.card,
|
||||||
|
derivedKeys = result.data.derivedKeys,
|
||||||
|
primaryCard = result.data.primaryCard,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
cardRepository.startCardActivation(cardId = result.data.card.cardId)
|
||||||
|
|
||||||
|
onDone.emit(Unit)
|
||||||
|
}
|
||||||
|
is CompletionResult.Failure -> {
|
||||||
|
if (result.error is TangemSdkError.WalletAlreadyCreated) {
|
||||||
|
// show should reset dialog
|
||||||
|
handleActivationError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleActivationError() {
|
||||||
|
updateDialog(
|
||||||
|
resetCardDialog(
|
||||||
|
onConfirm = ::navigateToSupportScreen,
|
||||||
|
dismiss = { updateDialog(null) },
|
||||||
|
onDismissButtonClick = ::resetCard,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resetCard() = importWallet(true)
|
||||||
|
|
||||||
|
fun navigateToSupportScreen() {
|
||||||
|
modelScope.launch {
|
||||||
|
val cardInfo = getCardInfoUseCase(multiWalletState.value.currentScanResponse).getOrNull() ?: return@launch
|
||||||
|
sendFeedbackEmailUseCase(FeedbackEmailType.DirectUserRequest(cardInfo))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateDialog(dialog: OnboardingDialogUM?) {
|
||||||
|
_uiState.update { st ->
|
||||||
|
when (st) {
|
||||||
|
is MultiWalletSeedPhraseUM.GeneratedWordsCheck -> st.copy(dialog = dialog)
|
||||||
|
is MultiWalletSeedPhraseUM.Import -> st.copy(dialog = dialog)
|
||||||
|
else -> st
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private inline fun <reified T : MultiWalletSeedPhraseUM> updateUiStateSpecific(block: (T) -> T) {
|
private inline fun <reified T : MultiWalletSeedPhraseUM> updateUiStateSpecific(block: (T) -> T) {
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
if (it !is T) return@update it
|
if (it !is T) return@update it
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,7 @@ data class SeedPhraseState(
|
||||||
val generatedWords12: Mnemonic? = null,
|
val generatedWords12: Mnemonic? = null,
|
||||||
val generatedWords24: Mnemonic? = null,
|
val generatedWords24: Mnemonic? = null,
|
||||||
val words24Option: Boolean = false,
|
val words24Option: Boolean = false,
|
||||||
|
val importedMnemonic: Mnemonic? = null,
|
||||||
|
val passphrase: String? = null,
|
||||||
val readyToImport: Boolean = false,
|
val readyToImport: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
@ -8,7 +8,7 @@ import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
|
|
||||||
class GenerateSeedPhraseUiStateBuilder(
|
internal class GenerateSeedPhraseUiStateBuilder(
|
||||||
private val state: MutableStateFlow<SeedPhraseState>,
|
private val state: MutableStateFlow<SeedPhraseState>,
|
||||||
private val updateUiState: (
|
private val updateUiState: (
|
||||||
(MultiWalletSeedPhraseUM.GenerateSeedPhrase) -> MultiWalletSeedPhraseUM.GenerateSeedPhrase,
|
(MultiWalletSeedPhraseUM.GenerateSeedPhrase) -> MultiWalletSeedPhraseUM.GenerateSeedPhrase,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,163 @@
|
||||||
|
package com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.model.builder
|
||||||
|
|
||||||
|
import androidx.compose.ui.text.TextRange
|
||||||
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
|
import com.tangem.common.core.TangemSdkError
|
||||||
|
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||||
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
|
import com.tangem.crypto.bip39.MnemonicErrorResult
|
||||||
|
import com.tangem.features.onboarding.v2.impl.R
|
||||||
|
import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.model.MnemonicRepository
|
||||||
|
import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.model.SeedPhraseState
|
||||||
|
import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.ui.state.MultiWalletSeedPhraseUM
|
||||||
|
import com.tangem.utils.coroutines.JobHolder
|
||||||
|
import com.tangem.utils.coroutines.saveIn
|
||||||
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
internal class ImportSeedPhraseUiStateBuilder(
|
||||||
|
private val modelScope: CoroutineScope,
|
||||||
|
private val state: MutableStateFlow<SeedPhraseState>,
|
||||||
|
private val mnemonicRepository: MnemonicRepository,
|
||||||
|
private val updateUiState: ((MultiWalletSeedPhraseUM.Import) -> MultiWalletSeedPhraseUM.Import) -> Unit,
|
||||||
|
private val importWallet: () -> Unit,
|
||||||
|
) {
|
||||||
|
private val wordsCheckJobHolder = JobHolder()
|
||||||
|
|
||||||
|
fun getState(): MultiWalletSeedPhraseUM {
|
||||||
|
return MultiWalletSeedPhraseUM.Import(
|
||||||
|
wordsChange = {
|
||||||
|
launchInterceptWords(wordsField = it)
|
||||||
|
suggestNextWord(it)
|
||||||
|
updateUiState { state ->
|
||||||
|
state.copy(words = it)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
passPhraseChange = {
|
||||||
|
state.update { st -> st.copy(passphrase = it.text) }
|
||||||
|
updateUiState { state -> state.copy(passPhrase = it) }
|
||||||
|
},
|
||||||
|
onPassphraseInfoClick = ::showInfoBS,
|
||||||
|
createWalletClick = importWallet,
|
||||||
|
onSuggestionClick = { word -> addSuggestedWord(word) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addSuggestedWord(word: String) {
|
||||||
|
updateUiState { st ->
|
||||||
|
val text = st.words.text
|
||||||
|
val wordsFromText = text.split(" ").filter { it.isNotBlank() }.map { it.trim() }
|
||||||
|
val newWords = wordsFromText.dropLast(1) + word
|
||||||
|
val newWordsText = newWords.joinToString(" ")
|
||||||
|
st.copy(
|
||||||
|
words = TextFieldValue(
|
||||||
|
text = newWordsText,
|
||||||
|
selection = TextRange(newWordsText.length),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun suggestNextWord(wordsField: TextFieldValue) {
|
||||||
|
if (wordsField.text.endsWith(" ")) {
|
||||||
|
updateUiState { it.copy(suggestionsList = emptyList<String>().toImmutableList()) }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val text = wordsField.text
|
||||||
|
val wordsFromText = text.split(" ").filter { it.isNotBlank() }.map { it.trim() }
|
||||||
|
val lastWord = wordsFromText.lastOrNull() ?: return
|
||||||
|
if (lastWord.length < 2) return
|
||||||
|
|
||||||
|
if (mnemonicRepository.words.contains(lastWord) && mnemonicRepository.words.none { it.startsWith(lastWord) }) {
|
||||||
|
updateUiState { it.copy(suggestionsList = emptyList<String>().toImmutableList()) }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
updateUiState { st ->
|
||||||
|
st.copy(
|
||||||
|
suggestionsList = mnemonicRepository.words
|
||||||
|
.filter { it.startsWith(lastWord) && it != lastWord }
|
||||||
|
.toImmutableList(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun launchInterceptWords(wordsField: TextFieldValue) {
|
||||||
|
modelScope.launch {
|
||||||
|
delay(timeMillis = 500)
|
||||||
|
interceptWords(wordsField)
|
||||||
|
}.saveIn(wordsCheckJobHolder)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun interceptWords(wordsField: TextFieldValue) {
|
||||||
|
updateUiState {
|
||||||
|
it.copy(
|
||||||
|
createWalletEnabled = false,
|
||||||
|
wordsErrorText = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
state.value = state.value.copy(importedMnemonic = null)
|
||||||
|
|
||||||
|
val text = wordsField.text
|
||||||
|
val wordsFromText = text.split(" ").filter { it.isNotBlank() }.map { it.trim() }
|
||||||
|
val invalidWords = wordsFromText.filterNot { it in mnemonicRepository.words }
|
||||||
|
if (invalidWords.isNotEmpty()) {
|
||||||
|
updateUiState {
|
||||||
|
it.copy(
|
||||||
|
invalidWords = invalidWords.toImmutableList(),
|
||||||
|
wordsErrorText = resourceReference(R.string.onboarding_seed_mnemonic_wrong_words),
|
||||||
|
createWalletEnabled = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val mnemonic = mnemonicRepository.generateMnemonic(text)
|
||||||
|
state.value = state.value.copy(importedMnemonic = mnemonic)
|
||||||
|
updateUiState {
|
||||||
|
it.copy(
|
||||||
|
invalidWords = emptyList<String>().toImmutableList(),
|
||||||
|
wordsErrorText = null,
|
||||||
|
createWalletEnabled = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (ex: TangemSdkError.MnemonicException) {
|
||||||
|
val error = ex.mnemonicResult
|
||||||
|
if (error is MnemonicErrorResult.InvalidChecksum) {
|
||||||
|
updateUiState {
|
||||||
|
it.copy(
|
||||||
|
wordsErrorText = resourceReference(R.string.onboarding_seed_mnemonic_invalid_checksum),
|
||||||
|
createWalletEnabled = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updateUiState {
|
||||||
|
it.copy(
|
||||||
|
createWalletEnabled = false,
|
||||||
|
wordsErrorText = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showInfoBS() {
|
||||||
|
updateUiState { state ->
|
||||||
|
state.copy(
|
||||||
|
infoBottomSheetConfig = TangemBottomSheetConfig.Empty.copy(
|
||||||
|
isShow = true,
|
||||||
|
onDismissRequest = {
|
||||||
|
updateUiState { it.copy(infoBottomSheetConfig = TangemBottomSheetConfig.Empty) }
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,7 +7,7 @@ import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
|
|
||||||
class SeedPhraseCheckUiStateBuilder(
|
internal class SeedPhraseCheckUiStateBuilder(
|
||||||
private val state: MutableStateFlow<SeedPhraseState>,
|
private val state: MutableStateFlow<SeedPhraseState>,
|
||||||
private val currentUiState: () -> MultiWalletSeedPhraseUM,
|
private val currentUiState: () -> MultiWalletSeedPhraseUM,
|
||||||
private val updateUiState: (
|
private val updateUiState: (
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import androidx.compose.ui.Modifier
|
||||||
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
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MultiWalletSeedPhrase(state: MultiWalletSeedPhraseUM, modifier: Modifier = Modifier) {
|
internal fun MultiWalletSeedPhrase(state: MultiWalletSeedPhraseUM, modifier: Modifier = Modifier) {
|
||||||
AnimatedContent(
|
AnimatedContent(
|
||||||
modifier = modifier.navigationBarsPadding(),
|
modifier = modifier.navigationBarsPadding(),
|
||||||
targetState = state,
|
targetState = state,
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,14 @@ import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyRow
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material.OutlinedTextField
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.SpanStyle
|
import androidx.compose.ui.text.SpanStyle
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
|
@ -30,8 +31,26 @@ import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.ui.st
|
||||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.ui.utils.InvalidWordsColorTransformation
|
import com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.ui.utils.InvalidWordsColorTransformation
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
|
@Suppress("LongMethod")
|
||||||
@Composable
|
@Composable
|
||||||
fun MultiWalletSeedPhraseImport(state: MultiWalletSeedPhraseUM.Import, modifier: Modifier = Modifier) {
|
internal fun MultiWalletSeedPhraseImport(state: MultiWalletSeedPhraseUM.Import, modifier: Modifier = Modifier) {
|
||||||
|
if (state.dialog != null) {
|
||||||
|
BasicDialog(
|
||||||
|
title = state.dialog.title.resolveReference(),
|
||||||
|
message = state.dialog.message.resolveReference(),
|
||||||
|
confirmButton = DialogButtonUM(
|
||||||
|
title = state.dialog.confirmButtonText.resolveReference(),
|
||||||
|
onClick = state.dialog.onConfirmClick,
|
||||||
|
),
|
||||||
|
dismissButton = DialogButtonUM(
|
||||||
|
title = state.dialog.dismissButtonText.resolveReference(),
|
||||||
|
warning = state.dialog.dismissWarningColor,
|
||||||
|
onClick = state.dialog.onDismissButtonClick,
|
||||||
|
),
|
||||||
|
onDismissDialog = state.dialog.onDismiss,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Box(modifier.fillMaxSize()) {
|
Box(modifier.fillMaxSize()) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -109,14 +128,16 @@ private fun PhraseBlock(state: MultiWalletSeedPhraseUM.Import, modifier: Modifie
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier.fillMaxWidth(),
|
modifier = modifier.fillMaxWidth(),
|
||||||
) {
|
) {
|
||||||
|
// TODO migrate to material3
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(TangemTheme.dimens.size142),
|
.height(TangemTheme.dimens.size142),
|
||||||
value = state.passPhrase,
|
value = state.words,
|
||||||
onValueChange = state.passPhraseChange,
|
onValueChange = state.wordsChange,
|
||||||
textStyle = TangemTheme.typography.body1,
|
textStyle = TangemTheme.typography.body1,
|
||||||
singleLine = false,
|
singleLine = false,
|
||||||
|
colors = TangemTextFieldsDefault.defaultTextFieldColors,
|
||||||
visualTransformation = invalidWordsColorTransformation,
|
visualTransformation = invalidWordsColorTransformation,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -151,6 +172,7 @@ private fun SuggestionsBlock(
|
||||||
visible = suggestionsList.isNotEmpty(),
|
visible = suggestionsList.isNotEmpty(),
|
||||||
) {
|
) {
|
||||||
LazyRow(
|
LazyRow(
|
||||||
|
modifier = Modifier.padding(bottom = 8.dp),
|
||||||
contentPadding = PaddingValues(horizontal = TangemTheme.dimens.size16),
|
contentPadding = PaddingValues(horizontal = TangemTheme.dimens.size16),
|
||||||
) {
|
) {
|
||||||
items(suggestionsList.size) { index ->
|
items(suggestionsList.size) { index ->
|
||||||
|
|
@ -171,13 +193,14 @@ private fun SuggestionsBlock(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SuggestionButton(text: String, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
private fun SuggestionButton(text: String, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||||
Box(modifier = modifier.clickable(onClick = onClick)) {
|
Notifier(
|
||||||
Notifier(
|
text = text,
|
||||||
text = text,
|
modifier = modifier
|
||||||
backgroundColor = TangemTheme.colors.icon.primary1,
|
.clip(TangemTheme.shapes.roundedCorners8)
|
||||||
textColor = TangemTheme.colors.text.primary2,
|
.clickable(onClick = onClick),
|
||||||
)
|
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) {
|
private fun Modifier.rowPadding(index: Int, rowSize: Int, outSide: Dp, inSide: Dp): Modifier = when (index) {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,10 @@ import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MultiWalletSeedPhraseWords(state: MultiWalletSeedPhraseUM.GenerateSeedPhrase, modifier: Modifier = Modifier) {
|
internal fun MultiWalletSeedPhraseWords(
|
||||||
|
state: MultiWalletSeedPhraseUM.GenerateSeedPhrase,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
Column(modifier.fillMaxSize()) {
|
Column(modifier.fillMaxSize()) {
|
||||||
SegmentSeedBlock(
|
SegmentSeedBlock(
|
||||||
modifier = Modifier.padding(vertical = 20.dp),
|
modifier = Modifier.padding(vertical = 20.dp),
|
||||||
|
|
|
||||||
|
|
@ -13,18 +13,38 @@ import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.tangem.core.ui.R
|
import com.tangem.core.ui.R
|
||||||
|
import com.tangem.core.ui.components.BasicDialog
|
||||||
|
import com.tangem.core.ui.components.DialogButtonUM
|
||||||
import com.tangem.core.ui.components.OutlineTextField
|
import com.tangem.core.ui.components.OutlineTextField
|
||||||
import com.tangem.core.ui.components.PrimaryButtonIconEnd
|
import com.tangem.core.ui.components.PrimaryButtonIconEnd
|
||||||
|
import com.tangem.core.ui.extensions.resolveReference
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
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
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MultiWalletSeedPhraseWordsCheck(
|
internal fun MultiWalletSeedPhraseWordsCheck(
|
||||||
state: MultiWalletSeedPhraseUM.GeneratedWordsCheck,
|
state: MultiWalletSeedPhraseUM.GeneratedWordsCheck,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
if (state.dialog != null) {
|
||||||
|
BasicDialog(
|
||||||
|
title = state.dialog.title.resolveReference(),
|
||||||
|
message = state.dialog.message.resolveReference(),
|
||||||
|
confirmButton = DialogButtonUM(
|
||||||
|
title = state.dialog.confirmButtonText.resolveReference(),
|
||||||
|
onClick = state.dialog.onConfirmClick,
|
||||||
|
),
|
||||||
|
dismissButton = DialogButtonUM(
|
||||||
|
title = state.dialog.dismissButtonText.resolveReference(),
|
||||||
|
warning = state.dialog.dismissWarningColor,
|
||||||
|
onClick = state.dialog.onDismissButtonClick,
|
||||||
|
),
|
||||||
|
onDismissDialog = state.dialog.onDismiss,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,12 @@ import androidx.compose.runtime.Immutable
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||||
import com.tangem.core.ui.extensions.TextReference
|
import com.tangem.core.ui.extensions.TextReference
|
||||||
|
import com.tangem.features.onboarding.v2.multiwallet.impl.common.ui.OnboardingDialogUM
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
sealed class MultiWalletSeedPhraseUM(
|
internal sealed class MultiWalletSeedPhraseUM(
|
||||||
val order: Int,
|
val order: Int,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
@ -35,6 +36,7 @@ sealed class MultiWalletSeedPhraseUM(
|
||||||
val createWalletButtonEnabled: Boolean = false,
|
val createWalletButtonEnabled: Boolean = false,
|
||||||
val createWalletButtonProgress: Boolean = false,
|
val createWalletButtonProgress: Boolean = false,
|
||||||
val onCreateWalletButtonClick: () -> Unit = {},
|
val onCreateWalletButtonClick: () -> Unit = {},
|
||||||
|
val dialog: OnboardingDialogUM? = null,
|
||||||
) : MultiWalletSeedPhraseUM(order = 2) {
|
) : MultiWalletSeedPhraseUM(order = 2) {
|
||||||
data class WordField(
|
data class WordField(
|
||||||
val index: Int,
|
val index: Int,
|
||||||
|
|
@ -58,5 +60,6 @@ sealed class MultiWalletSeedPhraseUM(
|
||||||
val suggestionsList: ImmutableList<String> = persistentListOf(),
|
val suggestionsList: ImmutableList<String> = persistentListOf(),
|
||||||
val onSuggestionClick: (String) -> Unit = {},
|
val onSuggestionClick: (String) -> Unit = {},
|
||||||
val infoBottomSheetConfig: TangemBottomSheetConfig = TangemBottomSheetConfig.Empty,
|
val infoBottomSheetConfig: TangemBottomSheetConfig = TangemBottomSheetConfig.Empty,
|
||||||
|
val dialog: OnboardingDialogUM? = null,
|
||||||
) : MultiWalletSeedPhraseUM(order = 1)
|
) : MultiWalletSeedPhraseUM(order = 1)
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.tangem.features.onboarding.v2.multiwallet.impl.common.ui
|
||||||
|
|
||||||
|
import com.tangem.common.ui.alerts.models.AlertUM
|
||||||
|
import com.tangem.core.ui.extensions.TextReference
|
||||||
|
|
||||||
|
internal data class OnboardingDialogUM(
|
||||||
|
override val title: TextReference,
|
||||||
|
override val message: TextReference,
|
||||||
|
val dismissButtonText: TextReference,
|
||||||
|
override val confirmButtonText: TextReference,
|
||||||
|
val dismissWarningColor: Boolean = false,
|
||||||
|
override val onConfirmClick: () -> Unit,
|
||||||
|
val onDismissButtonClick: () -> Unit,
|
||||||
|
val onDismiss: () -> Unit,
|
||||||
|
) : AlertUM
|
||||||
|
|
@ -1,18 +1,17 @@
|
||||||
package com.tangem.features.onboarding.v2.multiwallet.impl.child.createwallet.model
|
package com.tangem.features.onboarding.v2.multiwallet.impl.common.ui
|
||||||
|
|
||||||
import com.tangem.core.ui.extensions.resourceReference
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
import com.tangem.features.onboarding.v2.impl.R
|
import com.tangem.features.onboarding.v2.impl.R
|
||||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.createwallet.ui.state.MultiWalletCreateWalletUM
|
|
||||||
|
|
||||||
internal fun resetCardDialog(onConfirm: () -> Unit, dismiss: () -> Unit, onDismissButtonClick: () -> Unit) =
|
internal fun resetCardDialog(onConfirm: () -> Unit, dismiss: () -> Unit, onDismissButtonClick: () -> Unit) =
|
||||||
MultiWalletCreateWalletUM.Dialog(
|
OnboardingDialogUM(
|
||||||
title = resourceReference(R.string.onboarding_activation_error_title),
|
title = resourceReference(R.string.onboarding_activation_error_title),
|
||||||
description = resourceReference(R.string.onboarding_activation_error_message),
|
message = resourceReference(R.string.onboarding_activation_error_message),
|
||||||
confirmButtonText = resourceReference(R.string.common_support),
|
confirmButtonText = resourceReference(R.string.common_support),
|
||||||
dismissButtonText = resourceReference(R.string.common_ok),
|
dismissButtonText = resourceReference(R.string.common_ok),
|
||||||
dismissWarningColor = true,
|
dismissWarningColor = true,
|
||||||
onDismiss = dismiss,
|
onDismiss = dismiss,
|
||||||
onConfirm = {
|
onConfirmClick = {
|
||||||
onConfirm()
|
onConfirm()
|
||||||
dismiss()
|
dismiss()
|
||||||
},
|
},
|
||||||
Loading…
Add table
Add a link
Reference in a new issue