Updated on 2026-08-14
This commit is contained in:
commit
f2bf06648f
31 changed files with 453 additions and 103 deletions
|
|
@ -26,6 +26,7 @@ import com.tangem.tap.common.redux.global.GlobalState
|
|||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletAction
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupStartedSource
|
||||
import com.tangem.tap.features.saveWallet.redux.SaveWalletAction
|
||||
import com.tangem.tap.mainScope
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
|
|
@ -67,10 +68,21 @@ object OnboardingHelper {
|
|||
}
|
||||
|
||||
fun whereToNavigate(scanResponse: ScanResponse): AppRoute {
|
||||
if (store.inject(DaggerGraphState::onboardingV2FeatureToggles).isOnboardingV2Enabled) {
|
||||
val newOnboardingSupportTypes = scanResponse.productType == ProductType.Wallet2 ||
|
||||
scanResponse.productType == ProductType.Ring ||
|
||||
scanResponse.productType == ProductType.Wallet
|
||||
if (store.inject(DaggerGraphState::onboardingV2FeatureToggles).isOnboardingV2Enabled &&
|
||||
newOnboardingSupportTypes
|
||||
) {
|
||||
val backupState = store.state.onboardingWalletState.backupState
|
||||
|
||||
return AppRoute.Onboarding(
|
||||
scanResponse = scanResponse,
|
||||
startFromBackup = false,
|
||||
mode = when (backupState.startedSource) {
|
||||
BackupStartedSource.Onboarding -> AppRoute.Onboarding.Mode.Onboarding
|
||||
BackupStartedSource.CreateBackup -> AppRoute.Onboarding.Mode.AddBackup
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -235,6 +235,10 @@ internal class ChildFactory @Inject constructor(
|
|||
params = OnboardingEntryComponent.Params(
|
||||
scanResponse = route.scanResponse,
|
||||
startBackupFlow = route.startFromBackup,
|
||||
multiWalletMode = when (route.mode) {
|
||||
AppRoute.Onboarding.Mode.Onboarding -> OnboardingEntryComponent.MultiWalletMode.Onboarding
|
||||
AppRoute.Onboarding.Mode.AddBackup -> OnboardingEntryComponent.MultiWalletMode.AddBackup
|
||||
},
|
||||
),
|
||||
componentFactory = onboardingEntryComponentFactory,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -329,5 +329,12 @@ sealed class AppRoute(val path: String) : Route {
|
|||
data class Onboarding(
|
||||
val scanResponse: ScanResponse,
|
||||
val startFromBackup: Boolean,
|
||||
) : AppRoute(path = "/onboarding_v2${if (startFromBackup) "/backup" else ""}")
|
||||
val mode: Mode = Mode.Onboarding,
|
||||
) : AppRoute(path = "/onboarding_v2${if (startFromBackup) "/backup" else ""}") {
|
||||
|
||||
enum class Mode {
|
||||
Onboarding, // general Mode
|
||||
AddBackup, // continue backup process for existing wallet 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,13 @@ interface OnboardingEntryComponent : ComposableContentComponent {
|
|||
data class Params(
|
||||
val scanResponse: ScanResponse,
|
||||
val startBackupFlow: Boolean,
|
||||
val multiWalletMode: MultiWalletMode,
|
||||
)
|
||||
|
||||
enum class MultiWalletMode {
|
||||
Onboarding,
|
||||
AddBackup,
|
||||
}
|
||||
|
||||
interface Factory : ComponentFactory<Params, OnboardingEntryComponent>
|
||||
}
|
||||
|
|
@ -20,7 +20,6 @@ import com.tangem.core.ui.components.*
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
@Composable
|
||||
internal fun OnboardingDone(onContinueClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
|
|
@ -85,7 +84,6 @@ internal fun OnboardingDone(onContinueClick: () -> Unit, modifier: Modifier = Mo
|
|||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
delay(timeMillis = 500)
|
||||
showConfetti = true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.features.onboarding.v2.entry.impl
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -48,7 +49,7 @@ internal class DefaultOnboardingEntryComponent @AssistedInject constructor(
|
|||
popBack = {
|
||||
popInternal { success ->
|
||||
if (success.not()) {
|
||||
model.stackNavigation.pop() // TODO
|
||||
model.stackNavigation.pop()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -126,6 +127,9 @@ internal class DefaultOnboardingEntryComponent @AssistedInject constructor(
|
|||
override fun Content(modifier: Modifier) {
|
||||
val innerStackState by innerStack.subscribeAsState()
|
||||
|
||||
// prevent back navigation
|
||||
BackHandler { }
|
||||
|
||||
OnboardingEntry(
|
||||
modifier = modifier,
|
||||
stepperContent = { modifierParam ->
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import com.tangem.domain.wallets.models.UserWallet
|
|||
import com.tangem.features.onboarding.v2.TitleProvider
|
||||
import com.tangem.features.onboarding.v2.entry.OnboardingEntryComponent
|
||||
import com.tangem.features.onboarding.v2.entry.impl.routing.OnboardingRoute
|
||||
import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
|
||||
import com.tangem.sdk.api.TangemSdkManager
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
|
|
@ -46,6 +47,11 @@ internal class OnboardingEntryModel @Inject constructor(
|
|||
val startRoute = routeByProductType(params.scanResponse)
|
||||
|
||||
private fun routeByProductType(scanResponse: ScanResponse): OnboardingRoute {
|
||||
val multiWalletNavigationMode = when (params.multiWalletMode) {
|
||||
OnboardingEntryComponent.MultiWalletMode.Onboarding -> OnboardingMultiWalletComponent.Mode.Onboarding
|
||||
OnboardingEntryComponent.MultiWalletMode.AddBackup -> OnboardingMultiWalletComponent.Mode.AddBackup
|
||||
}
|
||||
|
||||
return when (scanResponse.productType) {
|
||||
ProductType.Note -> TODO()
|
||||
ProductType.Twins -> TODO()
|
||||
|
|
@ -54,6 +60,7 @@ internal class OnboardingEntryModel @Inject constructor(
|
|||
withSeedPhraseFlow = false,
|
||||
titleProvider = titleProvider,
|
||||
onDone = ::onMultiWalletOnboardingDone,
|
||||
mode = multiWalletNavigationMode,
|
||||
)
|
||||
ProductType.Ring,
|
||||
ProductType.Wallet2,
|
||||
|
|
@ -62,6 +69,7 @@ internal class OnboardingEntryModel @Inject constructor(
|
|||
withSeedPhraseFlow = true,
|
||||
titleProvider = titleProvider,
|
||||
onDone = ::onMultiWalletOnboardingDone,
|
||||
mode = multiWalletNavigationMode,
|
||||
)
|
||||
ProductType.Start2Coin -> TODO()
|
||||
ProductType.Visa -> TODO()
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ internal class OnboardingChildFactory @Inject constructor(
|
|||
withSeedPhraseFlow = route.withSeedPhraseFlow,
|
||||
titleProvider = route.titleProvider,
|
||||
onDone = route.onDone,
|
||||
mode = route.mode,
|
||||
),
|
||||
)
|
||||
is OnboardingRoute.ManageTokens -> onboardingManageTokensComponentFactory.create(
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.core.decompose.navigation.Route
|
|||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.features.onboarding.v2.TitleProvider
|
||||
import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
|
||||
|
||||
sealed class OnboardingRoute : Route {
|
||||
|
||||
|
|
@ -13,6 +14,7 @@ sealed class OnboardingRoute : Route {
|
|||
val titleProvider: TitleProvider,
|
||||
val scanResponse: ScanResponse,
|
||||
val withSeedPhraseFlow: Boolean,
|
||||
val mode: OnboardingMultiWalletComponent.Mode,
|
||||
val onDone: (UserWallet) -> Unit,
|
||||
) : OnboardingRoute()
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,14 @@ interface OnboardingMultiWalletComponent : ComposableContentComponent, InnerNavi
|
|||
val titleProvider: TitleProvider,
|
||||
val scanResponse: ScanResponse,
|
||||
val withSeedPhraseFlow: Boolean,
|
||||
val mode: Mode,
|
||||
val onDone: (UserWallet) -> Unit,
|
||||
)
|
||||
|
||||
enum class Mode {
|
||||
Onboarding,
|
||||
AddBackup,
|
||||
}
|
||||
|
||||
interface Factory : ComponentFactory<Params, OnboardingMultiWalletComponent>
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -17,6 +18,7 @@ import com.arkivanov.decompose.router.slot.navigate
|
|||
import com.arkivanov.decompose.router.stack.*
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.arkivanov.essenty.instancekeeper.getOrCreateSimple
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
|
|
@ -24,6 +26,7 @@ import com.tangem.core.decompose.navigation.inner.InnerNavigation
|
|||
import com.tangem.core.decompose.navigation.inner.InnerNavigationState
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.analytics.OnboardingEvent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.accesscode.MultiWalletAccessCodeComponent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.MultiWalletBackupComponent
|
||||
|
|
@ -45,6 +48,7 @@ import kotlinx.coroutines.launch
|
|||
internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor(
|
||||
@Assisted private val context: AppComponentContext,
|
||||
@Assisted private val params: OnboardingMultiWalletComponent.Params,
|
||||
private val analyticsHandler: AnalyticsEventHandler,
|
||||
) : OnboardingMultiWalletComponent, AppComponentContext by context {
|
||||
|
||||
private val model: OnboardingMultiWalletModel = getOrCreateModel(params)
|
||||
|
|
@ -70,6 +74,7 @@ internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor
|
|||
multiWalletState = model.state,
|
||||
parentParams = params,
|
||||
innerNavigation = innerNavigationStateFlow,
|
||||
backups = model.backups,
|
||||
)
|
||||
|
||||
private val childStack: Value<ChildStack<OnboardingMultiWalletState.Step, ComposableContentComponent>> =
|
||||
|
|
@ -92,13 +97,10 @@ internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor
|
|||
override val state = innerNavigationStateFlow
|
||||
|
||||
override fun pop(onComplete: (Boolean) -> Unit) {
|
||||
when (childStack.active.configuration) {
|
||||
CreateWallet -> { } // TODO show dialog
|
||||
ChooseBackupOption -> { } // TODO show dialog
|
||||
SeedPhrase -> { componentScope.launch { backButtonClickFlow.emit(Unit) } }
|
||||
AddBackupDevice -> {}
|
||||
Finalize -> {}
|
||||
Done -> {}
|
||||
if (childStack.active.configuration == SeedPhrase) {
|
||||
componentScope.launch { backButtonClickFlow.emit(Unit) }
|
||||
} else {
|
||||
model.onBack()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -183,6 +185,7 @@ internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor
|
|||
}
|
||||
Done -> {
|
||||
// final step - navigate to parent
|
||||
analyticsHandler.send(OnboardingEvent.Finished)
|
||||
val userWallet = childParams.multiWalletState.value.resultUserWallet ?: return
|
||||
params.onDone(userWallet)
|
||||
}
|
||||
|
|
@ -237,6 +240,8 @@ internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor
|
|||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
val artworksState by artworksState.collectAsStateWithLifecycle()
|
||||
|
||||
BackHandler { model.onBack() }
|
||||
|
||||
OnboardingMultiWallet(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.analytics
|
||||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
|
||||
sealed class OnboardingEvent(
|
||||
category: String,
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent(category, event, params) {
|
||||
|
||||
data object Started : OnboardingEvent("Onboarding", "Onboarding Started")
|
||||
data object Finished : OnboardingEvent("Onboarding", "Onboarding Finished")
|
||||
|
||||
sealed class CreateWallet(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
) : OnboardingEvent("Onboarding / Create Wallet", event, params) {
|
||||
|
||||
data object ScreenOpened : CreateWallet("Create Wallet Screen Opened")
|
||||
data object ButtonCreateWallet : CreateWallet("Button - Create Wallet")
|
||||
class WalletCreatedSuccessfully(
|
||||
creationType: WalletCreationType = WalletCreationType.PrivateKey,
|
||||
seedPhraseLength: Int? = null,
|
||||
) : CreateWallet(
|
||||
event = "Wallet Created Successfully",
|
||||
params = buildMap {
|
||||
put("Creation Type", creationType.value)
|
||||
if (seedPhraseLength != null) {
|
||||
put("Seed Phrase Length", seedPhraseLength.toString())
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
sealed class WalletCreationType(val value: String) {
|
||||
data object PrivateKey : WalletCreationType(value = "Private Key")
|
||||
data object NewSeed : WalletCreationType(value = "New Seed")
|
||||
data object SeedImport : WalletCreationType(value = "Seed Import")
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Backup(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
) : OnboardingEvent("Onboarding / Backup", event, params) {
|
||||
|
||||
data object ScreenOpened : Backup("Backup Screen Opened")
|
||||
data object Started : Backup("Backup Started")
|
||||
data object Skipped : Backup("Backup Skipped")
|
||||
data object SettingAccessCodeStarted : Backup("Setting Access Code Started")
|
||||
data object AccessCodeEntered : Backup("Access Code Entered")
|
||||
data object AccessCodeReEntered : Backup("Access Code Re-entered")
|
||||
|
||||
class Finished(cardsCount: Int) : Backup(
|
||||
event = "Backup Finished",
|
||||
params = mapOf("Cards count" to "$cardsCount"),
|
||||
)
|
||||
|
||||
data object ResetCancelEvent : Backup(
|
||||
event = "Reset Card Notification",
|
||||
params = mapOf("Option" to "Cancel"),
|
||||
)
|
||||
|
||||
data object ResetPerformEvent : Backup(
|
||||
event = "Reset Card Notification",
|
||||
params = mapOf("Option" to "Reset"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -8,5 +8,34 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
class MultiWalletChildParams(
|
||||
val multiWalletState: MutableStateFlow<OnboardingMultiWalletState>,
|
||||
val innerNavigation: MutableStateFlow<MultiWalletInnerNavigationState>,
|
||||
val backups: MutableStateFlow<Backup>,
|
||||
val parentParams: OnboardingMultiWalletComponent.Params,
|
||||
)
|
||||
) {
|
||||
data class Backup(
|
||||
val card2: BackupCardInfo? = null,
|
||||
val card3: BackupCardInfo? = null,
|
||||
) {
|
||||
data class BackupCardInfo(
|
||||
val cardId: String,
|
||||
val cardPublicKey: ByteArray,
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as BackupCardInfo
|
||||
|
||||
if (cardId != other.cardId) return false
|
||||
if (!cardPublicKey.contentEquals(other.cardPublicKey)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = cardId.hashCode()
|
||||
result = 31 * result + cardPublicKey.contentHashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.child.accesscode.model
|
||||
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.analytics.OnboardingEvent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.accesscode.ui.state.MultiWalletAccessCodeUM
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.model.OnboardingMultiWalletState
|
||||
|
|
@ -23,6 +25,7 @@ internal class MultiWalletAccessCodeModel @Inject constructor(
|
|||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val backupServiceHolder: BackupServiceHolder,
|
||||
private val analyticsHandler: AnalyticsEventHandler,
|
||||
) : Model() {
|
||||
|
||||
private val _uiState = MutableStateFlow(getInitialState())
|
||||
|
|
@ -32,6 +35,8 @@ internal class MultiWalletAccessCodeModel @Inject constructor(
|
|||
val onDismiss = MutableSharedFlow<Unit>()
|
||||
|
||||
init {
|
||||
analyticsHandler.send(OnboardingEvent.Backup.SettingAccessCodeStarted)
|
||||
|
||||
params.multiWalletState.update {
|
||||
it.copy(accessCode = null)
|
||||
}
|
||||
|
|
@ -111,6 +116,8 @@ internal class MultiWalletAccessCodeModel @Inject constructor(
|
|||
it.copy(atLeast4CharError = true)
|
||||
}
|
||||
}
|
||||
|
||||
analyticsHandler.send(OnboardingEvent.Backup.AccessCodeEntered)
|
||||
}
|
||||
MultiWalletAccessCodeUM.Step.ConfirmAccessCode -> {
|
||||
if (checkAccessCode()) {
|
||||
|
|
@ -123,6 +130,8 @@ internal class MultiWalletAccessCodeModel @Inject constructor(
|
|||
}
|
||||
modelScope.launch { onDismiss.emit(Unit) }
|
||||
}
|
||||
|
||||
analyticsHandler.send(OnboardingEvent.Backup.AccessCodeReEntered)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.model
|
|||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
|
|
@ -10,6 +11,7 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.models.scan.ProductType
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.analytics.OnboardingEvent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.MultiWalletBackupComponent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.ui.backupCardAttestationFailedDialog
|
||||
|
|
@ -33,6 +35,7 @@ class MultiWalletBackupModel @Inject constructor(
|
|||
private val backupServiceHolder: BackupServiceHolder,
|
||||
private val cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
private val tangemSdkManager: TangemSdkManager,
|
||||
private val analyticsHandler: AnalyticsEventHandler,
|
||||
) : Model() {
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
|
|
@ -57,6 +60,15 @@ class MultiWalletBackupModel @Inject constructor(
|
|||
val uiState: StateFlow<MultiWalletBackupUM> = _uiState
|
||||
val eventFlow = MutableSharedFlow<MultiWalletBackupComponent.Event>()
|
||||
|
||||
init {
|
||||
// for wallet 1 this event is sent in Wallet1ChooseOptionModel
|
||||
if (scanResponse.productType == ProductType.Wallet2 || scanResponse.productType == ProductType.Ring) {
|
||||
analyticsHandler.send(OnboardingEvent.Backup.ScreenOpened)
|
||||
}
|
||||
|
||||
analyticsHandler.send(OnboardingEvent.Backup.Started)
|
||||
}
|
||||
|
||||
private fun getInitState(): MultiWalletBackupUM {
|
||||
return when (backupService.currentState) {
|
||||
BackupService.State.Preparing -> {
|
||||
|
|
@ -73,6 +85,7 @@ class MultiWalletBackupModel @Inject constructor(
|
|||
},
|
||||
)
|
||||
}
|
||||
// TODO
|
||||
is BackupService.State.FinalizingBackupCard -> TODO()
|
||||
BackupService.State.FinalizingPrimaryCard -> TODO()
|
||||
BackupService.State.Finished -> TODO()
|
||||
|
|
@ -88,9 +101,6 @@ class MultiWalletBackupModel @Inject constructor(
|
|||
if (primaryCard != null) {
|
||||
backupService.setPrimaryCard(primaryCard)
|
||||
addBackupCardWithService()
|
||||
} else {
|
||||
// TODO wallet 2 ??? or from main?
|
||||
// BackupAction.StartAddingPrimaryCard
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -145,6 +155,8 @@ class MultiWalletBackupModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
modelScope.launch { eventFlow.emit(MultiWalletBackupComponent.Event.Done) }
|
||||
|
||||
analyticsHandler.send(OnboardingEvent.Backup.Finished(cardsCount = state.value.backupCardsNumber + 1))
|
||||
}
|
||||
|
||||
private fun addBackupCardWithService() {
|
||||
|
|
@ -167,6 +179,18 @@ class MultiWalletBackupModel @Inject constructor(
|
|||
backupCardsNumber = it.backupCardsNumber + 1,
|
||||
)
|
||||
}
|
||||
|
||||
val backupCardInfo = MultiWalletChildParams.Backup.BackupCardInfo(
|
||||
cardId = result.data.cardId,
|
||||
cardPublicKey = result.data.cardPublicKey,
|
||||
)
|
||||
params.backups.update {
|
||||
it.copy(
|
||||
card2 = if (state.value.backupCardsNumber == 1) backupCardInfo else it.card2,
|
||||
card3 = if (state.value.backupCardsNumber == 2) backupCardInfo else it.card3,
|
||||
)
|
||||
}
|
||||
|
||||
setNumberOfBackupCards(state.value.backupCardsNumber)
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
|
|
@ -176,7 +200,12 @@ class MultiWalletBackupModel @Inject constructor(
|
|||
st.copy(
|
||||
dialog = resetBackupCardDialog(
|
||||
onReset = { resetBackupCard(cardId = error.cardId) },
|
||||
onDismiss = { _uiState.update { it.copy(dialog = null) } },
|
||||
onDismiss = {
|
||||
_uiState.update { it.copy(dialog = null) }
|
||||
},
|
||||
onDismissClick = {
|
||||
analyticsHandler.send(OnboardingEvent.Backup.ResetCancelEvent)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -198,6 +227,8 @@ class MultiWalletBackupModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun resetBackupCard(cardId: String) {
|
||||
analyticsHandler.send(OnboardingEvent.Backup.ResetPerformEvent)
|
||||
|
||||
modelScope.launch {
|
||||
tangemSdkManager.resetToFactorySettings(
|
||||
cardId = cardId,
|
||||
|
|
|
|||
|
|
@ -4,16 +4,20 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.features.onboarding.v2.impl.R
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.backup.ui.state.MultiWalletBackupUMDialog
|
||||
|
||||
internal fun resetBackupCardDialog(onReset: () -> Unit, onDismiss: () -> Unit) = MultiWalletBackupUMDialog(
|
||||
title = resourceReference(R.string.common_attention),
|
||||
bodyText = resourceReference(R.string.onboarding_linking_error_card_with_wallets),
|
||||
confirmText = resourceReference(R.string.common_cancel),
|
||||
cancelText = resourceReference(R.string.card_settings_action_sheet_reset),
|
||||
warningCancelColor = true,
|
||||
onDismiss = onDismiss,
|
||||
onConfirm = onDismiss,
|
||||
onCancel = { onReset(); onDismiss() },
|
||||
)
|
||||
internal fun resetBackupCardDialog(onReset: () -> Unit, onDismiss: () -> Unit, onDismissClick: () -> Unit) =
|
||||
MultiWalletBackupUMDialog(
|
||||
title = resourceReference(R.string.common_attention),
|
||||
bodyText = resourceReference(R.string.onboarding_linking_error_card_with_wallets),
|
||||
confirmText = resourceReference(R.string.common_cancel),
|
||||
cancelText = resourceReference(R.string.card_settings_action_sheet_reset),
|
||||
warningCancelColor = true,
|
||||
onDismiss = {
|
||||
onDismiss()
|
||||
onDismissClick()
|
||||
},
|
||||
onConfirm = onDismiss,
|
||||
onCancel = { onReset(); onDismiss() },
|
||||
)
|
||||
|
||||
internal fun backupCardAttestationFailedDialog(onDismiss: () -> Unit) = MultiWalletBackupUMDialog(
|
||||
title = resourceReference(R.string.common_error),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.child.chooseoption.model
|
||||
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
|
|
@ -9,6 +10,7 @@ import com.tangem.domain.wallets.builder.UserWalletBuilder
|
|||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
|
||||
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.analytics.OnboardingEvent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
|
|
@ -23,18 +25,25 @@ internal class Wallet1ChooseOptionModel @Inject constructor(
|
|||
private val generateWalletNameUseCase: GenerateWalletNameUseCase,
|
||||
private val cardRepository: CardRepository,
|
||||
private val saveWalletUseCase: SaveWalletUseCase,
|
||||
private val analyticsHandler: AnalyticsEventHandler,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<MultiWalletChildParams>()
|
||||
private var skipClicked = false
|
||||
val returnToParentFlow = MutableSharedFlow<Unit>()
|
||||
|
||||
init {
|
||||
analyticsHandler.send(OnboardingEvent.Backup.ScreenOpened)
|
||||
}
|
||||
|
||||
fun onSkipClick() {
|
||||
// TODO show confirmation dialog
|
||||
|
||||
if (skipClicked) return
|
||||
skipClicked = true
|
||||
|
||||
analyticsHandler.send(OnboardingEvent.Backup.Skipped)
|
||||
|
||||
modelScope.launch {
|
||||
val scanResponse = params.multiWalletState.value.currentScanResponse
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.features.onboarding.v2.multiwallet.impl.child.createwallet.mo
|
|||
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
|
|
@ -11,6 +12,7 @@ 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.impl.R
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.analytics.OnboardingEvent
|
||||
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.common.ui.resetCardDialog
|
||||
|
|
@ -24,6 +26,7 @@ import kotlinx.coroutines.flow.update
|
|||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@ComponentScoped
|
||||
internal class MultiWalletCreateWalletModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
|
|
@ -32,6 +35,7 @@ internal class MultiWalletCreateWalletModel @Inject constructor(
|
|||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val getCardInfoUseCase: GetCardInfoUseCase,
|
||||
private val cardRepository: CardRepository,
|
||||
private val analyticsHandler: AnalyticsEventHandler,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<MultiWalletChildParams>()
|
||||
|
|
@ -50,7 +54,10 @@ internal class MultiWalletCreateWalletModel @Inject constructor(
|
|||
} else {
|
||||
resourceReference(R.string.onboarding_create_wallet_body)
|
||||
},
|
||||
onCreateWalletClick = { createWallet(false) },
|
||||
onCreateWalletClick = {
|
||||
analyticsHandler.send(OnboardingEvent.CreateWallet.ButtonCreateWallet)
|
||||
createWallet(false)
|
||||
},
|
||||
showOtherOptionsButton = params.parentParams.withSeedPhraseFlow,
|
||||
onOtherOptionsClick = {
|
||||
modelScope.launch {
|
||||
|
|
@ -64,6 +71,10 @@ internal class MultiWalletCreateWalletModel @Inject constructor(
|
|||
val uiState: StateFlow<MultiWalletCreateWalletUM> = _uiState
|
||||
val onDone = MutableSharedFlow<OnboardingMultiWalletState.Step>()
|
||||
|
||||
init {
|
||||
analyticsHandler.send(OnboardingEvent.CreateWallet.ScreenOpened)
|
||||
}
|
||||
|
||||
private fun createWallet(shouldReset: Boolean) {
|
||||
modelScope.launch {
|
||||
val result = tangemSdkManager.createProductWallet(
|
||||
|
|
@ -91,8 +102,7 @@ internal class MultiWalletCreateWalletModel @Inject constructor(
|
|||
onDone.emit(OnboardingMultiWalletState.Step.ChooseBackupOption)
|
||||
}
|
||||
|
||||
// TODO
|
||||
// Analytics.send(Onboarding.CreateWallet.WalletCreatedSuccessfully())
|
||||
analyticsHandler.send(OnboardingEvent.CreateWallet.WalletCreatedSuccessfully())
|
||||
}
|
||||
|
||||
is CompletionResult.Failure -> {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
|||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.finalize.MultiWalletFinalizeComponent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.finalize.ui.state.MultiWalletFinalizeUM
|
||||
|
|
@ -78,7 +79,7 @@ internal class MultiWalletFinalizeModel @Inject constructor(
|
|||
|
||||
private fun writePrimaryCard() {
|
||||
val backupService = backupServiceHolder.backupService.get() ?: return
|
||||
// store.dispatchOnMain(BackupAction.SetHasRing(hasRing = isRing))
|
||||
// TODO store.dispatchOnMain(BackupAction.SetHasRing(hasRing = isRing))
|
||||
|
||||
val primaryCardBatchId = backupService.primaryCardBatchId ?: return
|
||||
val isRing = isRing(primaryCardBatchId)
|
||||
|
|
@ -107,7 +108,7 @@ internal class MultiWalletFinalizeModel @Inject constructor(
|
|||
private fun writeBackupCard(cardIndex: Int) {
|
||||
val backupService = backupServiceHolder.backupService.get() ?: return
|
||||
|
||||
// store.dispatchOnMain(BackupAction.SetHasRing(hasRing = isRing))
|
||||
// TODO store.dispatchOnMain(BackupAction.SetHasRing(hasRing = isRing))
|
||||
|
||||
val backupCardBatchId = backupService.backupCardsBatchIds.getOrNull(cardIndex) ?: return
|
||||
val isRing = isRing(backupCardBatchId)
|
||||
|
|
@ -119,7 +120,6 @@ internal class MultiWalletFinalizeModel @Inject constructor(
|
|||
is CompletionResult.Success -> {
|
||||
val backupValidator = BackupValidator()
|
||||
if (backupValidator.isValidBackupStatus(CardDTO(result.data)).not()) {
|
||||
// TODO store.dispatchOnMain(BackupAction.ErrorInBackupCard)
|
||||
walletHasBackupError = true
|
||||
}
|
||||
|
||||
|
|
@ -155,43 +155,36 @@ internal class MultiWalletFinalizeModel @Inject constructor(
|
|||
|
||||
modelScope.launch {
|
||||
val scanResponse = params.multiWalletState.value.currentScanResponse
|
||||
|
||||
val userWallet = createUserWallet(scanResponse)
|
||||
userWalletsListManager.save(
|
||||
userWallet = userWallet.copy(
|
||||
scanResponse = scanResponse.updateScanResponseAfterBackup(),
|
||||
),
|
||||
canOverride = true,
|
||||
)
|
||||
|
||||
// TODO
|
||||
// BackupStartedSource.CreateBackup -> updateWallet(
|
||||
// userWalletsListManager = userWalletsListManager,
|
||||
// userWallet = userWallet,
|
||||
// backupState = backupState,
|
||||
// )
|
||||
|
||||
// TODO maybe here we should check for backup cards activation status
|
||||
// if (notActivatedCardIds.isEmpty()) {
|
||||
// delay(1000)
|
||||
// store.dispatchWithMain(BackupAction.BackupFinished(userWallet?.walletId))
|
||||
// return@launch
|
||||
// }
|
||||
//
|
||||
// Analytics.send(Onboarding.Finished())
|
||||
//
|
||||
// store.state.globalState.onboardingState.onboardingManager?.finishActivation(notActivatedCardIds)
|
||||
// handleFinishBackup(requireNotNull(scanResponse), userWallet)
|
||||
// delay(1000)
|
||||
// store.dispatchWithMain(BackupAction.BackupFinished(userWalletId = userWallet?.walletId))
|
||||
|
||||
cardRepository.finishCardActivation(scanResponse.card.cardId)
|
||||
when (params.parentParams.mode) {
|
||||
OnboardingMultiWalletComponent.Mode.Onboarding -> {
|
||||
userWalletsListManager.save(
|
||||
userWallet = userWallet.copy(
|
||||
scanResponse = scanResponse.updateScanResponseAfterBackup(),
|
||||
),
|
||||
canOverride = true,
|
||||
)
|
||||
}
|
||||
OnboardingMultiWalletComponent.Mode.AddBackup -> {
|
||||
userWalletsListManager.update(
|
||||
userWalletId = userWallet.walletId,
|
||||
update = { wallet ->
|
||||
wallet.copy(
|
||||
scanResponse = scanResponse.updateScanResponseAfterBackup(),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// save user wallet for manage tokens screen
|
||||
params.multiWalletState.update {
|
||||
it.copy(resultUserWallet = userWallet)
|
||||
}
|
||||
|
||||
cardRepository.finishCardActivation(scanResponse.card.cardId)
|
||||
|
||||
onEvent.emit(MultiWalletFinalizeComponent.Event.ThreeBackupCardsAdded)
|
||||
}
|
||||
}
|
||||
|
|
@ -218,8 +211,8 @@ internal class MultiWalletFinalizeModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun handleActivationError() {
|
||||
_uiState.update {
|
||||
it.copy(
|
||||
_uiState.update { st ->
|
||||
st.copy(
|
||||
dialog = resetCardDialog(
|
||||
onConfirm = ::navigateToSupportScreen,
|
||||
dismiss = { _uiState.update { it.copy(dialog = null) } },
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.features.onboarding.v2.multiwallet.impl.child.seedphrase.mode
|
|||
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
|
|
@ -11,6 +12,7 @@ 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.analytics.OnboardingEvent
|
||||
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.ImportSeedPhraseUiStateBuilder
|
||||
|
|
@ -41,6 +43,7 @@ internal class MultiWalletSeedPhraseModel @Inject constructor(
|
|||
private val cardRepository: CardRepository,
|
||||
private val getCardInfoUseCase: GetCardInfoUseCase,
|
||||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val analyticsHandler: AnalyticsEventHandler,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<MultiWalletChildParams>()
|
||||
|
|
@ -76,6 +79,7 @@ internal class MultiWalletSeedPhraseModel @Inject constructor(
|
|||
state.value.generatedWords12 ?: return@importWallet
|
||||
},
|
||||
passphrase = null,
|
||||
generatedSeedPhrase = true,
|
||||
)
|
||||
},
|
||||
// =============================================
|
||||
|
|
@ -85,7 +89,13 @@ internal class MultiWalletSeedPhraseModel @Inject constructor(
|
|||
modelScope = modelScope,
|
||||
mnemonicRepository = mnemonicRepository,
|
||||
updateUiState = { block -> updateUiStateSpecific(block) },
|
||||
importWallet = ::importWallet,
|
||||
importWallet = { mnemonic, passphrase ->
|
||||
importWallet(
|
||||
mnemonic = mnemonic,
|
||||
passphrase = passphrase,
|
||||
generatedSeedPhrase = false,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
val uiState = _uiState.asStateFlow()
|
||||
|
|
@ -159,7 +169,7 @@ internal class MultiWalletSeedPhraseModel @Inject constructor(
|
|||
// =============================================
|
||||
// | DON'T FIXME !!! MODIFY WITH CAUTION !!! |
|
||||
// =============================================
|
||||
private fun importWallet(mnemonic: Mnemonic, passphrase: String?) {
|
||||
private fun importWallet(mnemonic: Mnemonic, passphrase: String?, generatedSeedPhrase: Boolean) {
|
||||
val currentState = state.value
|
||||
if (currentState.readyToImport.not()) return
|
||||
|
||||
|
|
@ -175,7 +185,17 @@ internal class MultiWalletSeedPhraseModel @Inject constructor(
|
|||
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
// TODO on success send analytics event
|
||||
analyticsHandler.send(
|
||||
OnboardingEvent.CreateWallet.WalletCreatedSuccessfully(
|
||||
creationType = if (generatedSeedPhrase) {
|
||||
OnboardingEvent.CreateWallet.WalletCreationType.NewSeed
|
||||
} else {
|
||||
OnboardingEvent.CreateWallet.WalletCreationType.SeedImport
|
||||
},
|
||||
seedPhraseLength = mnemonic.mnemonicComponents.size,
|
||||
),
|
||||
)
|
||||
|
||||
multiWalletState.update {
|
||||
it.copy(
|
||||
currentScanResponse = it.currentScanResponse.copy(
|
||||
|
|
|
|||
|
|
@ -19,4 +19,20 @@ internal fun resetCardDialog(onConfirm: () -> Unit, dismiss: () -> Unit, onDismi
|
|||
onDismissButtonClick()
|
||||
dismiss()
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
internal fun interruptBackupDialog(onConfirm: () -> Unit, dismiss: () -> Unit) = OnboardingDialogUM(
|
||||
title = resourceReference(R.string.onboarding_exit_alert_title),
|
||||
message = resourceReference(R.string.onboarding_exit_alert_message),
|
||||
confirmButtonText = resourceReference(R.string.common_ok),
|
||||
dismissButtonText = resourceReference(R.string.common_cancel),
|
||||
dismissWarningColor = true,
|
||||
onDismiss = dismiss,
|
||||
onConfirmClick = {
|
||||
onConfirm()
|
||||
dismiss()
|
||||
},
|
||||
onDismissButtonClick = {
|
||||
dismiss()
|
||||
},
|
||||
)
|
||||
|
|
@ -1,11 +1,16 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.model
|
||||
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.wallets.usecase.GetCardImageUseCase
|
||||
import com.tangem.features.onboarding.v2.multiwallet.api.OnboardingMultiWalletComponent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.analytics.OnboardingEvent
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.common.ui.interruptBackupDialog
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.ui.state.OnboardingMultiWalletUM
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
|
@ -16,6 +21,8 @@ import javax.inject.Inject
|
|||
internal class OnboardingMultiWalletModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val router: Router,
|
||||
private val analyticsHandler: AnalyticsEventHandler,
|
||||
) : Model() {
|
||||
private val params = paramsContainer.require<OnboardingMultiWalletComponent.Params>()
|
||||
private val getCardImageUseCase = GetCardImageUseCase()
|
||||
|
|
@ -30,16 +37,50 @@ internal class OnboardingMultiWalletModel @Inject constructor(
|
|||
resultUserWallet = null,
|
||||
),
|
||||
)
|
||||
|
||||
val backups = MutableStateFlow(MultiWalletChildParams.Backup())
|
||||
val uiState = _uiState.asStateFlow()
|
||||
|
||||
init {
|
||||
// TODO add analytics
|
||||
// if (!manager.isActivationStarted(notNullCard.cardId)) {
|
||||
// Analytics.send(Onboarding.Started())
|
||||
// }
|
||||
initScreenTitleSub()
|
||||
analyticsHandler.send(OnboardingEvent.Started)
|
||||
initScreenTitle()
|
||||
loadCardArtwork()
|
||||
subscribeToBackups()
|
||||
}
|
||||
|
||||
fun onBack() {
|
||||
_uiState.update {
|
||||
it.copy(
|
||||
dialog = interruptBackupDialog(
|
||||
onConfirm = {
|
||||
router.pop()
|
||||
},
|
||||
dismiss = { _uiState.update { it.copy(dialog = null) } },
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribeToBackups() {
|
||||
modelScope.launch {
|
||||
backups.collectLatest { bst ->
|
||||
when {
|
||||
_uiState.value.artwork2Url == null && bst.card2 != null -> {
|
||||
val artwork =
|
||||
getCardImageUseCase.invoke(bst.card2.cardId, bst.card2.cardPublicKey)
|
||||
_uiState.update {
|
||||
it.copy(artwork2Url = artwork)
|
||||
}
|
||||
}
|
||||
_uiState.value.artwork3Url == null && bst.card3 != null -> {
|
||||
val artwork =
|
||||
getCardImageUseCase.invoke(bst.card3.cardId, bst.card3.cardPublicKey)
|
||||
_uiState.update {
|
||||
it.copy(artwork3Url = artwork)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getInitialStep(): OnboardingMultiWalletState.Step {
|
||||
|
|
@ -56,17 +97,9 @@ internal class OnboardingMultiWalletModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun initScreenTitleSub() {
|
||||
private fun initScreenTitle() {
|
||||
val title = screenTitleByStep(getInitialStep())
|
||||
params.titleProvider.changeTitle(title)
|
||||
|
||||
modelScope.launch {
|
||||
state.map { it.currentStep }
|
||||
.collectLatest { step ->
|
||||
val stepTitle = screenTitleByStep(step)
|
||||
params.titleProvider.changeTitle(stepTitle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadCardArtwork() {
|
||||
|
|
@ -76,7 +109,7 @@ internal class OnboardingMultiWalletModel @Inject constructor(
|
|||
|
||||
if (artwork != getCardImageUseCase.getDefaultFallbackUrl()) {
|
||||
_uiState.update {
|
||||
it.copy(artworkUrl = artwork)
|
||||
it.copy(artwork1Url = artwork)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.BasicDialog
|
||||
import com.tangem.core.ui.components.DialogButtonUM
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.ui.state.OnboardingMultiWalletUM
|
||||
|
||||
|
|
@ -20,12 +23,31 @@ internal fun OnboardingMultiWallet(
|
|||
modifier: Modifier = Modifier,
|
||||
childContent: @Composable (Modifier) -> Unit = {},
|
||||
) {
|
||||
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(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
) {
|
||||
if (isSeedPhraseState.not()) {
|
||||
WalletArtworks(
|
||||
url = state.artworkUrl,
|
||||
url1 = state.artwork1Url,
|
||||
url2 = state.artwork2Url,
|
||||
url3 = state.artwork3Url,
|
||||
modifier = Modifier
|
||||
.weight(.56f)
|
||||
.padding(horizontal = 34.dp)
|
||||
|
|
@ -51,7 +73,7 @@ internal fun OnboardingMultiWallet(
|
|||
private fun Preview() {
|
||||
TangemThemePreview {
|
||||
OnboardingMultiWallet(
|
||||
state = OnboardingMultiWalletUM(artworkUrl = null),
|
||||
state = OnboardingMultiWalletUM(artwork1Url = null),
|
||||
artworksState = WalletArtworksState.Folded,
|
||||
isSeedPhraseState = false,
|
||||
childContent = { md ->
|
||||
|
|
@ -71,7 +93,7 @@ private fun Preview() {
|
|||
private fun Preview2() {
|
||||
TangemThemePreview {
|
||||
OnboardingMultiWallet(
|
||||
state = OnboardingMultiWalletUM(artworkUrl = null),
|
||||
state = OnboardingMultiWalletUM(artwork1Url = null),
|
||||
artworksState = WalletArtworksState.Folded,
|
||||
isSeedPhraseState = true,
|
||||
childContent = { md ->
|
||||
|
|
|
|||
|
|
@ -58,7 +58,13 @@ sealed class WalletArtworksState {
|
|||
}
|
||||
|
||||
@Composable
|
||||
fun WalletArtworks(url: String?, state: WalletArtworksState, modifier: Modifier = Modifier) {
|
||||
fun WalletArtworks(
|
||||
url1: String?,
|
||||
url2: String?,
|
||||
url3: String?,
|
||||
state: WalletArtworksState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (state is WalletArtworksState.Hidden) return
|
||||
|
||||
BoxWithConstraints(
|
||||
|
|
@ -116,7 +122,9 @@ fun WalletArtworks(url: String?, state: WalletArtworksState, modifier: Modifier
|
|||
}
|
||||
|
||||
AnimatedWalletCards(
|
||||
url = url,
|
||||
url1 = url1,
|
||||
url2 = url2,
|
||||
url3 = url3,
|
||||
modifier = Modifier
|
||||
.widthIn(max = 450.dp)
|
||||
.matchParentSize(),
|
||||
|
|
@ -143,9 +151,12 @@ private data class WalletCardTransitionState(
|
|||
val zIndex: Float = 0f,
|
||||
)
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
private fun AnimatedWalletCards(
|
||||
url: String?,
|
||||
url1: String?,
|
||||
url2: String?,
|
||||
url3: String?,
|
||||
transition1: Transition<WalletCardTransitionState>,
|
||||
transition2: Transition<WalletCardTransitionState>,
|
||||
transition3: Transition<WalletCardTransitionState>,
|
||||
|
|
@ -190,7 +201,7 @@ private fun AnimatedWalletCards(
|
|||
scaleY = scaleY3
|
||||
rotationZ = rotation3
|
||||
},
|
||||
url = url,
|
||||
url = url3,
|
||||
)
|
||||
|
||||
WalletCard(
|
||||
|
|
@ -208,7 +219,7 @@ private fun AnimatedWalletCards(
|
|||
scaleX = scaleX2
|
||||
scaleY = scaleY2
|
||||
},
|
||||
url = url,
|
||||
url = url2,
|
||||
)
|
||||
|
||||
WalletCard(
|
||||
|
|
@ -225,7 +236,7 @@ private fun AnimatedWalletCards(
|
|||
translationY = translationY1
|
||||
rotationZ = rotation1
|
||||
},
|
||||
url = url,
|
||||
url = url1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -450,7 +461,9 @@ private fun Preview() {
|
|||
var state: WalletArtworksState by remember { mutableStateOf(WalletArtworksState.Folded) }
|
||||
|
||||
WalletArtworks(
|
||||
url = null,
|
||||
url1 = null,
|
||||
url2 = null,
|
||||
url3 = null,
|
||||
state = state,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
package com.tangem.features.onboarding.v2.multiwallet.impl.ui.state
|
||||
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.common.ui.OnboardingDialogUM
|
||||
|
||||
internal data class OnboardingMultiWalletUM(
|
||||
val artworkUrl: String? = null,
|
||||
val artwork1Url: String? = null,
|
||||
val artwork2Url: String? = null,
|
||||
val artwork3Url: String? = null,
|
||||
val dialog: OnboardingDialogUM? = null,
|
||||
)
|
||||
|
|
@ -6,6 +6,8 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.arkivanov.essenty.instancekeeper.getOrCreateSimple
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.domain.feedback.GetCardInfoUseCase
|
||||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||
|
|
@ -23,11 +25,19 @@ internal class DefaultOnboardingStepperComponent @AssistedInject constructor(
|
|||
@Assisted val params: OnboardingStepperComponent.Params,
|
||||
private val getCardInfoUseCase: GetCardInfoUseCase,
|
||||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val analyticsHandler: AnalyticsEventHandler,
|
||||
) : OnboardingStepperComponent, AppComponentContext by context {
|
||||
|
||||
override val state = instanceKeeper.getOrCreateSimple { MutableStateFlow(params.initState) }
|
||||
|
||||
private fun openSupport() {
|
||||
analyticsHandler.send(
|
||||
AnalyticsEvent(
|
||||
category = "Onboarding",
|
||||
event = "Button - Chat",
|
||||
),
|
||||
)
|
||||
|
||||
componentScope.launch {
|
||||
val cardInfo = getCardInfoUseCase(params.scanResponse).getOrNull() ?: return@launch
|
||||
sendFeedbackEmailUseCase(FeedbackEmailType.DirectUserRequest(cardInfo))
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ dependencies {
|
|||
implementation(projects.features.pushNotifications.api)
|
||||
implementation(projects.features.markets.api)
|
||||
implementation(projects.features.onramp.api)
|
||||
implementation(projects.features.onboardingV2.api)
|
||||
|
||||
/** Common modules */
|
||||
implementation(projects.common.ui)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.common.routing.AppRouter
|
|||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.feature.wallet.presentation.router.DefaultWalletRouter
|
||||
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
|
||||
import com.tangem.features.wallet.navigation.WalletRouter
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -21,7 +22,8 @@ internal object WalletRouterModule {
|
|||
appRouter: AppRouter,
|
||||
urlOpener: UrlOpener,
|
||||
reduxStateHolder: ReduxStateHolder,
|
||||
onboardingV2FeatureToggles: OnboardingV2FeatureToggles,
|
||||
): WalletRouter {
|
||||
return DefaultWalletRouter(appRouter, urlOpener, reduxStateHolder)
|
||||
return DefaultWalletRouter(appRouter, urlOpener, reduxStateHolder, onboardingV2FeatureToggles)
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ import com.tangem.common.routing.AppRoute
|
|||
import com.tangem.common.routing.AppRoute.ManageTokens.Source
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
|
|
@ -27,6 +28,7 @@ import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensViewM
|
|||
import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletViewModel
|
||||
import com.tangem.features.markets.entry.MarketsEntryComponent
|
||||
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
/** Default implementation of wallet feature router */
|
||||
|
|
@ -34,6 +36,7 @@ internal class DefaultWalletRouter(
|
|||
private val router: AppRouter,
|
||||
private val urlOpener: UrlOpener,
|
||||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val onboardingV2FeatureToggles: OnboardingV2FeatureToggles,
|
||||
) : InnerWalletRouter {
|
||||
|
||||
private var navController: NavHostController by Delegates.notNull()
|
||||
|
|
@ -106,10 +109,24 @@ internal class DefaultWalletRouter(
|
|||
)
|
||||
}
|
||||
|
||||
override fun openOnboardingScreen() {
|
||||
router.push(
|
||||
AppRoute.OnboardingWallet(canSkipBackup = false),
|
||||
)
|
||||
override fun openOnboardingScreen(scanResponse: ScanResponse, continueBackup: Boolean) {
|
||||
if (onboardingV2FeatureToggles.isOnboardingV2Enabled) {
|
||||
router.push(
|
||||
AppRoute.Onboarding(
|
||||
scanResponse = scanResponse,
|
||||
startFromBackup = true,
|
||||
mode = if (continueBackup) {
|
||||
AppRoute.Onboarding.Mode.AddBackup
|
||||
} else {
|
||||
AppRoute.Onboarding.Mode.Onboarding
|
||||
},
|
||||
),
|
||||
)
|
||||
} else {
|
||||
router.push(
|
||||
AppRoute.OnboardingWallet(canSkipBackup = false),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun openOnrampSuccessScreen(txId: String) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.router
|
|||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.features.markets.entry.MarketsEntryComponent
|
||||
|
|
@ -36,7 +37,7 @@ internal interface InnerWalletRouter : WalletRouter {
|
|||
fun openDetailsScreen(selectedWalletId: UserWalletId)
|
||||
|
||||
/** Open onboarding screen */
|
||||
fun openOnboardingScreen()
|
||||
fun openOnboardingScreen(scanResponse: ScanResponse, continueBackup: Boolean = false)
|
||||
|
||||
/** Open onramp success screen for [txId] */
|
||||
fun openOnrampSuccessScreen(txId: String)
|
||||
|
|
|
|||
|
|
@ -104,9 +104,13 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
canSkipBackup = false,
|
||||
),
|
||||
)
|
||||
|
||||
// navigation action shouldn't be out of coroutine to avoid race
|
||||
router.openOnboardingScreen(
|
||||
scanResponse = it.scanResponse,
|
||||
continueBackup = true,
|
||||
)
|
||||
}
|
||||
// navigation action shouldn't be out of coroutine to avoid race
|
||||
router.openOnboardingScreen()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue