Updated on 2026-08-14
This commit is contained in:
parent
ce878fdbe2
commit
18fe9257cb
14 changed files with 23 additions and 908 deletions
|
|
@ -20,10 +20,10 @@ 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.core.ui.extensions.TextReference
|
||||
import com.tangem.features.onboarding.v2.done.api.OnboardingDoneComponent
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
import com.tangem.features.onboarding.v2.note.api.OnboardingNoteComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.create.OnboardingNoteCreateWalletComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.topup.OnboardingNoteTopUpComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.model.OnboardingNoteModel
|
||||
import com.tangem.features.onboarding.v2.note.impl.model.OnboardingNoteCommonState
|
||||
import com.tangem.features.onboarding.v2.note.impl.route.ONBOARDING_NOTE_STEPS_COUNT
|
||||
|
|
@ -39,6 +39,7 @@ import kotlinx.coroutines.flow.StateFlow
|
|||
internal class DefaultOnboardingNoteComponent @AssistedInject constructor(
|
||||
@Assisted context: AppComponentContext,
|
||||
@Assisted val params: OnboardingNoteComponent.Params,
|
||||
val onboardingDoneComponentFactory: OnboardingDoneComponent.Factory,
|
||||
) : OnboardingNoteComponent, AppComponentContext by context {
|
||||
|
||||
private val model: OnboardingNoteModel = getOrCreateModel(params)
|
||||
|
|
@ -98,14 +99,14 @@ internal class DefaultOnboardingNoteComponent @AssistedInject constructor(
|
|||
childParams = childParams,
|
||||
onWalletCreated = { userWallet ->
|
||||
model.onWalletCreated(userWallet)
|
||||
model.stackNavigation.push(OnboardingNoteRoute.TopUp)
|
||||
model.stackNavigation.push(OnboardingNoteRoute.Done)
|
||||
},
|
||||
),
|
||||
)
|
||||
OnboardingNoteRoute.TopUp -> OnboardingNoteTopUpComponent(
|
||||
appComponentContext = factoryContext,
|
||||
params = OnboardingNoteTopUpComponent.Params(
|
||||
childParams = childParams,
|
||||
OnboardingNoteRoute.Done -> onboardingDoneComponentFactory.create(
|
||||
context = factoryContext,
|
||||
params = OnboardingDoneComponent.Params(
|
||||
mode = OnboardingDoneComponent.Mode.WalletCreated,
|
||||
onDone = { params.onDone() },
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.child.topup
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
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.features.onboarding.v2.note.impl.DefaultOnboardingNoteComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.topup.model.OnboardingNoteTopUpModel
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.topup.ui.OnboardingNoteTopUp
|
||||
|
||||
internal class OnboardingNoteTopUpComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
private val params: Params,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: OnboardingNoteTopUpModel = getOrCreateModel(params)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
BackHandler(onBack = remember(this) { { params.childParams.onBack() } })
|
||||
|
||||
OnboardingNoteTopUp(
|
||||
modifier = modifier,
|
||||
state = state,
|
||||
)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val childParams: DefaultOnboardingNoteComponent.ChildParams,
|
||||
val onDone: () -> Unit,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,253 +0,0 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.child.topup.model
|
||||
|
||||
import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheetConfig
|
||||
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.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.card.repository.CardRepository
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.network.NetworkAddress
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.onramp.GetLegacyTopUpUrlUseCase
|
||||
import com.tangem.domain.tokens.FetchCurrencyStatusUseCase
|
||||
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
|
||||
import com.tangem.domain.tokens.TokensFeatureToggles
|
||||
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
||||
import com.tangem.domain.tokens.model.analytics.TokenReceiveAnalyticsEvent
|
||||
import com.tangem.domain.tokens.wallet.WalletBalanceFetcher
|
||||
import com.tangem.domain.wallets.builder.ColdUserWalletBuilder
|
||||
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
|
||||
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 com.tangem.utils.extensions.isPositive
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@ModelScoped
|
||||
internal class OnboardingNoteTopUpModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
|
||||
private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase,
|
||||
private val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory,
|
||||
private val getLegacyTopUpUrlUseCase: GetLegacyTopUpUrlUseCase,
|
||||
private val urlOpener: UrlOpener,
|
||||
private val clipboardManager: ClipboardManager,
|
||||
private val shareManager: ShareManager,
|
||||
private val rampStateManager: RampStateManager,
|
||||
private val cardRepository: CardRepository,
|
||||
private val saveWalletUseCase: SaveWalletUseCase,
|
||||
private val walletBalanceFetcher: WalletBalanceFetcher,
|
||||
private val tokensFeatureToggles: TokensFeatureToggles,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<OnboardingNoteTopUpComponent.Params>()
|
||||
private val commonState = params.childParams.commonState
|
||||
private val scanResponse = params.childParams.commonState.value.scanResponse
|
||||
private var userWallet = params.childParams.commonState.value.userWallet
|
||||
|
||||
private val _uiState = MutableStateFlow(
|
||||
OnboardingNoteTopUpUM(
|
||||
onRefreshBalanceClick = ::refreshBalance,
|
||||
onBuyCryptoClick = ::onBuyCryptoClick,
|
||||
onShowWalletAddressClick = ::onShowWalletAddressClick,
|
||||
onDismissBottomSheet = ::onDismissBottomSheet,
|
||||
),
|
||||
)
|
||||
|
||||
val uiState: StateFlow<OnboardingNoteTopUpUM> = _uiState
|
||||
|
||||
init {
|
||||
Analytics.send(OnboardingEvent.Topup.ScreenOpened)
|
||||
observeArtwork()
|
||||
modelScope.launch {
|
||||
createUserWalletIfNull()
|
||||
cardRepository.finishCardActivation(scanResponse.card.cardId)
|
||||
observeCryptoCurrencyStatus()
|
||||
refreshBalance()
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshBalance() {
|
||||
modelScope.launch {
|
||||
showBalanceLoadingProgress(true)
|
||||
createUserWalletIfNull()
|
||||
val userWalletId = requireNotNull(userWallet?.walletId)
|
||||
if (tokensFeatureToggles.isWalletBalanceFetcherEnabled) {
|
||||
walletBalanceFetcher(params = WalletBalanceFetcher.Params(userWalletId = userWalletId))
|
||||
.onLeft(Timber::e)
|
||||
} else {
|
||||
fetchCurrencyStatusUseCase(userWalletId = userWalletId, refresh = true)
|
||||
}
|
||||
showBalanceLoadingProgress(false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onBuyCryptoClick() {
|
||||
val cryptoCurrencyStatus = params.childParams.commonState.value.cryptoCurrencyStatus ?: return
|
||||
modelScope.launch {
|
||||
getLegacyTopUpUrlUseCase(cryptoCurrencyStatus).onRight {
|
||||
urlOpener.openUrl(it)
|
||||
}
|
||||
}
|
||||
Analytics.send(OnboardingEvent.Topup.ButtonBuyCrypto(cryptoCurrencyStatus.currency))
|
||||
}
|
||||
|
||||
private fun onShowWalletAddressClick() {
|
||||
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() {
|
||||
if (userWallet != null) {
|
||||
return
|
||||
}
|
||||
val commonState = params.childParams.commonState.value
|
||||
userWallet = commonState.userWallet ?: createAndSaveUserWallet(scanResponse)
|
||||
}
|
||||
|
||||
private fun observeArtwork() {
|
||||
modelScope.launch {
|
||||
params.childParams.commonState.collect {
|
||||
_uiState.value = _uiState.value.copy(
|
||||
cardArtwork = it.cardArtwork,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeCryptoCurrencyStatus() {
|
||||
val userWalletId = userWallet?.walletId ?: return
|
||||
getSingleCryptoCurrencyStatusUseCase.invokeSingleWallet(userWalletId = userWalletId)
|
||||
.map { it.getOrNull() }
|
||||
.filterNotNull()
|
||||
.onEach(::applyCryptoCurrencyStatusToState)
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun applyCryptoCurrencyStatusToState(status: CryptoCurrencyStatus) {
|
||||
if (commonState.value.cryptoCurrencyStatus == null) {
|
||||
loadAvailableForBuy(status)
|
||||
}
|
||||
|
||||
commonState.update {
|
||||
it.copy(cryptoCurrencyStatus = status)
|
||||
}
|
||||
|
||||
val amount = when (status.value) {
|
||||
is CryptoCurrencyStatus.Loaded -> status.value.amount
|
||||
is CryptoCurrencyStatus.NoAccount -> status.value.amount
|
||||
is CryptoCurrencyStatus.NoQuote -> status.value.amount
|
||||
else -> null
|
||||
}
|
||||
val hasCurrentNetworkTransactions = when (status.value) {
|
||||
is CryptoCurrencyStatus.Loaded -> status.value.hasCurrentNetworkTransactions
|
||||
is CryptoCurrencyStatus.NoAccount -> status.value.hasCurrentNetworkTransactions
|
||||
else -> false
|
||||
}
|
||||
val amountToCreateAccount = (status.value as? CryptoCurrencyStatus.NoAccount)?.amountToCreateAccount
|
||||
|
||||
if (amount?.isPositive() == true || hasCurrentNetworkTransactions) {
|
||||
params.onDone()
|
||||
}
|
||||
|
||||
_uiState.update {
|
||||
it.copy(
|
||||
amountToCreateAccount = amountToCreateAccount
|
||||
?.format {
|
||||
crypto(
|
||||
symbol = status.currency.symbol,
|
||||
decimals = status.currency.decimals,
|
||||
)
|
||||
},
|
||||
balance = amount?.format {
|
||||
crypto(
|
||||
symbol = status.currency.symbol,
|
||||
decimals = status.currency.decimals,
|
||||
)
|
||||
}.orEmpty(),
|
||||
isTopUpDataLoading = status.value.networkAddress == null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showBalanceLoadingProgress(value: Boolean) {
|
||||
_uiState.update {
|
||||
it.copy(isRefreshing = value)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadAvailableForBuy(cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
modelScope.launch {
|
||||
val availableForBuy = rampStateManager.availableForBuy(
|
||||
userWallet = userWallet ?: return@launch,
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency,
|
||||
)
|
||||
_uiState.update {
|
||||
it.copy(
|
||||
availableForBuy = availableForBuy == ScenarioUnavailabilityReason.None,
|
||||
availableForBuyLoading = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createReceiveBS(currencyStatus: CryptoCurrencyStatus, networkAddress: NetworkAddress) =
|
||||
TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = uiState.value.onDismissBottomSheet,
|
||||
content = TokenReceiveBottomSheetConfig(
|
||||
asset = TokenReceiveBottomSheetConfig.Asset.Currency(
|
||||
name = currencyStatus.currency.name,
|
||||
symbol = currencyStatus.currency.symbol,
|
||||
),
|
||||
network = currencyStatus.currency.network,
|
||||
networkAddress = networkAddress,
|
||||
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 createAndSaveUserWallet(scanResponse: ScanResponse): UserWallet {
|
||||
val wallet = requireNotNull(
|
||||
value = coldUserWalletBuilderFactory.create(scanResponse = scanResponse).build(),
|
||||
lazyMessage = { "User wallet not created" },
|
||||
)
|
||||
saveWalletUseCase(wallet, false)
|
||||
return wallet
|
||||
}
|
||||
}
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.child.topup.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
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.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.SpacerH8
|
||||
import com.tangem.core.ui.components.SpacerHMax
|
||||
import com.tangem.core.ui.components.artwork.ArtworkUM
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.LocalTangemShimmer
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.onboarding.v2.common.ui.RefreshButton
|
||||
import com.tangem.features.onboarding.v2.common.ui.WalletCard
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
import com.valentinilk.shimmer.shimmer
|
||||
|
||||
@Composable
|
||||
fun OnboardingNoteTopUpHeader(
|
||||
balance: String,
|
||||
cardArtwork: ArtworkUM?,
|
||||
isRefreshing: Boolean,
|
||||
onRefreshBalanceClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.heightIn(min = 180.dp)
|
||||
.widthIn(max = 450.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 24.dp, horizontal = 16.dp)
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
TangemTheme.colors.button.secondary,
|
||||
shape = TangemTheme.shapes.roundedCornersMedium,
|
||||
),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.padding(horizontal = 32.dp),
|
||||
) {
|
||||
SpacerHMax()
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.common_balance_title),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
SpacerH8()
|
||||
Text(
|
||||
modifier = if (balance.isEmpty()) {
|
||||
Modifier
|
||||
.width(120.dp)
|
||||
.clip(RoundedCornerShape(size = TangemTheme.dimens.radius3))
|
||||
.shimmer(LocalTangemShimmer.current)
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
text = balance,
|
||||
)
|
||||
SpacerHMax()
|
||||
}
|
||||
}
|
||||
WalletCard(
|
||||
modifier = Modifier.width(120.dp).align(Alignment.TopCenter),
|
||||
artwork = cardArtwork,
|
||||
)
|
||||
RefreshButton(
|
||||
modifier = Modifier.align(Alignment.BottomCenter),
|
||||
isRefreshing = isRefreshing,
|
||||
onRefreshBalanceClick = onRefreshBalanceClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun OnboardinNoteTopUpHeaderPreview() {
|
||||
TangemThemePreview {
|
||||
OnboardingNoteTopUpHeader(
|
||||
balance = "0.00000001 BTC",
|
||||
cardArtwork = ArtworkUM(null, ""),
|
||||
onRefreshBalanceClick = {},
|
||||
isRefreshing = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
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.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
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.PrimaryButton
|
||||
import com.tangem.core.ui.components.SecondaryButton
|
||||
import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheet
|
||||
import com.tangem.core.ui.components.SpacerH16
|
||||
import com.tangem.core.ui.components.SpacerHMax
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
import com.tangem.features.onboarding.v2.note.impl.ALL_STEPS_TOP_CONTAINER_WEIGHT
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.topup.ui.state.OnboardingNoteTopUpUM
|
||||
|
||||
@Composable
|
||||
fun OnboardingNoteTopUp(state: OnboardingNoteTopUpUM, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.navigationBarsPadding(),
|
||||
verticalArrangement = Arrangement.Bottom,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
OnboardingNoteTopUpHeader(
|
||||
balance = state.balance,
|
||||
cardArtwork = state.cardArtwork,
|
||||
onRefreshBalanceClick = state.onRefreshBalanceClick,
|
||||
isRefreshing = state.isRefreshing,
|
||||
modifier = Modifier
|
||||
.padding(top = 64.dp)
|
||||
.padding(horizontal = 24.dp)
|
||||
.weight(ALL_STEPS_TOP_CONTAINER_WEIGHT)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.weight(1 - ALL_STEPS_TOP_CONTAINER_WEIGHT)
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(horizontal = 32.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
SpacerHMax()
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.onboarding_topup_title),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
)
|
||||
|
||||
val text = if (state.amountToCreateAccount != null) {
|
||||
stringResourceSafe(
|
||||
R.string.onboarding_top_up_min_create_account_amount,
|
||||
state.amountToCreateAccount,
|
||||
)
|
||||
} else {
|
||||
stringResourceSafe(R.string.onboarding_top_up_body)
|
||||
}
|
||||
SpacerH16()
|
||||
Text(
|
||||
text = text,
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
SpacerHMax()
|
||||
}
|
||||
|
||||
BottomButtons(state)
|
||||
|
||||
state.addressBottomSheetConfig?.let { config ->
|
||||
TokenReceiveBottomSheet(config = config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BottomButtons(state: OnboardingNoteTopUpUM) {
|
||||
if (state.availableForBuy) {
|
||||
PrimaryButton(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 8.dp)
|
||||
.fillMaxWidth(),
|
||||
text = stringResourceSafe(R.string.onboarding_top_up_button_but_crypto),
|
||||
onClick = state.onBuyCryptoClick,
|
||||
)
|
||||
} else {
|
||||
PrimaryButton(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
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)
|
||||
@Composable
|
||||
private fun OnboardingNoteTopUpPreview() {
|
||||
TangemThemePreview {
|
||||
OnboardingNoteTopUp(
|
||||
state = OnboardingNoteTopUpUM(
|
||||
availableForBuy = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.child.topup.ui.state
|
||||
|
||||
import com.tangem.core.ui.components.artwork.ArtworkUM
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
|
||||
data class OnboardingNoteTopUpUM(
|
||||
val cardArtwork: ArtworkUM? = 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 = {},
|
||||
)
|
||||
|
|
@ -5,7 +5,6 @@ import com.tangem.core.decompose.model.Model
|
|||
import com.tangem.features.onboarding.v2.note.api.OnboardingNoteComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.DefaultOnboardingNoteComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.create.model.OnboardingNoteCreateWalletModel
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.topup.model.OnboardingNoteTopUpModel
|
||||
import com.tangem.features.onboarding.v2.note.impl.model.OnboardingNoteModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
|
|
@ -37,9 +36,4 @@ internal interface ModelModule {
|
|||
@IntoMap
|
||||
@ClassKey(OnboardingNoteCreateWalletModel::class)
|
||||
fun provideNoteCreateWalletModel(model: OnboardingNoteCreateWalletModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(OnboardingNoteTopUpModel::class)
|
||||
fun provideNoteTopUpModel(model: OnboardingNoteTopUpModel): Model
|
||||
}
|
||||
|
|
@ -84,7 +84,7 @@ internal class OnboardingNoteModel @Inject constructor(
|
|||
return if (card.wallets.isEmpty()) {
|
||||
OnboardingNoteRoute.CreateWallet
|
||||
} else {
|
||||
OnboardingNoteRoute.TopUp
|
||||
OnboardingNoteRoute.Done
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ internal sealed class OnboardingNoteRoute {
|
|||
data object CreateWallet : OnboardingNoteRoute()
|
||||
|
||||
@Serializable
|
||||
data object TopUp : OnboardingNoteRoute()
|
||||
data object Done : OnboardingNoteRoute()
|
||||
}
|
||||
|
||||
internal const val ONBOARDING_NOTE_STEPS_COUNT = 3
|
||||
internal const val ONBOARDING_NOTE_STEPS_COUNT = 2
|
||||
|
|
@ -2,5 +2,5 @@ package com.tangem.features.onboarding.v2.note.impl.route
|
|||
|
||||
internal fun OnboardingNoteRoute.stepNum() = when (this) {
|
||||
OnboardingNoteRoute.CreateWallet -> 1
|
||||
OnboardingNoteRoute.TopUp -> 2
|
||||
OnboardingNoteRoute.Done -> 2
|
||||
}
|
||||
|
|
@ -7,40 +7,22 @@ import com.tangem.common.core.TangemError
|
|||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheetConfig
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
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.decompose.ui.UiMessageSender
|
||||
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.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.toWrappedList
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.datasource.local.config.issuers.IssuersConfigStorage
|
||||
import com.tangem.domain.card.common.util.twinsIsTwinned
|
||||
import com.tangem.domain.card.repository.CardRepository
|
||||
import com.tangem.domain.common.TwinCardNumber
|
||||
import com.tangem.domain.common.getTwinCardNumber
|
||||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase
|
||||
import com.tangem.domain.onramp.GetLegacyTopUpUrlUseCase
|
||||
import com.tangem.domain.tokens.FetchCurrencyStatusUseCase
|
||||
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
|
||||
import com.tangem.domain.tokens.TokensFeatureToggles
|
||||
import com.tangem.domain.tokens.model.analytics.TokenReceiveAnalyticsEvent
|
||||
import com.tangem.domain.tokens.wallet.WalletBalanceFetcher
|
||||
import com.tangem.domain.wallets.builder.ColdUserWalletBuilder
|
||||
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
|
||||
import com.tangem.domain.wallets.usecase.DeleteWalletUseCase
|
||||
|
|
@ -61,11 +43,9 @@ import com.tangem.utils.coroutines.saveIn
|
|||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
|
|
@ -81,16 +61,8 @@ internal class OnboardingTwinModel @Inject constructor(
|
|||
private val tangemSdkManager: TangemSdkManager,
|
||||
private val issuersConfigStorage: IssuersConfigStorage,
|
||||
private val cardRepository: CardRepository,
|
||||
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
|
||||
private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase,
|
||||
private val getLegacyTopUpUrlUseCase: GetLegacyTopUpUrlUseCase,
|
||||
private val urlOpener: UrlOpener,
|
||||
private val uiMessageSender: UiMessageSender,
|
||||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val clipboardManager: ClipboardManager,
|
||||
private val shareManager: ShareManager,
|
||||
private val tokensFeatureToggles: TokensFeatureToggles,
|
||||
private val walletBalanceFetcher: WalletBalanceFetcher,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<OnboardingTwinComponent.Params>()
|
||||
|
|
@ -116,14 +88,10 @@ internal class OnboardingTwinModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
Mode.CreateWallet -> {
|
||||
if (params.scanResponse.twinsIsTwinned()) {
|
||||
OnboardingTwinUM.TopUpPrepare
|
||||
} else {
|
||||
OnboardingTwinUM.Welcome(
|
||||
pairCardNumber = firstCardTwinNumber.pairNumber().number,
|
||||
onContinueClick = ::navigateToFirstScan,
|
||||
)
|
||||
}
|
||||
OnboardingTwinUM.Welcome(
|
||||
pairCardNumber = firstCardTwinNumber.pairNumber().number,
|
||||
onContinueClick = ::navigateToFirstScan,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
@ -139,11 +107,6 @@ internal class OnboardingTwinModel @Inject constructor(
|
|||
saveTwinsOnboardingShownUseCase()
|
||||
}
|
||||
}
|
||||
OnboardingTwinUM.TopUpPrepare -> {
|
||||
modelScope.launch {
|
||||
setTopUpState(params.scanResponse)
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
|
@ -218,10 +181,7 @@ internal class OnboardingTwinModel @Inject constructor(
|
|||
},
|
||||
)
|
||||
}
|
||||
|
||||
innerNavigationState.update {
|
||||
it.copy(stackSize = 2)
|
||||
}
|
||||
innerNavigationState.update { it.copy(stackSize = 2) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -229,7 +189,6 @@ internal class OnboardingTwinModel @Inject constructor(
|
|||
|
||||
private fun createSecondWallet(firstPublicKey: String) {
|
||||
setLoading(true)
|
||||
|
||||
modelScope.launch {
|
||||
val secondCardNumber = firstCardTwinNumber.pairNumber().number
|
||||
val result = tangemSdkManager.createSecondTwinWallet(
|
||||
|
|
@ -318,13 +277,13 @@ internal class OnboardingTwinModel @Inject constructor(
|
|||
Mode.CreateWallet -> {
|
||||
modelScope.launch {
|
||||
setLoading(true)
|
||||
setTopUpState(scanResponse)
|
||||
finishActivation(scanResponse)
|
||||
}.saveIn(cryptoCurrencyStatusJobHolder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun setTopUpState(scanResponse: ScanResponse) = coroutineScope {
|
||||
private suspend fun finishActivation(scanResponse: ScanResponse) = coroutineScope {
|
||||
val userWallet = coldUserWalletBuilderFactory.create(scanResponse).build() ?: run {
|
||||
Timber.e("User wallet not created")
|
||||
setLoading(false)
|
||||
|
|
@ -342,117 +301,7 @@ internal class OnboardingTwinModel @Inject constructor(
|
|||
|
||||
cardRepository.finishCardActivation(params.scanResponse.card.cardId)
|
||||
|
||||
if (tokensFeatureToggles.isWalletBalanceFetcherEnabled) {
|
||||
walletBalanceFetcher(params = WalletBalanceFetcher.Params(userWalletId = userWallet.walletId))
|
||||
} else {
|
||||
fetchCurrencyStatusUseCase.invoke(userWalletId = userWallet.walletId, refresh = true)
|
||||
}
|
||||
.onLeft {
|
||||
Timber.e("Unable to fetch currency status: $it")
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
val cryptoCurrencyStatus = getSingleCryptoCurrencyStatusUseCase.invokeSingleWallet(userWallet.walletId)
|
||||
.firstOrNull()?.getOrNull()
|
||||
?: run {
|
||||
setLoading(false)
|
||||
Timber.e("Unable to get currency status")
|
||||
return@coroutineScope
|
||||
}
|
||||
|
||||
launch {
|
||||
getSingleCryptoCurrencyStatusUseCase.invokeSingleWallet(userWallet.walletId)
|
||||
.collect {
|
||||
it.onRight { status ->
|
||||
applyCryptoCurrencyStatusToState(status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_uiState.value = OnboardingTwinUM.TopUp(
|
||||
onBuyCryptoClick = { onBuyCryptoClick(cryptoCurrencyStatus) },
|
||||
onRefreshClick = { onRefreshBalanceClick(userWallet) },
|
||||
onShowAddressClick = { onShowAddressClick(cryptoCurrencyStatus) },
|
||||
isLoading = true,
|
||||
)
|
||||
|
||||
innerNavigationState.update {
|
||||
it.copy(stackSize = 4)
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyCryptoCurrencyStatusToState(status: CryptoCurrencyStatus) {
|
||||
val amount = (status.value as? CryptoCurrencyStatus.Loaded)?.amount ?: return
|
||||
if (amount > BigDecimal.ZERO) {
|
||||
params.modelCallbacks.onDone()
|
||||
} else {
|
||||
update<OnboardingTwinUM.TopUp> {
|
||||
it.copy(
|
||||
balance = BigDecimal.ZERO.format { crypto(status.currency) },
|
||||
onBuyCryptoClick = { onBuyCryptoClick(status) },
|
||||
onShowAddressClick = { onShowAddressClick(status) },
|
||||
isLoading = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onBuyCryptoClick(status: CryptoCurrencyStatus) {
|
||||
modelScope.launch {
|
||||
getLegacyTopUpUrlUseCase(status).onRight {
|
||||
urlOpener.openUrl(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onShowAddressClick(status: CryptoCurrencyStatus) {
|
||||
val currency = status.currency
|
||||
val networkAddress = status.value.networkAddress ?: return
|
||||
|
||||
update<OnboardingTwinUM.TopUp> {
|
||||
it.copy(
|
||||
bottomSheetConfig = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = {
|
||||
update<OnboardingTwinUM.TopUp> {
|
||||
it.copy(bottomSheetConfig = TangemBottomSheetConfig.Empty)
|
||||
}
|
||||
},
|
||||
content = TokenReceiveBottomSheetConfig(
|
||||
asset = TokenReceiveBottomSheetConfig.Asset.Currency(
|
||||
name = currency.name,
|
||||
symbol = currency.symbol,
|
||||
),
|
||||
network = currency.network,
|
||||
networkAddress = networkAddress,
|
||||
showMemoDisclaimer =
|
||||
currency.network.transactionExtrasType != Network.TransactionExtrasType.NONE,
|
||||
onCopyClick = {
|
||||
Analytics.send(TokenReceiveAnalyticsEvent.ButtonCopyAddress(currency.symbol))
|
||||
clipboardManager.setText(text = it, isSensitive = true)
|
||||
},
|
||||
onShareClick = {
|
||||
Analytics.send(TokenReceiveAnalyticsEvent.ButtonShareAddress(currency.symbol))
|
||||
shareManager.shareText(text = it)
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onRefreshBalanceClick(userWallet: UserWallet) {
|
||||
update<OnboardingTwinUM.TopUp> {
|
||||
it.copy(isLoading = true)
|
||||
}
|
||||
modelScope.launch {
|
||||
if (tokensFeatureToggles.isWalletBalanceFetcherEnabled) {
|
||||
walletBalanceFetcher(params = WalletBalanceFetcher.Params(userWalletId = userWallet.walletId))
|
||||
.onLeft(Timber::e)
|
||||
} else {
|
||||
fetchCurrencyStatusUseCase(userWalletId = userWallet.walletId, refresh = true)
|
||||
}
|
||||
}
|
||||
params.modelCallbacks.onDone()
|
||||
}
|
||||
|
||||
private fun saveWalletAndDone() {
|
||||
|
|
|
|||
|
|
@ -18,9 +18,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
import com.tangem.core.ui.components.PrimaryButtonIconEnd
|
||||
import com.tangem.core.ui.components.SecondaryButton
|
||||
import com.tangem.core.ui.components.SpacerH16
|
||||
import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheet
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemAnimations
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -43,11 +41,6 @@ internal fun OnboardingTwin(state: OnboardingTwinUM, modifier: Modifier = Modifi
|
|||
.weight(.48f)
|
||||
.fillMaxWidth(),
|
||||
state = state.artwork,
|
||||
balance = (state as? OnboardingTwinUM.TopUp)?.balance ?: "",
|
||||
isRefreshing = state.isLoading,
|
||||
onRefreshBalanceClick = {
|
||||
(state as? OnboardingTwinUM.TopUp)?.onRefreshClick()
|
||||
},
|
||||
)
|
||||
|
||||
AnimatedContent(
|
||||
|
|
@ -60,16 +53,10 @@ internal fun OnboardingTwin(state: OnboardingTwinUM, modifier: Modifier = Modifi
|
|||
when (st) {
|
||||
is OnboardingTwinUM.ResetWarning -> ResetWarning(st)
|
||||
is OnboardingTwinUM.ScanCard -> ScanCard(st)
|
||||
is OnboardingTwinUM.TopUp -> TopUp(st)
|
||||
is OnboardingTwinUM.Welcome -> Welcome(st)
|
||||
OnboardingTwinUM.TopUpPrepare -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state is OnboardingTwinUM.TopUp) {
|
||||
TokenReceiveBottomSheet(config = state.bottomSheetConfig)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
|
|
@ -154,55 +141,6 @@ private fun ResetWarning(state: OnboardingTwinUM.ResetWarning, modifier: Modifie
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TopUp(state: OnboardingTwinUM.TopUp, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(start = 32.dp, end = 32.dp, bottom = 16.dp)
|
||||
.weight(1f)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.onboarding_topup_title),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
style = TangemTheme.typography.h2,
|
||||
)
|
||||
|
||||
SpacerH16()
|
||||
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.onboarding_top_up_body),
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
style = TangemTheme.typography.body1,
|
||||
)
|
||||
}
|
||||
|
||||
PrimaryButton(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 12.dp)
|
||||
.fillMaxWidth(),
|
||||
text = stringResourceSafe(R.string.onboarding_top_up_button_but_crypto),
|
||||
onClick = state.onBuyCryptoClick,
|
||||
)
|
||||
|
||||
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.onShowAddressClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ScanCard(state: OnboardingTwinUM.ScanCard, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
|
|
@ -287,14 +225,6 @@ private fun Welcome(state: OnboardingTwinUM.Welcome, modifier: Modifier = Modifi
|
|||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun PreviewTopUp() {
|
||||
TangemThemePreview {
|
||||
OnboardingTwin(OnboardingTwinUM.TopUp())
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun PreviewWelcome() {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
package com.tangem.features.onboarding.v2.twin.impl.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.Transition
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Button
|
||||
|
|
@ -16,21 +14,15 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import androidx.compose.ui.zIndex
|
||||
import com.tangem.core.ui.components.SpacerH8
|
||||
import com.tangem.core.ui.components.SpacerHMax
|
||||
import com.tangem.core.ui.components.artwork.ArtworkUM
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.domain.wallets.models.Artwork
|
||||
import com.tangem.features.onboarding.v2.common.ui.RefreshButton
|
||||
import com.tangem.features.onboarding.v2.common.ui.WalletCard
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
import kotlinx.coroutines.delay
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
|
@ -45,8 +37,6 @@ internal sealed class TwinWalletArtworkUM {
|
|||
FirstCard, SecondCard
|
||||
}
|
||||
}
|
||||
|
||||
data object TopUp : TwinWalletArtworkUM()
|
||||
}
|
||||
|
||||
private data class CardsTransitionState(
|
||||
|
|
@ -64,15 +54,10 @@ private data class WalletCardTransitionState(
|
|||
val zIndex: Float = 0f,
|
||||
)
|
||||
|
||||
@SuppressLint("UnusedBoxWithConstraintsScope")
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
internal fun TwinWalletArtworks(
|
||||
state: TwinWalletArtworkUM,
|
||||
balance: String,
|
||||
isRefreshing: Boolean,
|
||||
onRefreshBalanceClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
internal fun TwinWalletArtworks(state: TwinWalletArtworkUM, modifier: Modifier = Modifier) {
|
||||
BoxWithConstraints(
|
||||
modifier
|
||||
.heightIn(min = 180.dp)
|
||||
|
|
@ -110,22 +95,6 @@ internal fun TwinWalletArtworks(
|
|||
}
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = state == TwinWalletArtworkUM.TopUp,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 24.dp, horizontal = 16.dp)
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
TangemTheme.colors.button.secondary,
|
||||
shape = TangemTheme.shapes.roundedCornersMedium,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedTwinCards(
|
||||
transition1 = transition1,
|
||||
transition2 = transition2,
|
||||
|
|
@ -133,46 +102,6 @@ internal fun TwinWalletArtworks(
|
|||
.widthIn(max = 450.dp)
|
||||
.matchParentSize(),
|
||||
)
|
||||
|
||||
AnimatedVisibility(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
visible = state == TwinWalletArtworkUM.TopUp,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
SpacerHMax()
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.common_balance_title),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
SpacerH8()
|
||||
Text(
|
||||
text = balance,
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
SpacerHMax()
|
||||
}
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
modifier = Modifier.align(Alignment.BottomCenter),
|
||||
visible = state == TwinWalletArtworkUM.TopUp,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(),
|
||||
) {
|
||||
RefreshButton(
|
||||
isRefreshing = isRefreshing,
|
||||
onRefreshBalanceClick = onRefreshBalanceClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -308,26 +237,6 @@ private fun TwinWalletArtworkUM.toTransitionSetState(
|
|||
)
|
||||
}
|
||||
}
|
||||
TwinWalletArtworkUM.TopUp -> {
|
||||
val scale = 0.4f
|
||||
val yTranslation = -maxHeightDp * density - 24 * density
|
||||
listOf(
|
||||
CardsTransitionState(
|
||||
walletCard1 = WalletCardTransitionState(
|
||||
yTranslation = yTranslation,
|
||||
xScale = scale,
|
||||
yScale = scale,
|
||||
zIndex = 2f,
|
||||
),
|
||||
walletCard2 = WalletCardTransitionState(
|
||||
yTranslation = yTranslation * 0.35f,
|
||||
xScale = scale * 0.8f,
|
||||
yScale = scale * 0.8f,
|
||||
zIndex = 1f,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360, heightDp = 640)
|
||||
|
|
@ -341,16 +250,13 @@ private fun Preview() {
|
|||
.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
var state: TwinWalletArtworkUM by remember { mutableStateOf(TwinWalletArtworkUM.TopUp) }
|
||||
var state: TwinWalletArtworkUM by remember { mutableStateOf(TwinWalletArtworkUM.Spread) }
|
||||
|
||||
TwinWalletArtworks(
|
||||
state = state,
|
||||
modifier = Modifier
|
||||
.padding(top = 250.dp)
|
||||
.fillMaxWidth(),
|
||||
balance = "1 USD",
|
||||
isRefreshing = false,
|
||||
onRefreshBalanceClick = {},
|
||||
)
|
||||
|
||||
var index by remember { mutableIntStateOf(0) }
|
||||
|
|
@ -366,7 +272,6 @@ private fun Preview() {
|
|||
TwinWalletArtworkUM.Leapfrog(step = TwinWalletArtworkUM.Leapfrog.Step.SecondCard),
|
||||
TwinWalletArtworkUM.Leapfrog(step = TwinWalletArtworkUM.Leapfrog.Step.FirstCard),
|
||||
TwinWalletArtworkUM.Leapfrog(step = TwinWalletArtworkUM.Leapfrog.Step.SecondCard),
|
||||
TwinWalletArtworkUM.TopUp,
|
||||
)
|
||||
|
||||
state = list[index % list.size]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.features.onboarding.v2.twin.impl.ui.state
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.features.onboarding.v2.twin.impl.ui.TwinWalletArtworkUM
|
||||
|
||||
@Immutable
|
||||
|
|
@ -11,12 +10,6 @@ internal sealed class OnboardingTwinUM {
|
|||
abstract val isLoading: Boolean
|
||||
abstract val artwork: TwinWalletArtworkUM
|
||||
|
||||
data object TopUpPrepare : OnboardingTwinUM() {
|
||||
override val stepIndex: Int = 0
|
||||
override val isLoading: Boolean = false
|
||||
override val artwork: TwinWalletArtworkUM = TwinWalletArtworkUM.Spread
|
||||
}
|
||||
|
||||
data class Welcome(
|
||||
override val isLoading: Boolean = false,
|
||||
val pairCardNumber: Int = 2,
|
||||
|
|
@ -56,23 +49,9 @@ internal sealed class OnboardingTwinUM {
|
|||
override val artwork: TwinWalletArtworkUM = TwinWalletArtworkUM.Leapfrog(artworkStep)
|
||||
}
|
||||
|
||||
data class TopUp(
|
||||
override val isLoading: Boolean = false,
|
||||
val balance: String = "",
|
||||
val bottomSheetConfig: TangemBottomSheetConfig = TangemBottomSheetConfig.Empty,
|
||||
val onBuyCryptoClick: () -> Unit = {},
|
||||
val onShowAddressClick: () -> Unit = {},
|
||||
val onRefreshClick: () -> Unit = {},
|
||||
) : OnboardingTwinUM() {
|
||||
override val stepIndex: Int = 2
|
||||
override val artwork: TwinWalletArtworkUM = TwinWalletArtworkUM.TopUp
|
||||
}
|
||||
|
||||
fun copySealed(isLoading: Boolean = this.isLoading): OnboardingTwinUM = when (this) {
|
||||
is Welcome -> copy(isLoading = isLoading)
|
||||
is ResetWarning -> copy()
|
||||
is ScanCard -> copy(isLoading = isLoading)
|
||||
is TopUp -> copy(isLoading = isLoading)
|
||||
TopUpPrepare -> this
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue