Updated on 2026-08-14
This commit is contained in:
parent
11c0834815
commit
d27c57cd84
20 changed files with 279 additions and 93 deletions
|
|
@ -151,6 +151,9 @@ internal class DefaultOnboardingVisaComponent @AssistedInject constructor(
|
|||
)
|
||||
OnboardingVisaRoute.InProgress -> OnboardingVisaInProgressComponent(
|
||||
appComponentContext = factoryContext,
|
||||
config = OnboardingVisaInProgressComponent.Config(
|
||||
scanResponse = params.scanResponse,
|
||||
),
|
||||
params = OnboardingVisaInProgressComponent.Params(
|
||||
childParams = childParams,
|
||||
onDone = { stackNavigation.push(OnboardingVisaRoute.PinCode) },
|
||||
|
|
@ -159,15 +162,19 @@ internal class DefaultOnboardingVisaComponent @AssistedInject constructor(
|
|||
is OnboardingVisaRoute.OtherWalletApproveOption -> OnboardingVisaOtherWalletComponent(
|
||||
appComponentContext = factoryContext,
|
||||
config = OnboardingVisaOtherWalletComponent.Config(
|
||||
scanResponse = params.scanResponse,
|
||||
visaDataForApprove = route.visaDataForApprove,
|
||||
),
|
||||
params = OnboardingVisaOtherWalletComponent.Params(
|
||||
childParams = childParams,
|
||||
onDone = { stackNavigation.push(OnboardingVisaRoute.InProgress) },
|
||||
onDone = { stackNavigation.push(OnboardingVisaRoute.PinCode) },
|
||||
),
|
||||
)
|
||||
OnboardingVisaRoute.PinCode -> OnboardingVisaPinCodeComponent(
|
||||
appComponentContext = factoryContext,
|
||||
config = OnboardingVisaPinCodeComponent.Config(
|
||||
scanResponse = params.scanResponse,
|
||||
),
|
||||
params = OnboardingVisaPinCodeComponent.Params(
|
||||
childParams = childParams,
|
||||
onDone = { params.onDone() },
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
|||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.tan
|
||||
|
||||
@Stable
|
||||
@ComponentScoped
|
||||
|
|
@ -39,6 +40,8 @@ internal class OnboardingVisaAccessCodeModel @Inject constructor(
|
|||
val onDone = MutableSharedFlow<OnboardingVisaAccessCodeComponent.DoneEvent>()
|
||||
|
||||
fun onBack() {
|
||||
if (uiState.value.buttonLoading) return
|
||||
|
||||
when (uiState.value.step) {
|
||||
OnboardingVisaAccessCodeUM.Step.Enter -> modelScope.launch { onBack.emit(Unit) }
|
||||
OnboardingVisaAccessCodeUM.Step.ReEnter ->
|
||||
|
|
@ -141,7 +144,7 @@ internal class OnboardingVisaAccessCodeModel @Inject constructor(
|
|||
OnboardingVisaAccessCodeComponent.DoneEvent(
|
||||
visaDataForApprove = VisaDataForApprove(
|
||||
targetAddress = "x9F65354e595284956599F2892fA4A4a87653D6E6",
|
||||
approveHash = "approve hash",
|
||||
approveHash = "48b55c482123a10ad9022f9f4c5dd95c",
|
||||
),
|
||||
walletFound = false, // TODO
|
||||
),
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import androidx.compose.ui.Modifier
|
|||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.features.onboarding.v2.visa.impl.DefaultOnboardingVisaComponent
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.inprogress.model.OnboardingVisaInProgressModel
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.inprogress.ui.OnboardingVisaInProgress
|
||||
|
|
@ -15,10 +16,11 @@ import kotlinx.coroutines.flow.onEach
|
|||
|
||||
internal class OnboardingVisaInProgressComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
config: Config,
|
||||
private val params: Params,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: OnboardingVisaInProgressModel = getOrCreateModel()
|
||||
private val model: OnboardingVisaInProgressModel = getOrCreateModel(config)
|
||||
|
||||
init {
|
||||
model.onDone
|
||||
|
|
@ -35,6 +37,10 @@ internal class OnboardingVisaInProgressComponent(
|
|||
)
|
||||
}
|
||||
|
||||
data class Config(
|
||||
val scanResponse: ScanResponse,
|
||||
)
|
||||
|
||||
data class Params(
|
||||
val childParams: DefaultOnboardingVisaComponent.ChildParams,
|
||||
val onDone: () -> Unit,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@ package com.tangem.features.onboarding.v2.visa.impl.child.inprogress.model
|
|||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.domain.visa.model.VisaActivationRemoteState
|
||||
import com.tangem.domain.visa.repository.VisaActivationRepository
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.inprogress.OnboardingVisaInProgressComponent
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
|
|
@ -12,16 +16,29 @@ import javax.inject.Inject
|
|||
@Stable
|
||||
@ComponentScoped
|
||||
internal class OnboardingVisaInProgressModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val visaActivationRepositoryFactory: VisaActivationRepository.Factory,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<OnboardingVisaInProgressComponent.Config>()
|
||||
private val visaActivationRepository = visaActivationRepositoryFactory.create(params.scanResponse.card.cardId)
|
||||
val onDone = MutableSharedFlow<Unit>()
|
||||
|
||||
init {
|
||||
modelScope.launch {
|
||||
// TODO check state
|
||||
delay(timeMillis = 2000)
|
||||
onDone.emit(Unit)
|
||||
while (true) {
|
||||
val result = runCatching {
|
||||
visaActivationRepository.getActivationRemoteStateLongPoll()
|
||||
}.getOrNull()
|
||||
|
||||
if (result == VisaActivationRemoteState.WaitingPinCode) {
|
||||
onDone.emit(Unit)
|
||||
break
|
||||
}
|
||||
|
||||
delay(timeMillis = 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.visa.model.VisaDataForApprove
|
||||
import com.tangem.features.onboarding.v2.visa.impl.DefaultOnboardingVisaComponent
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.otherwallet.model.OnboardingVisaOtherWalletModel
|
||||
|
|
@ -43,6 +44,7 @@ internal class OnboardingVisaOtherWalletComponent(
|
|||
}
|
||||
|
||||
data class Config(
|
||||
val scanResponse: ScanResponse,
|
||||
val visaDataForApprove: VisaDataForApprove,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,12 +4,18 @@ import androidx.compose.runtime.Stable
|
|||
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.navigation.share.ShareManager
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.domain.visa.model.VisaActivationRemoteState
|
||||
import com.tangem.domain.visa.repository.VisaActivationRepository
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.otherwallet.OnboardingVisaOtherWalletComponent
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.otherwallet.ui.state.OnboardingVisaOtherWalletUM
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@Stable
|
||||
|
|
@ -17,15 +23,36 @@ import javax.inject.Inject
|
|||
internal class OnboardingVisaOtherWalletModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val urlOpener: UrlOpener,
|
||||
private val shareManager: ShareManager,
|
||||
private val visaActivationRepositoryFactory: VisaActivationRepository.Factory,
|
||||
) : Model() {
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
private val config = paramsContainer.require<OnboardingVisaOtherWalletComponent.Config>()
|
||||
private val visaActivationRepository = visaActivationRepositoryFactory.create(config.scanResponse.card.cardId)
|
||||
private val _uiState = MutableStateFlow(getInitialState())
|
||||
|
||||
val uiState = _uiState.asStateFlow()
|
||||
val onDone = MutableSharedFlow<Unit>()
|
||||
|
||||
init {
|
||||
modelScope.launch {
|
||||
while (true) {
|
||||
val result = runCatching {
|
||||
visaActivationRepository.getActivationRemoteStateLongPoll()
|
||||
}.getOrNull()
|
||||
|
||||
if (result == VisaActivationRemoteState.WaitingPinCode) {
|
||||
onDone.emit(Unit)
|
||||
break
|
||||
}
|
||||
|
||||
delay(timeMillis = 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getInitialState(): OnboardingVisaOtherWalletUM {
|
||||
return OnboardingVisaOtherWalletUM(
|
||||
onShareClick = ::onShareClicked,
|
||||
|
|
@ -34,10 +61,10 @@ internal class OnboardingVisaOtherWalletModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun onShareClicked() {
|
||||
// TODO
|
||||
shareManager.shareText("https://tangem.com/${config.visaDataForApprove.approveHash}")
|
||||
}
|
||||
|
||||
private fun onOpenInBrowserClicked() {
|
||||
// TODO
|
||||
urlOpener.openUrl("https://tangem.com/${config.visaDataForApprove.approveHash}")
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ import com.tangem.core.decompose.context.AppComponentContext
|
|||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.core.ui.security.DisableScreenshotsDisposableEffect
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.features.onboarding.v2.visa.impl.DefaultOnboardingVisaComponent
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.pincode.model.OnboardingVisaPinCodeModel
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.pincode.ui.OnboardingVisaPinCode
|
||||
|
|
@ -17,10 +18,11 @@ import kotlinx.coroutines.launch
|
|||
|
||||
internal class OnboardingVisaPinCodeComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
config: Config,
|
||||
private val params: Params,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: OnboardingVisaPinCodeModel = getOrCreateModel()
|
||||
private val model: OnboardingVisaPinCodeModel = getOrCreateModel(config)
|
||||
|
||||
init {
|
||||
componentScope.launch {
|
||||
|
|
@ -42,6 +44,10 @@ internal class OnboardingVisaPinCodeComponent(
|
|||
)
|
||||
}
|
||||
|
||||
data class Config(
|
||||
val scanResponse: ScanResponse,
|
||||
)
|
||||
|
||||
data class Params(
|
||||
val childParams: DefaultOnboardingVisaComponent.ChildParams,
|
||||
val onDone: () -> Unit,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,15 @@ package com.tangem.features.onboarding.v2.visa.impl.child.pincode.model
|
|||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.datasource.local.visa.VisaAuthTokenStorage
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.visa.model.VisaCardActivationStatus
|
||||
import com.tangem.domain.wallets.builder.UserWalletBuilder
|
||||
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.visa.impl.child.pincode.OnboardingVisaPinCodeComponent
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.pincode.ui.state.OnboardingVisaPinCodeUM
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
|
|
@ -10,14 +19,21 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
|
||||
@Stable
|
||||
@ComponentScoped
|
||||
internal class OnboardingVisaPinCodeModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val generateWalletNameUseCase: GenerateWalletNameUseCase,
|
||||
private val userWalletsListManager: UserWalletsListManager,
|
||||
private val authTokenStorage: VisaAuthTokenStorage,
|
||||
private val otpStorage: VisaAuthTokenStorage,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<OnboardingVisaPinCodeComponent.Config>()
|
||||
private val _uiState = MutableStateFlow(getInitialState())
|
||||
|
||||
val uiState = _uiState.asStateFlow()
|
||||
|
|
@ -31,21 +47,61 @@ internal class OnboardingVisaPinCodeModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun onPinCodeChange(pin: String) {
|
||||
if (pin.all { it.isDigit() }) _uiState.update { it.copy(pinCode = pin) }
|
||||
if (pin.all { it.isDigit() }) {
|
||||
_uiState.update {
|
||||
it.copy(
|
||||
pinCode = pin,
|
||||
submitButtonEnabled = checkPinCode(pin),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onSubmitClick() {
|
||||
if (checkPinCode(_uiState.value.pinCode).not()) return
|
||||
|
||||
// TODO
|
||||
modelScope.launch {
|
||||
loading(true)
|
||||
|
||||
modelScope.launch { onDone.emit(Unit) }
|
||||
// TODO
|
||||
// make backend call
|
||||
saveWallet()
|
||||
|
||||
loading(false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkPinCode(pin: String): Boolean {
|
||||
return pin.length == PIN_CODE_LENGTH
|
||||
}
|
||||
|
||||
private fun loading(state: Boolean) {
|
||||
_uiState.update { it.copy(submitButtonLoading = state) }
|
||||
}
|
||||
|
||||
private suspend fun saveWallet() {
|
||||
val userWallet = createUserWallet(params.scanResponse)
|
||||
userWalletsListManager.save(userWallet)
|
||||
authTokenStorage.remove(params.scanResponse.card.cardId)
|
||||
otpStorage.remove(params.scanResponse.card.cardId)
|
||||
onDone.emit(Unit)
|
||||
}
|
||||
|
||||
private suspend fun createUserWallet(scanResponse: ScanResponse): UserWallet = withContext(dispatchers.io) {
|
||||
val newActivationStatus = VisaCardActivationStatus.Activated(
|
||||
visaAuthTokens = authTokenStorage.get(scanResponse.card.cardId)
|
||||
?: error("Impossible state. Wrong feature implementation"),
|
||||
)
|
||||
|
||||
requireNotNull(
|
||||
value = UserWalletBuilder(
|
||||
scanResponse.copy(visaCardActivationStatus = newActivationStatus),
|
||||
generateWalletNameUseCase,
|
||||
).build(),
|
||||
lazyMessage = { "User wallet not created" },
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val PIN_CODE_LENGTH = 4
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,9 +28,11 @@ import androidx.compose.ui.text.style.TextAlign
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.common.ui.navigationButtons.NavigationButton
|
||||
import com.tangem.common.ui.navigationButtons.NavigationPrimaryButton
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.SpacerH16
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.onboarding.v2.visa.impl.child.pincode.ui.state.OnboardingVisaPinCodeUM
|
||||
|
|
@ -72,13 +74,17 @@ internal fun OnboardingVisaPinCode(state: OnboardingVisaPinCodeUM, modifier: Mod
|
|||
PinCodeSection(state)
|
||||
}
|
||||
|
||||
PrimaryButton(
|
||||
NavigationPrimaryButton(
|
||||
modifier = Modifier
|
||||
.imePadding()
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
text = "Submit",
|
||||
onClick = state.onSubmitClick,
|
||||
primaryButton = NavigationButton(
|
||||
textReference = TextReference.Str("Submit"),
|
||||
onClick = state.onSubmitClick,
|
||||
showProgress = state.submitButtonLoading,
|
||||
isEnabled = state.submitButtonEnabled,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,5 +3,7 @@ package com.tangem.features.onboarding.v2.visa.impl.child.pincode.ui.state
|
|||
internal data class OnboardingVisaPinCodeUM(
|
||||
val pinCode: String = "",
|
||||
val onPinCodeChange: (String) -> Unit = {},
|
||||
val submitButtonLoading: Boolean = false,
|
||||
val submitButtonEnabled: Boolean = true,
|
||||
val onSubmitClick: () -> Unit = {},
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue