Updated on 2026-08-14
This commit is contained in:
parent
9ee6611585
commit
ba28cf5b6d
29 changed files with 761 additions and 41 deletions
|
|
@ -23,6 +23,7 @@ import com.tangem.feature.wallet.child.wallet.model.WalletModel
|
|||
import com.tangem.feature.wallet.navigation.WalletRoute
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.KycRejectedComponent
|
||||
import com.tangem.feature.walletsettings.component.RenameWalletComponent
|
||||
import com.tangem.features.biometry.AskBiometryComponent
|
||||
import com.tangem.features.feed.entry.components.FeedEntryComponent
|
||||
|
|
@ -126,6 +127,17 @@ internal class WalletComponent @AssistedInject constructor(
|
|||
),
|
||||
)
|
||||
}
|
||||
is WalletDialogConfig.KycRejected -> {
|
||||
KycRejectedComponent(
|
||||
appComponentContext = childByContext(componentContext),
|
||||
params = KycRejectedComponent.Params(
|
||||
walletId = dialogConfig.walletId,
|
||||
customerId = dialogConfig.customerId,
|
||||
callbacks = model.tangemPayKycRejectedCallbacks,
|
||||
onDismiss = model.innerWalletRouter.dialogNavigation::dismiss,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent.Dem
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.KycRejectedCallbacks
|
||||
import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider
|
||||
import com.tangem.features.biometry.AskBiometryComponent
|
||||
import com.tangem.features.feed.entry.featuretoggle.FeedFeatureToggle
|
||||
|
|
@ -107,6 +108,7 @@ internal class WalletModel @Inject constructor(
|
|||
) : Model() {
|
||||
|
||||
val askBiometryModelCallbacks = AskBiometryModelCallbacks()
|
||||
val tangemPayKycRejectedCallbacks = TangemPayKycRejectedCallbacks()
|
||||
val askForPushNotificationsModelCallbacks = AskForPushNotificationsCallbacks()
|
||||
val uiState: StateFlow<WalletScreenState> = stateHolder.uiState
|
||||
|
||||
|
|
@ -769,6 +771,20 @@ internal class WalletModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
inner class TangemPayKycRejectedCallbacks : KycRejectedCallbacks {
|
||||
override fun onClickYourProfile(userWalletId: UserWalletId) {
|
||||
clickIntents.onKycRejectedOpenProfileClicked(userWalletId)
|
||||
}
|
||||
|
||||
override fun onClickGoToSupport(customerId: String) {
|
||||
clickIntents.onKycRejectedGoToSupportClicked(customerId)
|
||||
}
|
||||
|
||||
override fun onClickHideKyc(userWalletId: UserWalletId) {
|
||||
clickIntents.onKycRejectedHideKycClicked(userWalletId)
|
||||
}
|
||||
}
|
||||
|
||||
inner class AskForPushNotificationsCallbacks : PushNotificationsModelCallbacks {
|
||||
|
||||
override fun onAllowSystemPermission() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.wallet.child.wallet.model.intents
|
||||
|
||||
import com.arkivanov.decompose.router.slot.activate
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
|
|
@ -13,6 +14,7 @@ import com.tangem.core.ui.message.bottomSheetMessage
|
|||
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
|
||||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
import com.tangem.domain.feedback.models.WalletMetaInfo
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.TangemPayDetailsConfig
|
||||
|
|
@ -21,6 +23,7 @@ import com.tangem.domain.pay.repository.OnboardingRepository
|
|||
import com.tangem.domain.pay.usecase.ProduceTangemPayInitialDataUseCase
|
||||
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.TangemPayHideOnboardingStateTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.TangemPayRefreshNeededStateTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.TangemPayRefreshShowProgressTransformer
|
||||
|
|
@ -38,6 +41,14 @@ internal interface TangemPayIntents {
|
|||
|
||||
fun onKycProgressClicked(userWalletId: UserWalletId)
|
||||
|
||||
fun onKycRejectedClicked(userWalletId: UserWalletId, customerId: String)
|
||||
|
||||
fun onKycRejectedOpenProfileClicked(userWalletId: UserWalletId)
|
||||
|
||||
fun onKycRejectedGoToSupportClicked(customerId: String)
|
||||
|
||||
fun onKycRejectedHideKycClicked(userWalletId: UserWalletId)
|
||||
|
||||
fun onIssuingCardClicked()
|
||||
|
||||
fun onIssuingFailedClicked()
|
||||
|
|
@ -137,6 +148,42 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
|
|||
uiMessageSender.send(kycInfoBottomSheet)
|
||||
}
|
||||
|
||||
override fun onKycRejectedOpenProfileClicked(userWalletId: UserWalletId) {
|
||||
router.openTangemPayOnboarding(
|
||||
mode = AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding(userWalletId),
|
||||
)
|
||||
}
|
||||
|
||||
override fun onKycRejectedGoToSupportClicked(customerId: String) {
|
||||
goToSupportForRejectKyc(customerId)
|
||||
}
|
||||
|
||||
override fun onKycRejectedHideKycClicked(userWalletId: UserWalletId) {
|
||||
disableTangemPay(userWalletId)
|
||||
}
|
||||
|
||||
override fun onKycRejectedClicked(userWalletId: UserWalletId, customerId: String) {
|
||||
router.dialogNavigation.activate(
|
||||
configuration = WalletDialogConfig.KycRejected(
|
||||
walletId = userWalletId,
|
||||
customerId = customerId,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun goToSupportForRejectKyc(customerId: String) {
|
||||
modelScope.launch {
|
||||
sendFeedbackEmailUseCase(
|
||||
type = FeedbackEmailType.Visa.KycRejected(
|
||||
walletMetaInfo = WalletMetaInfo(
|
||||
userWalletId = stateHolder.getSelectedWalletId(),
|
||||
),
|
||||
customerId = customerId,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onIssuingCardClicked() {
|
||||
val issuingBottomSheet = bottomSheetMessage {
|
||||
infoBlock {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.core.decompose.model.Model
|
|||
import com.tangem.feature.wallet.DefaultWalletEntryComponent
|
||||
import com.tangem.feature.wallet.child.organizetokens.model.OrganizeTokensModel
|
||||
import com.tangem.feature.wallet.child.wallet.model.WalletModel
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.KycRejectedModel
|
||||
import com.tangem.feature.wallet.utils.DefaultUserWalletImageFetcher
|
||||
import com.tangem.feature.wallet.utils.DefaultUserWalletsFetcher
|
||||
import com.tangem.features.wallet.WalletEntryComponent
|
||||
|
|
@ -36,6 +37,11 @@ internal interface WalletFeatureModule {
|
|||
@ClassKey(WalletModel::class)
|
||||
fun bindWalletModel(model: WalletModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(KycRejectedModel::class)
|
||||
fun bindKycRejectedModel(model: KycRejectedModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(OrganizeTokensModel::class)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.analytics.utils
|
|||
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.pay.model.CustomerInfo
|
||||
import com.tangem.domain.pay.model.MainScreenCustomerInfo
|
||||
import com.tangem.domain.pay.model.OrderStatus
|
||||
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
|
||||
|
|
@ -25,8 +26,10 @@ internal class WalletTangemPayAnalyticsEventSender @Inject constructor(
|
|||
// TODO: TangemPay refactor analytics
|
||||
// when statement copied from TangemPayUpdateInfoStateTransformer. Be careful when editing
|
||||
val event = when {
|
||||
customerInfo.orderStatus == OrderStatus.CANCELED -> return // ignore cancelled state on analytics
|
||||
!customerInfo.info.isKycApproved -> return // ignore kyc not approved state on analytics
|
||||
// ignore cancelled state on analytics
|
||||
customerInfo.orderStatus == OrderStatus.CANCELED -> return
|
||||
// ignore kyc not approved state on analytics
|
||||
customerInfo.info.kycStatus != CustomerInfo.KycStatus.APPROVED -> return
|
||||
cardInfo != null && productInstance != null -> return
|
||||
else -> TangemPayAnalyticsEvents.IssuingBannerDisplayed()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,4 +32,7 @@ internal sealed interface WalletDialogConfig {
|
|||
val tokenAction: TokenAction,
|
||||
val onWarningAcknowledged: (TokenAction) -> Unit,
|
||||
) : WalletDialogConfig
|
||||
|
||||
@Serializable
|
||||
data class KycRejected(val walletId: UserWalletId, val customerId: String) : WalletDialogConfig
|
||||
}
|
||||
|
|
@ -16,6 +16,8 @@ import com.tangem.feature.wallet.child.wallet.model.intents.TangemPayIntents
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState.Progress
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.domain.pay.model.CustomerInfo.KycStatus.APPROVED
|
||||
import com.tangem.domain.pay.model.CustomerInfo
|
||||
import java.util.Currency
|
||||
|
||||
/**
|
||||
|
|
@ -43,11 +45,13 @@ internal class TangemPayUpdateInfoStateTransformer(
|
|||
private fun createInitialState(): TangemPayState {
|
||||
val cardInfo = value.info.cardInfo
|
||||
val productInstance = value.info.productInstance
|
||||
val customerId = value.info.customerId
|
||||
|
||||
// when statement copied to WalletTangemPayAnalyticsEventSender. Be careful when editing.
|
||||
return when {
|
||||
value.orderStatus == OrderStatus.CANCELED -> createCancelledState()
|
||||
!value.info.isKycApproved -> createKycInProgressState()
|
||||
value.info.kycStatus != APPROVED && !customerId.isNullOrEmpty() ->
|
||||
createKycInProgressState(kycStatus = value.info.kycStatus, customerId = customerId)
|
||||
cardInfo != null && productInstance != null -> getCardInfoState(cardInfo, productInstance)
|
||||
else -> createIssueProgressState()
|
||||
}
|
||||
|
|
@ -80,13 +84,25 @@ internal class TangemPayUpdateInfoStateTransformer(
|
|||
}
|
||||
}
|
||||
|
||||
private fun createKycInProgressState(): TangemPayState = Progress(
|
||||
title = TextReference.Res(R.string.tangempay_payment_account),
|
||||
description = TextReference.Res(R.string.tangempay_kyc_in_progress),
|
||||
buttonText = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_button),
|
||||
iconRes = R.drawable.ic_promo_kyc_36,
|
||||
onButtonClick = { tangemPayClickIntents.onKycProgressClicked(userWalletId) },
|
||||
)
|
||||
private fun createKycInProgressState(kycStatus: CustomerInfo.KycStatus, customerId: String): TangemPayState =
|
||||
Progress(
|
||||
title = TextReference.Res(R.string.tangempay_payment_account),
|
||||
description = when (kycStatus) {
|
||||
CustomerInfo.KycStatus.REJECTED -> TextReference.Res(R.string.tangempay_kyc_has_failed)
|
||||
else -> TextReference.Res(R.string.tangempay_kyc_in_progress)
|
||||
},
|
||||
buttonText = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_button),
|
||||
iconRes = R.drawable.ic_promo_kyc_36,
|
||||
onButtonClick = {
|
||||
when (kycStatus) {
|
||||
CustomerInfo.KycStatus.REJECTED -> tangemPayClickIntents.onKycRejectedClicked(
|
||||
userWalletId = userWalletId,
|
||||
customerId = customerId,
|
||||
)
|
||||
else -> tangemPayClickIntents.onKycProgressClicked(userWalletId)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
private fun createIssueProgressState(): TangemPayState = Progress(
|
||||
title = TextReference.Res(R.string.tangempay_payment_account),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components.visa
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetV2
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
internal class KycRejectedComponent @AssistedInject constructor(
|
||||
@Assisted appComponentContext: AppComponentContext,
|
||||
@Assisted params: Params,
|
||||
) : ComposableBottomSheetComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: KycRejectedModel = getOrCreateModel(params = params)
|
||||
|
||||
override fun dismiss() {
|
||||
model.onDismiss()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun BottomSheet() {
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
MessageBottomSheetV2(state = state, onDismissRequest = ::dismiss)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val callbacks: KycRejectedCallbacks,
|
||||
val walletId: UserWalletId,
|
||||
val customerId: String,
|
||||
val onDismiss: () -> Unit,
|
||||
)
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(appComponentContext: AppComponentContext, params: Params): KycRejectedComponent
|
||||
}
|
||||
}
|
||||
|
||||
internal interface KycRejectedCallbacks {
|
||||
fun onClickYourProfile(userWalletId: UserWalletId)
|
||||
fun onClickGoToSupport(customerId: String)
|
||||
fun onClickHideKyc(userWalletId: UserWalletId)
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components.visa
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.res.R
|
||||
import com.tangem.core.ui.components.bottomsheets.message.*
|
||||
import com.tangem.core.ui.extensions.combinedReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.styledResourceReference
|
||||
import com.tangem.core.ui.message.bottomSheetMessage
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@Stable
|
||||
@ModelScoped
|
||||
internal class KycRejectedModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<KycRejectedComponent.Params>()
|
||||
|
||||
val uiState: StateFlow<MessageBottomSheetUMV2>
|
||||
field = MutableStateFlow(getInitialState())
|
||||
|
||||
private fun getInitialState(): MessageBottomSheetUMV2 {
|
||||
return bottomSheetMessage {
|
||||
infoBlock {
|
||||
icon(com.tangem.core.ui.R.drawable.ic_heart_broken_32) {
|
||||
type = MessageBottomSheetUMV2.Icon.Type.Warning
|
||||
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.Warning
|
||||
}
|
||||
title = resourceReference(R.string.tangempay_kyc_rejected)
|
||||
body = combinedReference(
|
||||
resourceReference(R.string.tangempay_kyc_rejected_description),
|
||||
stringReference(" "),
|
||||
styledResourceReference(
|
||||
id = R.string.tangempay_kyc_rejected_description_span,
|
||||
spanStyleReference = {
|
||||
TangemTheme.typography.body2.copy(TangemTheme.colors.text.accent).toSpanStyle()
|
||||
.copy(textDecoration = TextDecoration.None)
|
||||
},
|
||||
onClick = {
|
||||
params.callbacks.onClickYourProfile(userWalletId = params.walletId)
|
||||
onDismiss()
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
primaryButton {
|
||||
text = resourceReference(R.string.tangempay_go_to_support)
|
||||
onClick = {
|
||||
params.callbacks.onClickGoToSupport(customerId = params.customerId)
|
||||
onDismiss()
|
||||
}
|
||||
}
|
||||
secondaryButton {
|
||||
text = resourceReference(R.string.tangempay_kyc_rejected_button_text)
|
||||
onClick = {
|
||||
params.callbacks.onClickHideKyc(userWalletId = params.walletId)
|
||||
onDismiss()
|
||||
}
|
||||
}
|
||||
}.messageBottomSheetUMV2
|
||||
}
|
||||
|
||||
fun onDismiss() {
|
||||
params.onDismiss()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue