Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-16 16:10:57 +04:00
parent 7e02e72a40
commit c2e67ba6bc
41 changed files with 863 additions and 119 deletions

View file

@ -1,5 +1,6 @@
package com.tangem.feature.wallet.child.wallet.model.intents
import com.tangem.common.routing.AppRoute
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.res.R
@ -14,6 +15,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.transformers.TangemPayHideOnboardingStateTransformer
import com.tangem.features.tangempay.TangemPayFeatureToggles
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -29,6 +31,10 @@ internal interface TangemPayIntents {
fun onIssuingFailedClicked()
fun onPaySupportClick()
fun onOnboardingBannerClick(userWalletId: UserWalletId)
fun onOnboardingBannerCloseClick(userWalletId: UserWalletId)
}
@Suppress("LongParameterList")
@ -115,4 +121,15 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
)
}
}
override fun onOnboardingBannerClick(userWalletId: UserWalletId) {
router.openTangemPayOnboarding(mode = AppRoute.TangemPayOnboarding.Mode.FromBannerOnMain(userWalletId))
}
override fun onOnboardingBannerCloseClick(userWalletId: UserWalletId) {
modelScope.launch {
stateHolder.update(transformer = TangemPayHideOnboardingStateTransformer(userWalletId))
onboardingRepository.setHideMainOnboardingBanner(userWalletId)
}
}
}

View file

