Updated on 2026-08-14

This commit is contained in:
Tangem 2025-03-21 15:19:00 +05:00
parent c40326c1c9
commit 894b3fdc71
6 changed files with 112 additions and 30 deletions

View file

@ -1,6 +1,8 @@
package com.tangem.features.onboarding.v2.common.analytics
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.domain.tokens.model.CryptoCurrency
sealed class OnboardingEvent(
category: String,
@ -46,6 +48,11 @@ sealed class OnboardingEvent(
object ScreenOpened : Topup("Activation Screen Opened")
object ButtonShowWalletAddress : Topup("Button - Show the Wallet Address")
class ButtonBuyCrypto(currency: CryptoCurrency) : Topup(
event = "Button - Buy Crypto",
params = mapOf(AnalyticsParam.CURRENCY to currency.symbol),
)
}
sealed class Backup(

View file

@ -4,20 +4,30 @@ import com.tangem.core.analytics.Analytics
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.navigation.share.ShareManager
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.clipboard.ClipboardManager
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.tokenreceive.mapToAddressModels
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.onramp.GetLegacyTopUpUrlUseCase
import com.tangem.domain.tokens.FetchCurrencyStatusUseCase
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.model.NetworkAddress
import com.tangem.domain.tokens.model.analytics.TokenReceiveAnalyticsEvent
import com.tangem.domain.wallets.builder.UserWalletBuilder
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.features.onboarding.v2.common.analytics.OnboardingEvent
import com.tangem.features.onboarding.v2.note.impl.child.topup.OnboardingNoteTopUpComponent
import com.tangem.features.onboarding.v2.note.impl.child.topup.ui.state.OnboardingNoteTopUpUM
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -32,6 +42,9 @@ internal class OnboardingNoteTopUpModel @Inject constructor(
private val userWalletBuilderFactory: UserWalletBuilder.Factory,
private val getLegacyTopUpUrlUseCase: GetLegacyTopUpUrlUseCase,
private val urlOpener: UrlOpener,
private val clipboardManager: ClipboardManager,
private val shareManager: ShareManager,
private val rampStateManager: RampStateManager,
) : Model() {
private val params = paramsContainer.require<OnboardingNoteTopUpComponent.Params>()
@ -44,6 +57,7 @@ internal class OnboardingNoteTopUpModel @Inject constructor(
onRefreshBalanceClick = ::refreshBalance,
onBuyCryptoClick = ::onBuyCryptoClick,
onShowWalletAddressClick = ::onShowWalletAddressClick,
onDismissBottomSheet = ::onDismissBottomSheet,
),
)
@ -79,10 +93,23 @@ internal class OnboardingNoteTopUpModel @Inject constructor(
urlOpener.openUrl(it)
}
}
Analytics.send(OnboardingEvent.Topup.ButtonBuyCrypto(cryptoCurrencyStatus.currency))
}
private fun onShowWalletAddressClick() {
// TODO [REDACTED_TASK_KEY]
val currencyStatus = params.childParams.commonState.value.cryptoCurrencyStatus ?: return
val networkAddress = currencyStatus.value.networkAddress ?: return
_uiState.update {
it.copy(addressBottomSheetConfig = createReceiveBS(currencyStatus, networkAddress))
}
Analytics.send(OnboardingEvent.Topup.ButtonShowWalletAddress)
}
private fun onDismissBottomSheet() {
_uiState.update {
it.copy(addressBottomSheetConfig = null)
}
}
private suspend fun createUserWalletIfNull() {
@ -110,9 +137,13 @@ internal class OnboardingNoteTopUpModel @Inject constructor(
}
private fun applyCryptoCurrencyStatusToState(status: CryptoCurrencyStatus) {
if (commonState.value.cryptoCurrencyStatus == null) {
loadAvailableForBuy(status)
}
commonState.update {
it.copy(cryptoCurrencyStatus = status)
}
_uiState.update {
it.copy(
amountToCreateAccount = (status.value as? CryptoCurrencyStatus.NoAccount)
@ -132,7 +163,7 @@ internal class OnboardingNoteTopUpModel @Inject constructor(
decimals = status.currency.decimals,
)
},
),
).orEmpty(),
isTopUpDataLoading = status.value.networkAddress == null,
)
}
@ -144,6 +175,48 @@ internal class OnboardingNoteTopUpModel @Inject constructor(
}
}
private fun loadAvailableForBuy(cryptoCurrencyStatus: CryptoCurrencyStatus) {
val userWalletId = userWallet?.walletId ?: return
modelScope.launch {
val availableForBuy = rampStateManager.availableForBuy(
scanResponse = scanResponse,
userWalletId = userWalletId,
cryptoCurrency = cryptoCurrencyStatus.currency,
)
_uiState.update {
it.copy(
availableForBuy = availableForBuy,
availableForBuyLoading = false,
)
}
}
}
private fun createReceiveBS(currencyStatus: CryptoCurrencyStatus, networkAddress: NetworkAddress) =
TangemBottomSheetConfig(
isShown = true,
onDismissRequest = uiState.value.onDismissBottomSheet,
content = TokenReceiveBottomSheetConfig(
name = currencyStatus.currency.name,
symbol = currencyStatus.currency.symbol,
network = currencyStatus.currency.network.name,
addresses = networkAddress.availableAddresses
.mapToAddressModels(currencyStatus.currency)
.toImmutableList(),
showMemoDisclaimer =
currencyStatus.currency.network.transactionExtrasType != Network.TransactionExtrasType.NONE,
onCopyClick = {
Analytics.send(TokenReceiveAnalyticsEvent.ButtonCopyAddress(currencyStatus.currency.symbol))
clipboardManager.setText(text = it, isSensitive = true)
},
onShareClick = {
Analytics.send(TokenReceiveAnalyticsEvent.ButtonShareAddress(currencyStatus.currency.symbol))
shareManager.shareText(text = it)
},
),
)
private suspend fun createUserWallet(scanResponse: ScanResponse): UserWallet {
return requireNotNull(
value = userWalletBuilderFactory.create(scanResponse = scanResponse).build(),

View file

@ -20,7 +20,7 @@ import com.tangem.features.onboarding.v2.impl.R
@Composable
fun OnboardingNoteTopUpHeader(
balance: String?,
balance: String,
cardArtworkUrl: String?,
isRefreshing: Boolean,
onRefreshBalanceClick: () -> Unit,
@ -58,7 +58,7 @@ fun OnboardingNoteTopUpHeader(
)
SpacerH8()
Text(
text = balance.orEmpty(),
text = balance,
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,

View file

@ -1,9 +1,9 @@
package com.tangem.features.onboarding.v2.note.impl.child.topup.ui
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@ -11,9 +11,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.PrimaryButtonIconEnd
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.components.SecondaryButton
import com.tangem.core.ui.components.SpacerH8
import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheet
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
@ -66,14 +66,10 @@ fun OnboardingNoteTopUp(state: OnboardingNoteTopUpUM, modifier: Modifier = Modif
)
}
if (state.isTopUpDataLoading) {
CircularProgressIndicator(
modifier = Modifier
.padding(all = 32.dp)
.size(36.dp),
)
} else {
BottomButtons(state)
BottomButtons(state)
state.addressBottomSheetConfig?.let { config ->
TokenReceiveBottomSheet(config = config)
}
}
}
@ -81,32 +77,33 @@ fun OnboardingNoteTopUp(state: OnboardingNoteTopUpUM, modifier: Modifier = Modif
@Composable
private fun BottomButtons(state: OnboardingNoteTopUpUM) {
if (state.availableForBuy) {
PrimaryButtonIconEnd(
PrimaryButton(
modifier = Modifier
.padding(horizontal = 16.dp)
.padding(start = 16.dp, end = 16.dp, bottom = 8.dp)
.fillMaxWidth(),
iconResId = R.drawable.ic_tangem_24,
text = stringResourceSafe(R.string.onboarding_top_up_button_but_crypto),
onClick = state.onBuyCryptoClick,
)
SpacerH8()
SecondaryButton(
modifier = Modifier
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
.fillMaxWidth(),
text = stringResourceSafe(R.string.onboarding_top_up_button_show_wallet_address),
onClick = state.onShowWalletAddressClick,
)
} else {
PrimaryButtonIconEnd(
PrimaryButton(
modifier = Modifier
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
.fillMaxWidth(),
iconResId = R.drawable.ic_tangem_24,
text = stringResourceSafe(R.string.onboarding_button_receive_crypto),
onClick = state.onShowWalletAddressClick,
)
}
AnimatedVisibility(visible = !state.availableForBuyLoading) {
if (state.availableForBuy) {
SecondaryButton(
modifier = Modifier
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
.fillMaxWidth(),
text = stringResourceSafe(R.string.onboarding_top_up_button_show_wallet_address),
onClick = state.onShowWalletAddressClick,
)
}
}
}
@Preview(showBackground = true)

View file

@ -1,13 +1,18 @@
package com.tangem.features.onboarding.v2.note.impl.child.topup.ui.state
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
data class OnboardingNoteTopUpUM(
val cardArtworkUrl: String? = null,
val availableForBuy: Boolean = true,
val balance: String? = null,
val availableForBuy: Boolean = false,
val availableForBuyLoading: Boolean = true,
val balance: String = "",
val isRefreshing: Boolean = false,
val isTopUpDataLoading: Boolean = true,
val amountToCreateAccount: String? = null,
val addressBottomSheetConfig: TangemBottomSheetConfig? = null,
val onBuyCryptoClick: () -> Unit = {},
val onShowWalletAddressClick: () -> Unit = {},
val onRefreshBalanceClick: () -> Unit = {},
val onDismissBottomSheet: () -> Unit = {},
)