@ -111,14 +111,8 @@ internal class DefaultWalletRouter @Inject constructor(
)
}
override fun openTangemPayOnboarding(userWalletId: UserWalletId) {
router.push(
AppRoute.TangemPayOnboarding(
AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding(
userWalletId = userWalletId,
),
),
)
override fun openTangemPayOnboarding(mode: AppRoute.TangemPayOnboarding.Mode) {
router.push(route = AppRoute.TangemPayOnboarding(mode = mode))
}
override fun openTangemPayDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig) {

View file

@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.router
import androidx.compose.runtime.Stable
import com.arkivanov.decompose.router.slot.SlotNavigation
import com.tangem.common.routing.AppRoute
import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
@ -63,7 +64,7 @@ internal interface InnerWalletRouter {
fun openTokenReceiveBottomSheet(tokenReceiveConfig: TokenReceiveConfig)
fun openTangemPayOnboarding(userWalletId: UserWalletId)
fun openTangemPayOnboarding(mode: AppRoute.TangemPayOnboarding.Mode)
fun openTangemPayDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig)

View file

@ -11,6 +11,11 @@ internal sealed class TangemPayState {
data object Loading : TangemPayState()
data class OnboardingBanner(
val onClick: () -> Unit,
val closeOnClick: () -> Unit,
) : TangemPayState()
data class Progress(
val title: TextReference,
val description: TextReference,

View file

@ -0,0 +1,18 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
internal class TangemPayHideOnboardingStateTransformer(
userWalletId: UserWalletId,
) : WalletStateTransformer(userWalletId) {
override fun transform(prevState: WalletState): WalletState {
return if (prevState is WalletState.MultiCurrency.Content) {
prevState.copy(tangemPayState = TangemPayState.Empty)
} else {
prevState
}
}
}

View file

@ -0,0 +1,25 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
internal class TangemPayOnboardingBannerStateTransformer(
userWalletId: UserWalletId,
private val onClick: (UserWalletId) -> Unit,
private val closeOnClick: (UserWalletId) -> Unit,
) : WalletStateTransformer(userWalletId) {
override fun transform(prevState: WalletState): WalletState {
return if (prevState is WalletState.MultiCurrency.Content) {
prevState.copy(
tangemPayState = TangemPayState.OnboardingBanner(
onClick = { onClick(userWalletId) },
closeOnClick = { closeOnClick(userWalletId) },
),
)
} else {
prevState
}
}
}

View file

@ -1,5 +1,6 @@
package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.common.routing.AppRoute
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.model.MainCustomerInfoContentState
@ -79,6 +80,13 @@ internal class TangemPayMainSubscriber @AssistedInject constructor(
updateTangemPay(data = state.info, userWalletId = userWalletId)
analytics.send(customerInfo = state.info)
}
is MainCustomerInfoContentState.OnboardingBanner -> stateController.update(
transformer = TangemPayOnboardingBannerStateTransformer(
userWalletId = userWalletId,
onClick = clickIntents::onOnboardingBannerClick,
closeOnClick = clickIntents::onOnboardingBannerCloseClick,
),
)
}
}
@ -91,7 +99,11 @@ internal class TangemPayMainSubscriber @AssistedInject constructor(
userWalletId = userWalletId,
value = data,
cardFrozenState = cardFrozenState,
onClickKyc = { innerWalletRouter.openTangemPayOnboarding(userWalletId) },
onClickKyc = {
innerWalletRouter.openTangemPayOnboarding(
mode = AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding(userWalletId),
)
},
onIssuingCard = clickIntents::onIssuingCardClicked,
onIssuingFailed = clickIntents::onIssuingFailedClicked,
openDetails = { config ->

View file

@ -23,6 +23,7 @@ internal fun TangemPayMainScreenBlock(state: TangemPayState, isBalanceHidden: Bo
is TangemPayState.RefreshNeeded -> TangemPayRefreshBlock(state, modifier)
is TangemPayState.TemporaryUnavailable -> TangemPayUnavailableBlock(state, modifier)
is TangemPayState.FailedIssue -> TangemPayFailedIssueState(state, modifier)
is TangemPayState.OnboardingBanner -> TangemPayOnboardingBanner(state, modifier)
TangemPayState.Loading -> TangemPayLoadingScreenBlock(modifier)
}
}

View file

@ -0,0 +1,129 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.visa
import android.content.res.Configuration
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.SpacerH4
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
private const val GRADIENT_START_COLOR = 0xFF252934
private const val GRADIENT_END_COLOR = 0xFF12141E
private const val GRADIENT_OFFSET_X = 0f
private const val GRADIENT_OFFSET_Y = 80F
private const val GRADIENT_RADIUS = 200F
@Composable
internal fun TangemPayOnboardingBanner(state: TangemPayState.OnboardingBanner, modifier: Modifier = Modifier) {
Box(
modifier = modifier
.fillMaxWidth()
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(
brush = Brush.radialGradient(
colors = listOf(Color(GRADIENT_START_COLOR), Color(GRADIENT_END_COLOR)),
center = Offset(GRADIENT_OFFSET_X, GRADIENT_OFFSET_Y),
radius = GRADIENT_RADIUS,
),
)
.clickable(onClick = state.onClick),
) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min)
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Image(
painter = painterResource(R.drawable.img_tangem_pay_onboarding_banner),
contentDescription = null,
modifier = Modifier
.padding(horizontal = 8.dp)
.width(55.dp)
.height(95.dp)
.offset(y = 10.dp),
)
SpacerH4()
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.Center,
) {
Text(
text = stringResourceSafe(R.string.tangempay_onboarding_banner_title),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.constantWhite,
)
SpacerH(6.dp)
Text(
text = stringResourceSafe(R.string.tangempay_onboarding_banner_description),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
)
}
Box(
modifier = Modifier.fillMaxHeight(),
contentAlignment = Alignment.TopEnd,
) {
Image(
modifier = Modifier
.clip(TangemTheme.shapes.roundedCornersXMedium)
.clickable(onClick = state.closeOnClick)
.padding(4.dp)
.padding(top = 12.dp)
.size(12.dp),
painter = painterResource(id = R.drawable.ic_close_24),
colorFilter = ColorFilter.tint(TangemTheme.colors.icon.inactive),
contentDescription = null,
)
}
}
}
}
@Preview(showBackground = true)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun PreviewTangemOnboardingBanner() {
TangemThemePreview {
TangemPayOnboardingBanner(
TangemPayState.OnboardingBanner(
onClick = {},
closeOnClick = {},
),
)
}
}