Updated on 2026-08-14
This commit is contained in:
commit
3fc5bf8fb3
388 changed files with 5078 additions and 2871 deletions
|
|
@ -1,10 +0,0 @@
|
|||
<?xml version="1.0" ?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>BooleanPropertyNaming:PortfolioFetcher.kt$PortfolioFetcher.Mode.All$val onlyMultiCurrency: Boolean</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:PortfolioSelectorComponent.kt$PortfolioSelectorController$/** * for some Feature specific filtering * combine and update with your Feature data and [PortfolioFetcher.data] */ val isEnabled: MutableStateFlow<(UserWallet, AccountStatus) -> Boolean></ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:PortfolioSelectorComponent.kt$PortfolioSelectorController$val isAccountMode: Flow<Boolean></ID>
|
||||
<ID>UseSumOfInsteadOfFlatMapSize:PortfolioFetcher.kt$PortfolioFetcher.Data$flatten()</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
|
@ -26,7 +26,7 @@ interface PortfolioFetcher {
|
|||
|
||||
val isSingleChoice: Boolean = balances.values
|
||||
.map { it.accountsBalance.accountStatuses }
|
||||
.flatten().size == 1
|
||||
.sumOf { it.size } == 1
|
||||
|
||||
fun isSingleChoice(walletId: UserWalletId): Boolean = balances[walletId]
|
||||
?.accountsBalance
|
||||
|
|
@ -43,7 +43,7 @@ interface PortfolioFetcher {
|
|||
}
|
||||
|
||||
sealed interface Mode {
|
||||
data class All(val onlyMultiCurrency: Boolean) : Mode
|
||||
data class All(val isOnlyMultiCurrency: Boolean) : Mode
|
||||
data class Wallet(val walletId: UserWalletId) : Mode
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,5 @@
|
|||
<?xml version="1.0" ?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:DefaultPortfolioSelectorController.kt$DefaultPortfolioSelectorController$override val isAccountMode: Flow<Boolean> by lazy { isAccountsModeEnabledUseCase() }</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:DefaultPortfolioSelectorController.kt$DefaultPortfolioSelectorController$override val isEnabled: MutableStateFlow<(UserWallet, AccountStatus) -> Boolean> = MutableStateFlow { _, _ -> true }</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:DefaultPortfolioSelectorController.kt$DefaultPortfolioSelectorController$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:PortfolioSelectorModel.kt$PortfolioSelectorModel$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
|
||||
</CurrentIssues>
|
||||
<CurrentIssues/>
|
||||
</SmellBaseline>
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ internal class ArchivedAccountListModel @Inject constructor(
|
|||
|
||||
messageSender.send(
|
||||
DialogMessage(
|
||||
title = resourceReference(R.string.account_recover_limit_dialog_title),
|
||||
title = resourceReference(R.string.common_something_went_wrong),
|
||||
message = resourceReference(
|
||||
id = R.string.account_recover_limit_dialog_description,
|
||||
formatArgs = wrappedList(AccountList.MAX_ACCOUNTS_COUNT.toString()),
|
||||
|
|
|
|||
|
|
@ -274,7 +274,7 @@ internal class AccountCreateEditModel @Inject constructor(
|
|||
private fun showSomethingWrong() {
|
||||
val dialogMessage = DialogMessage(
|
||||
title = resourceReference(R.string.common_something_went_wrong),
|
||||
message = resourceReference(R.string.account_could_not_create),
|
||||
message = resourceReference(R.string.account_generic_error_dialog_message),
|
||||
)
|
||||
messageSender.send(dialogMessage)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,10 @@
|
|||
package com.tangem.features.account.createedit.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -30,6 +25,8 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import androidx.compose.ui.util.fastForEachIndexed
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.common.ui.account.*
|
||||
import com.tangem.core.ui.components.PrimaryButton
|
||||
|
|
@ -186,9 +183,9 @@ private fun AccountColor(colorsState: AccountCreateEditUM.Colors) {
|
|||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(contentPadding),
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp, Alignment.CenterHorizontally),
|
||||
) {
|
||||
colorsState.list.forEach { color ->
|
||||
colorsState.list.fastForEach { color ->
|
||||
val isSelected = color == colorsState.selected
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
|
|
@ -236,8 +233,9 @@ private fun AccountIcons(iconsState: AccountCreateEditUM.Icons) {
|
|||
FlowRow(
|
||||
maxItemsInEachRow = 6,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
iconsState.list.forEachIndexed { index, icon ->
|
||||
iconsState.list.fastForEachIndexed { index, icon ->
|
||||
val isSelected = icon == iconsState.selected
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
|
|
|
|||
|
|
@ -104,21 +104,13 @@ internal class AccountDetailsModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun failedArchiveDialog(error: ArchiveCryptoPortfolioUseCase.Error) {
|
||||
val titleRes = when (error) {
|
||||
is ArchiveCryptoPortfolioUseCase.Error.CriticalTechError.AccountListRequirementsNotMet,
|
||||
is ArchiveCryptoPortfolioUseCase.Error.CriticalTechError.AccountNotFound,
|
||||
is ArchiveCryptoPortfolioUseCase.Error.CriticalTechError.AccountsNotCreated,
|
||||
is ArchiveCryptoPortfolioUseCase.Error.DataOperationFailed,
|
||||
-> R.string.common_something_went_wrong
|
||||
is ArchiveCryptoPortfolioUseCase.Error.ActiveReferralStatus,
|
||||
-> R.string.account_could_not_archive_referral_program_title
|
||||
}
|
||||
val titleRes = R.string.common_something_went_wrong
|
||||
val messageRes = when (error) {
|
||||
is ArchiveCryptoPortfolioUseCase.Error.CriticalTechError.AccountListRequirementsNotMet,
|
||||
is ArchiveCryptoPortfolioUseCase.Error.CriticalTechError.AccountNotFound,
|
||||
is ArchiveCryptoPortfolioUseCase.Error.CriticalTechError.AccountsNotCreated,
|
||||
is ArchiveCryptoPortfolioUseCase.Error.DataOperationFailed,
|
||||
-> R.string.account_could_not_archive
|
||||
-> R.string.account_generic_error_dialog_message
|
||||
is ArchiveCryptoPortfolioUseCase.Error.ActiveReferralStatus,
|
||||
-> R.string.account_could_not_archive_referral_program_message
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ internal class DefaultPortfolioFetcher @AssistedInject constructor(
|
|||
|
||||
private fun List<UserWallet>.filterWallets(mode: Mode): List<UserWallet> = this.filter { wallet ->
|
||||
when (mode) {
|
||||
is Mode.All -> if (mode.onlyMultiCurrency) wallet.isMultiCurrency else true
|
||||
is Mode.All -> if (mode.isOnlyMultiCurrency) wallet.isMultiCurrency else true
|
||||
is Mode.Wallet -> wallet.walletId == mode.walletId
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
<?xml version="1.0" ?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>BooleanPropertyNaming:AskBiometryUM.kt$AskBiometryUM$val bottomSheetVariant: Boolean = false</ID>
|
||||
<ID>BooleanPropertyNaming:AskBiometryUM.kt$AskBiometryUM$val showProgress: Boolean = false</ID>
|
||||
<ID>BooleanPropertyNaming:DefaultAskBiometryComponent.kt$DefaultAskBiometryComponent$val bsShown by bsShown.collectAsStateWithLifecycle()</ID>
|
||||
<ID>MultilineLambdaItParameter:AskBiometryModel.kt$AskBiometryModel${ uiMessageSender.send( SnackbarMessage(stringReference("Something went wrong. Please contact support: $it")), ) }</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
|
@ -41,10 +41,10 @@ internal class DefaultAskBiometryComponent @AssistedInject constructor(
|
|||
@Composable
|
||||
override fun BottomSheet() {
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
val bsShown by bsShown.collectAsStateWithLifecycle()
|
||||
val bsConfig = remember(this, bsShown) {
|
||||
val isBSShown by bsShown.collectAsStateWithLifecycle()
|
||||
val bsConfig = remember(this, isBSShown) {
|
||||
TangemBottomSheetConfig(
|
||||
isShown = bsShown,
|
||||
isShown = isBSShown,
|
||||
onDismissRequest = ::dismiss,
|
||||
content = TangemBottomSheetConfigContent.Empty,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ internal class AskBiometryModel @Inject constructor(
|
|||
|
||||
private val _uiState = MutableStateFlow(
|
||||
AskBiometryUM(
|
||||
bottomSheetVariant = params.isBottomSheetVariant,
|
||||
isBottomSheetVariant = params.isBottomSheetVariant,
|
||||
onAllowClick = ::onAllowClick,
|
||||
onDontAllowClick = ::dontAllow,
|
||||
onDismiss = ::dismiss,
|
||||
|
|
@ -85,7 +85,7 @@ internal class AskBiometryModel @Inject constructor(
|
|||
return@launch
|
||||
}
|
||||
|
||||
_uiState.update { it.copy(showProgress = true) }
|
||||
_uiState.update { it.copy(shouldShowProgress = true) }
|
||||
|
||||
/*
|
||||
|
||||
|
|
@ -96,7 +96,7 @@ internal class AskBiometryModel @Inject constructor(
|
|||
uiMessageSender.send(
|
||||
SnackbarMessage(stringReference("No selected user wallet")),
|
||||
)
|
||||
_uiState.update { it.copy(showProgress = false) }
|
||||
_uiState.update { it.copy(shouldShowProgress = false) }
|
||||
|
||||
return@launch
|
||||
}
|
||||
|
|
@ -128,7 +128,7 @@ internal class AskBiometryModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
if (_uiState.value.bottomSheetVariant) {
|
||||
if (_uiState.value.isBottomSheetVariant) {
|
||||
dismissBSFlow.emit(Unit)
|
||||
delay(timeMillis = 500)
|
||||
}
|
||||
|
|
@ -142,9 +142,9 @@ internal class AskBiometryModel @Inject constructor(
|
|||
userWalletId = userWallet.walletId,
|
||||
lockMethod = UserWalletsListRepository.LockMethod.Biometric,
|
||||
changeUnsecured = false,
|
||||
).onLeft {
|
||||
).onLeft { error ->
|
||||
uiMessageSender.send(
|
||||
SnackbarMessage(stringReference("Something went wrong. Please contact support: $it")),
|
||||
SnackbarMessage(stringReference("Something went wrong. Please contact support: $error")),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ internal fun AskBiometry(state: AskBiometryUM, modifier: Modifier = Modifier) {
|
|||
Column(
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
if (state.bottomSheetVariant) {
|
||||
if (state.isBottomSheetVariant) {
|
||||
Header(onCloseClick = state.onDismiss)
|
||||
}
|
||||
|
||||
|
|
@ -128,12 +128,12 @@ private fun Footer(state: AskBiometryUM, modifier: Modifier = Modifier) {
|
|||
) {
|
||||
PrimaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
showProgress = state.showProgress,
|
||||
showProgress = state.shouldShowProgress,
|
||||
text = stringResourceSafe(id = R.string.save_user_wallet_agreement_allow_biometrics),
|
||||
onClick = state.onAllowClick,
|
||||
)
|
||||
|
||||
if (state.bottomSheetVariant.not()) {
|
||||
if (state.isBottomSheetVariant.not()) {
|
||||
SpacerH12()
|
||||
|
||||
SecondaryButton(
|
||||
|
|
@ -199,7 +199,7 @@ private fun Preview() {
|
|||
private fun PreviewBS() {
|
||||
TangemThemePreview {
|
||||
AskBiometry(
|
||||
state = AskBiometryUM(bottomSheetVariant = true),
|
||||
state = AskBiometryUM(isBottomSheetVariant = true),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,8 +3,8 @@ package com.tangem.features.biometry.impl.ui.state
|
|||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
internal data class AskBiometryUM(
|
||||
val bottomSheetVariant: Boolean = false,
|
||||
val showProgress: Boolean = false,
|
||||
val isBottomSheetVariant: Boolean = false,
|
||||
val shouldShowProgress: Boolean = false,
|
||||
val error: TextReference? = null,
|
||||
val onAllowClick: () -> Unit = {},
|
||||
val onDontAllowClick: () -> Unit = {},
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ dependencies {
|
|||
implementation(projects.domain.settings)
|
||||
implementation(projects.domain.wallets)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.hotWallet)
|
||||
|
||||
/** Core modules */
|
||||
implementation(projects.core.configToggles)
|
||||
|
|
|
|||
|
|
@ -2,15 +2,18 @@ package com.tangem.features.createwalletselection
|
|||
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.components.label.entity.LabelStyle
|
||||
import com.tangem.core.ui.components.label.entity.LabelUM
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.card.analytics.IntroductionProcess
|
||||
import com.tangem.domain.card.analytics.Shop
|
||||
import com.tangem.core.ui.message.dialog.Dialogs.hotWalletCreationNotSupportedDialog
|
||||
import com.tangem.domain.hotwallet.IsHotWalletCreationSupported
|
||||
import com.tangem.domain.wallets.usecase.GenerateBuyTangemCardLinkUseCase
|
||||
import com.tangem.features.createwalletselection.entity.CreateWalletSelectionUM
|
||||
import com.tangem.features.createwalletselection.impl.R
|
||||
|
|
@ -31,6 +34,8 @@ internal class CreateWalletSelectionModel @Inject constructor(
|
|||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val generateBuyTangemCardLinkUseCase: GenerateBuyTangemCardLinkUseCase,
|
||||
private val urlOpener: UrlOpener,
|
||||
private val isHotWalletCreationSupported: IsHotWalletCreationSupported,
|
||||
private val uiMessageSender: UiMessageSender,
|
||||
) : Model() {
|
||||
|
||||
internal val uiState: StateFlow<CreateWalletSelectionUM>
|
||||
|
|
@ -93,6 +98,13 @@ internal class CreateWalletSelectionModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun onMobileWalletClick() {
|
||||
if (!isHotWalletCreationSupported()) {
|
||||
uiMessageSender.send(
|
||||
hotWalletCreationNotSupportedDialog(isHotWalletCreationSupported.getLeastVersionName()),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
router.push(AppRoute.CreateMobileWallet)
|
||||
}
|
||||
|
||||
|
|
@ -101,8 +113,7 @@ internal class CreateWalletSelectionModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun onBuyClick() {
|
||||
analyticsEventHandler.send(IntroductionProcess.ButtonBuyCards)
|
||||
analyticsEventHandler.send(Shop.ScreenOpened)
|
||||
analyticsEventHandler.send(Basic.ButtonBuy(source = AnalyticsParam.ScreensSources.AddNewWallet))
|
||||
modelScope.launch {
|
||||
generateBuyTangemCardLinkUseCase.invoke().let { urlOpener.openUrl(it) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ dependencies {
|
|||
implementation(projects.domain.settings)
|
||||
implementation(projects.domain.wallets)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.hotWallet)
|
||||
|
||||
/** Core modules */
|
||||
implementation(projects.core.configToggles)
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0" ?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>BooleanPropertyNaming:CreateWalletStartUM.kt$CreateWalletStartUM$val showScanSecondaryButton: Boolean</ID>
|
||||
<ID>MultilineLambdaItParameter:CreateWalletStartContent.kt${ FeatureItem( iconResId = it.iconResId, text = it.text, ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:CreateWalletStartModel.kt$CreateWalletStartModel${ delay(HIDE_PROGRESS_DELAY) setLoading(false) when (it) { is SaveWalletError.DataError -> Timber.e(it.toString(), "Unable to save user wallet") is SaveWalletError.WalletAlreadySaved -> { userWalletsListRepository.unlock( userWalletId = userWallet.walletId, unlockMethod = UserWalletsListRepository.UnlockMethod.Scan(scanResponse), ).onRight { appRouter.replaceAll(AppRoute.Wallet) } } } }</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
|
@ -6,8 +6,7 @@ import com.tangem.common.routing.AppRoute
|
|||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic.SignedIn
|
||||
import com.tangem.core.analytics.models.Basic.SignedIn.SignInType
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.decompose.di.GlobalUiMessageSender
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
|
|
@ -18,12 +17,13 @@ import com.tangem.core.navigation.url.UrlOpener
|
|||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.DialogMessage
|
||||
import com.tangem.core.ui.message.dialog.Dialogs.hotWalletCreationNotSupportedDialog
|
||||
import com.tangem.domain.card.ScanCardProcessor
|
||||
import com.tangem.domain.card.analytics.ParamCardCurrencyConverter
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.card.analytics.IntroductionProcess
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.common.wallets.error.SaveWalletError
|
||||
import com.tangem.domain.hotwallet.IsHotWalletCreationSupported
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.domain.wallets.builder.ColdUserWalletBuilder
|
||||
|
|
@ -51,14 +51,15 @@ internal class CreateWalletStartModel @Inject constructor(
|
|||
private val scanCardProcessor: ScanCardProcessor,
|
||||
private val cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
private val settingsRepository: SettingsRepository,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val appRouter: AppRouter,
|
||||
private val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory,
|
||||
private val saveWalletUseCase: SaveWalletUseCase,
|
||||
private val isHotWalletCreationSupported: IsHotWalletCreationSupported,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
@GlobalUiMessageSender private val uiMessageSender: UiMessageSender,
|
||||
private val generateBuyTangemCardLinkUseCase: GenerateBuyTangemCardLinkUseCase,
|
||||
private val urlOpener: UrlOpener,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<CreateWalletStartComponent.Params>()
|
||||
|
|
@ -84,7 +85,7 @@ internal class CreateWalletStartModel @Inject constructor(
|
|||
),
|
||||
),
|
||||
imageResId = R.drawable.img_hardware_wallet,
|
||||
showScanSecondaryButton = true,
|
||||
shouldShowScanSecondaryButton = true,
|
||||
onPrimaryButtonClick = ::onBuyClick,
|
||||
primaryButtonText = resourceReference(R.string.details_buy_wallet),
|
||||
otherMethodTitle = resourceReference(R.string.welcome_create_wallet_mobile_title),
|
||||
|
|
@ -112,7 +113,7 @@ internal class CreateWalletStartModel @Inject constructor(
|
|||
),
|
||||
),
|
||||
imageResId = R.drawable.img_mobile_wallet,
|
||||
showScanSecondaryButton = false,
|
||||
shouldShowScanSecondaryButton = false,
|
||||
onPrimaryButtonClick = ::onStartWithMobileWalletClick,
|
||||
primaryButtonText = resourceReference(R.string.welcome_create_wallet_mobile_title),
|
||||
otherMethodTitle = resourceReference(R.string.welcome_create_wallet_use_hardware_title),
|
||||
|
|
@ -126,14 +127,25 @@ internal class CreateWalletStartModel @Inject constructor(
|
|||
)
|
||||
|
||||
private fun onScanClick() {
|
||||
analyticsEventHandler.send(
|
||||
event = IntroductionProcess.ButtonScanCard(AnalyticsParam.ScreensSources.CreateNewWallet),
|
||||
)
|
||||
scanCard()
|
||||
}
|
||||
|
||||
private fun onStartWithMobileWalletClick() {
|
||||
if (!isHotWalletCreationSupported()) {
|
||||
uiMessageSender.send(
|
||||
hotWalletCreationNotSupportedDialog(isHotWalletCreationSupported.getLeastVersionName()),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
router.push(AppRoute.CreateMobileWallet)
|
||||
}
|
||||
|
||||
private fun onBuyClick() {
|
||||
analyticsEventHandler.send(Basic.ButtonBuy(source = AnalyticsParam.ScreensSources.CreateNewWallet))
|
||||
modelScope.launch {
|
||||
generateBuyTangemCardLinkUseCase.invoke().let { urlOpener.openUrl(it) }
|
||||
}
|
||||
|
|
@ -182,11 +194,11 @@ internal class CreateWalletStartModel @Inject constructor(
|
|||
}
|
||||
|
||||
saveWalletUseCase(userWallet = userWallet).fold(
|
||||
ifLeft = {
|
||||
ifLeft = { error ->
|
||||
delay(HIDE_PROGRESS_DELAY)
|
||||
setLoading(false)
|
||||
when (it) {
|
||||
is SaveWalletError.DataError -> Timber.e(it.toString(), "Unable to save user wallet")
|
||||
when (error) {
|
||||
is SaveWalletError.DataError -> Timber.e(error.toString(), "Unable to save user wallet")
|
||||
is SaveWalletError.WalletAlreadySaved -> {
|
||||
userWalletsListRepository.unlock(
|
||||
userWalletId = userWallet.walletId,
|
||||
|
|
@ -199,28 +211,11 @@ internal class CreateWalletStartModel @Inject constructor(
|
|||
},
|
||||
ifRight = {
|
||||
setLoading(false)
|
||||
sendSignedInCardAnalyticsEvent(scanResponse = scanResponse, isImported = userWallet.isImported)
|
||||
appRouter.replaceAll(AppRoute.Wallet)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun sendSignedInCardAnalyticsEvent(scanResponse: ScanResponse, isImported: Boolean) {
|
||||
val currency = ParamCardCurrencyConverter().convert(value = scanResponse.cardTypesResolver)
|
||||
if (currency != null) {
|
||||
analyticsEventHandler.send(
|
||||
SignedIn(
|
||||
currency = currency,
|
||||
batch = scanResponse.card.batchId,
|
||||
signInType = SignInType.Card,
|
||||
walletsCount = userWalletsListRepository.userWalletsSync().size.toString(),
|
||||
isImported = isImported,
|
||||
hasBackup = scanResponse.card.backupStatus?.isActive,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setLoading(isLoading: Boolean) {
|
||||
uiState.update { it.copy(isScanInProgress = isLoading) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ internal data class CreateWalletStartUM(
|
|||
val featureItems: ImmutableList<FeatureItem>,
|
||||
val imageResId: Int,
|
||||
val isScanInProgress: Boolean,
|
||||
val showScanSecondaryButton: Boolean,
|
||||
val shouldShowScanSecondaryButton: Boolean,
|
||||
val primaryButtonText: TextReference,
|
||||
val onPrimaryButtonClick: () -> Unit,
|
||||
val otherMethodDescription: TextReference,
|
||||
|
|
|
|||
|
|
@ -120,10 +120,10 @@ internal fun CreateWalletStartContent(state: CreateWalletStartUM, modifier: Modi
|
|||
horizontalArrangement = Arrangement.Center,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
state.featureItems.forEach {
|
||||
state.featureItems.forEach { item ->
|
||||
FeatureItem(
|
||||
iconResId = it.iconResId,
|
||||
text = it.text,
|
||||
iconResId = item.iconResId,
|
||||
text = item.text,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -143,7 +143,7 @@ internal fun CreateWalletStartContent(state: CreateWalletStartUM, modifier: Modi
|
|||
)
|
||||
},
|
||||
bottomContent = {
|
||||
if (state.showScanSecondaryButton) {
|
||||
if (state.shouldShowScanSecondaryButton) {
|
||||
SecondaryButtonIconEnd(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
|
@ -232,7 +232,7 @@ internal fun CreateWalletStartContent(state: CreateWalletStartUM, modifier: Modi
|
|||
minImageHeight = 160.dp,
|
||||
)
|
||||
}
|
||||
if (!state.showScanSecondaryButton) {
|
||||
if (!state.shouldShowScanSecondaryButton) {
|
||||
FlowRow(
|
||||
modifier = Modifier
|
||||
.wrapContentWidth()
|
||||
|
|
@ -425,7 +425,7 @@ private class CreateWalletStartStateProvider : CollectionPreviewParameterProvide
|
|||
),
|
||||
),
|
||||
imageResId = R.drawable.img_hardware_wallet,
|
||||
showScanSecondaryButton = true,
|
||||
shouldShowScanSecondaryButton = true,
|
||||
onPrimaryButtonClick = { },
|
||||
primaryButtonText = resourceReference(R.string.details_buy_wallet),
|
||||
otherMethodTitle = resourceReference(R.string.welcome_create_wallet_mobile_title),
|
||||
|
|
@ -455,7 +455,7 @@ private class CreateWalletStartStateProvider : CollectionPreviewParameterProvide
|
|||
),
|
||||
),
|
||||
imageResId = R.drawable.img_mobile_wallet,
|
||||
showScanSecondaryButton = false,
|
||||
shouldShowScanSecondaryButton = false,
|
||||
onPrimaryButtonClick = { },
|
||||
primaryButtonText = resourceReference(R.string.welcome_create_wallet_mobile_title),
|
||||
otherMethodTitle = resourceReference(R.string.welcome_create_wallet_use_hardware_title),
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
<ID>MultilineLambdaItParameter:DetailsModel.kt$DetailsModel${ it.copy( selectFeedbackEmailTypeBSConfig = it.selectFeedbackEmailTypeBSConfig.copy(isShown = false), ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:PreviewUserWalletListComponent.kt$PreviewUserWalletListComponent${ it.copy( balance = UserWalletItemUM.Balance.Loaded( value = "1.000 BTC", isFlickering = true, ), ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:UserWalletSaver.kt$UserWalletSaver${ val message = it.message if (!message.isNullOrEmpty()) { messageSender.send(SnackbarMessage(message)) } }</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:UserWalletListModel.kt$UserWalletListModel$private val isWalletSavingInProgress: MutableStateFlow<Boolean> = MutableStateFlow(value = false)</ID>
|
||||
<ID>RedundantSuspendModifier:UserWalletSaver.kt$UserWalletSaver$suspend</ID>
|
||||
<ID>UnnecessaryLet:ItemsBuilder.kt$ItemsBuilder$let(::add)</ID>
|
||||
</CurrentIssues>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ import android.content.res.Resources
|
|||
import arrow.core.getOrElse
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.analytics.AppInstanceIdProvider
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
|
|
@ -13,13 +16,14 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
|||
import com.tangem.domain.card.common.TapWorkarounds.isVisa
|
||||
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
|
||||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||
import com.tangem.domain.feedback.models.WalletMetaInfo
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
import com.tangem.domain.feedback.models.WalletMetaInfo
|
||||
import com.tangem.domain.feedback.repository.FeedbackFeatureToggles
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.redux.LegacyAction
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.walletconnect.CheckIsWalletConnectAvailableUseCase
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.GenerateBuyTangemCardLinkUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.features.details.component.DetailsComponent
|
||||
|
|
@ -29,6 +33,7 @@ import com.tangem.features.details.entity.DetailsUM
|
|||
import com.tangem.features.details.entity.SelectEmailFeedbackTypeBS
|
||||
import com.tangem.features.details.utils.ItemsBuilder
|
||||
import com.tangem.features.details.utils.SocialsBuilder
|
||||
import com.tangem.features.hotwallet.HotWalletFeatureToggles
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.version.AppVersionProvider
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
@ -60,6 +65,9 @@ internal class DetailsModel @Inject constructor(
|
|||
private val getWalletsUseCase: GetWalletsUseCase,
|
||||
private val feedbackFeatureToggles: FeedbackFeatureToggles,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val generateBuyTangemCardLinkUseCase: GenerateBuyTangemCardLinkUseCase,
|
||||
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : Model() {
|
||||
|
||||
private val params: DetailsComponent.Params = paramsContainer.require()
|
||||
|
|
@ -216,7 +224,12 @@ internal class DetailsModel @Inject constructor(
|
|||
|
||||
private fun onBuyClick() {
|
||||
modelScope.launch {
|
||||
urlOpener.openUrl(buildBuyLink())
|
||||
if (hotWalletFeatureToggles.isHotWalletEnabled) {
|
||||
analyticsEventHandler.send(Basic.ButtonBuy(source = AnalyticsParam.ScreensSources.Settings))
|
||||
generateBuyTangemCardLinkUseCase.invoke().let { urlOpener.openUrl(it) }
|
||||
} else {
|
||||
urlOpener.openUrl(buildBuyLink())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,6 @@
|
|||
<ID>BooleanPropertyNaming:HomeUM.kt$HomeUM$val scanInProgress: Boolean</ID>
|
||||
<ID>MultilineLambdaItParameter:HomeModel.kt$HomeModel${ delay(HIDE_PROGRESS_DELAY) setLoading(false) when (it) { is SaveWalletError.DataError -> Timber.e(it.toString(), "Unable to save user wallet") is SaveWalletError.WalletAlreadySaved -> appRouter.replaceAll(AppRoute.Wallet) } }</ID>
|
||||
<ID>MultilineLambdaItParameter:StoriesProgressBar.kt${ when (index) { currentStep -> it.fillMaxWidth(progress.value) in 0 until currentStep -> it.fillMaxWidth(fraction = 1f) else -> it } }</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:StoriesAnimation.kt$val isFirstStepLaunched = remember { mutableStateOf(false) }</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:StoriesAnimation.kt$val isLaunched = remember { mutableStateOf(false) }</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:StoriesAnimation.kt$val isSecondStepLaunched = remember { mutableStateOf(false) }</ID>
|
||||
<ID>ReusedModifierInstance:HomeButtonsV2.kt$StoriesButton( modifier = modifier, text = stringResourceSafe(id = R.string.common_get_started), useDarkerColors = false, onClick = onGetStartedClick, )</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import com.tangem.common.routing.AppRouter
|
|||
import com.tangem.common.routing.entity.InitScreenLaunchMode
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic.SignedIn
|
||||
import com.tangem.core.analytics.models.Basic.SignedIn.SignInType
|
||||
import com.tangem.core.analytics.models.Basic.SignedInLegacy
|
||||
import com.tangem.core.analytics.models.Basic.SignedInLegacy.SignInType
|
||||
import com.tangem.core.decompose.di.GlobalUiMessageSender
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
|
|
@ -127,7 +127,7 @@ internal class HomeModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun onScanClick() {
|
||||
analyticsEventHandler.send(IntroductionProcess.ButtonScanCard)
|
||||
analyticsEventHandler.send(IntroductionProcess.ButtonScanCardLegacy)
|
||||
scanCard()
|
||||
}
|
||||
|
||||
|
|
@ -212,7 +212,7 @@ internal class HomeModel @Inject constructor(
|
|||
val currency = ParamCardCurrencyConverter().convert(value = scanResponse.cardTypesResolver)
|
||||
if (currency != null) {
|
||||
analyticsEventHandler.send(
|
||||
SignedIn(
|
||||
SignedInLegacy(
|
||||
currency = currency,
|
||||
batch = scanResponse.card.batchId,
|
||||
signInType = SignInType.Card,
|
||||
|
|
|
|||
|
|
@ -17,11 +17,8 @@
|
|||
<ID>BooleanPropertyNaming:ManualBackupCheckUM.kt$ManualBackupCheckUM$val completeButtonProgress: Boolean</ID>
|
||||
<ID>BooleanPropertyNaming:ManualBackupCheckUM.kt$ManualBackupCheckUM.WordField$val error: Boolean</ID>
|
||||
<ID>BooleanPropertyNaming:MobileWalletSetupFinishedContent.kt$var showConfetti by remember { mutableStateOf(false) }</ID>
|
||||
<ID>BooleanPropertyNaming:UpgradeWalletModel.kt$UpgradeWalletModel$val otherWalletAndAlreadyCreated by lazy { userWallet?.walletId != params.userWalletId && it.card.wallets.map { it.curve }.toSet().isNotEmpty() }</ID>
|
||||
<ID>BooleanPropertyNaming:UpgradeWalletModel.kt$UpgradeWalletModel$val sameWalletButNotFinishedBackup by lazy { userWallet?.walletId == params.userWalletId && BackupValidator.isValidFull(it.card).not() }</ID>
|
||||
<ID>BooleanPropertyNaming:WalletBackupUM.kt$WalletBackupUM$val backedUp: Boolean</ID>
|
||||
<ID>BooleanPropertyNaming:WalletHardwareBackupUM.kt$WalletHardwareBackupUM$val showPurchaseBlock: Boolean = false</ID>
|
||||
<ID>MaxChainedCallsOnSameLine:UpgradeWalletModel.kt$UpgradeWalletModel$it.card.wallets.map { it.curve }.toSet().isNotEmpty()</ID>
|
||||
<ID>MultilineLambdaItParameter:AddExistingWalletImportModel.kt$AddExistingWalletImportModel${ Timber.e(it) setImportProgress(false) }</ID>
|
||||
<ID>MultilineLambdaItParameter:AddExistingWalletImportModel.kt$AddExistingWalletImportModel${ setImportProgress(false) when (it) { is SaveWalletError.DataError -> Timber.e(it.toString(), "Unable to save user wallet") is SaveWalletError.WalletAlreadySaved -> { uiMessageSender.send( SnackbarMessage(resourceReference(R.string.hw_import_seed_phrase_already_imported)), ) } } }</ID>
|
||||
<ID>MultilineLambdaItParameter:CreateHardwareWalletModel.kt$CreateHardwareWalletModel${ delay(HIDE_PROGRESS_DELAY) setLoading(false) when (it) { is SaveWalletError.DataError -> Timber.e(it.toString(), "Unable to save user wallet") is SaveWalletError.WalletAlreadySaved -> { userWalletsListRepository.unlock( userWalletId = userWallet.walletId, unlockMethod = UserWalletsListRepository.UnlockMethod.Scan(scanResponse), ).onRight { router.replaceAll(AppRoute.Wallet) } } } }</ID>
|
||||
|
|
@ -48,7 +45,6 @@
|
|||
<ID>MultilineLambdaItParameter:ManualBackupCheckModel.kt$ManualBackupCheckModel${ it.copy( words = seedPhrasePrivateInfo.mnemonic.mnemonicComponents.filterIndexed { index, _ -> WORD_FIELD_INDICES.contains(index + 1) }.toImmutableList(), ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:ManualBackupPhraseContent.kt${ EnumeratedTwoColumnGridItem( index = it + 1, mnemonic = "word${it + 1}", ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:ManualBackupPhraseModel.kt$ManualBackupPhraseModel${ it.copy( words = seedPhrasePrivateInfo.mnemonic.mnemonicComponents.mapIndexed { index, s -> EnumeratedTwoColumnGridItem(index + 1, s) }.toImmutableList(), ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:UpgradeWalletModel.kt$UpgradeWalletModel${ // Check if user attempted to upgrade before but something went wrong and a full reset is required val userWallet = coldUserWalletBuilderFactory.create(it).build() val sameWalletButNotFinishedBackup by lazy { userWallet?.walletId == params.userWalletId && BackupValidator.isValidFull(it.card).not() } val otherWalletAndAlreadyCreated by lazy { userWallet?.walletId != params.userWalletId && it.card.wallets.map { it.curve }.toSet().isNotEmpty() } if (userWallet != null && (sameWalletButNotFinishedBackup || otherWalletAndAlreadyCreated)) { startResetCardsFlow.emit(userWallet) return@doOnSuccess } delay(DELAY_SDK_DIALOG_CLOSE) tangemSdkManager.changeDisplayedCardIdNumbersCount(it) navigateToUpgradeFlow(it) }</ID>
|
||||
<ID>MultilineLambdaItParameter:ViewPhraseContent.kt${ EnumeratedTwoColumnGridItem( index = it + 1, mnemonic = "word${it + 1}", ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:ViewPhraseModel.kt$ViewPhraseModel${ it.copy( words = words.mapIndexed { index, s -> EnumeratedTwoColumnGridItem(index + 1, s) }.toImmutableList(), ) }</ID>
|
||||
<ID>NoNameShadowing:ManualBackupCheckModel.kt$ManualBackupCheckModel${ it.copy(completeButtonProgress = false) }</ID>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.core.ui.message.DialogMessage
|
||||
import com.tangem.core.ui.message.EventMessageAction
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.hotwallet.IsAccessCodeSimpleUseCase
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.settings.CanUseBiometryUseCase
|
||||
|
|
@ -28,7 +29,6 @@ import com.tangem.hot.sdk.model.HotAuth
|
|||
import com.tangem.hot.sdk.model.HotWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
|
@ -57,6 +57,7 @@ internal class AccessCodeModel @Inject constructor(
|
|||
private val shouldShowAskBiometryUseCase: ShouldShowAskBiometryUseCase,
|
||||
private val setAskBiometryShownUseCase: SetAskBiometryShownUseCase,
|
||||
private val canUseBiometryUseCase: CanUseBiometryUseCase,
|
||||
private val isAccessCodeSimpleUseCase: IsAccessCodeSimpleUseCase,
|
||||
private val uiMessageSender: UiMessageSender,
|
||||
) : Model() {
|
||||
|
||||
|
|
@ -118,16 +119,45 @@ internal class AccessCodeModel @Inject constructor(
|
|||
modelScope.launch {
|
||||
delay(timeMillis = SUCCESS_DISPLAY_DURATION_MS)
|
||||
|
||||
params.callbacks.onNewAccessCodeInput(params.userWalletId, uiState.value.accessCode)
|
||||
|
||||
uiState.update { currentState ->
|
||||
currentState.copy(
|
||||
accessCode = "",
|
||||
)
|
||||
if (isAccessCodeSimpleUseCase(uiState.value.accessCode)) {
|
||||
showSimpleAccessCodeDialog()
|
||||
} else {
|
||||
setNewCode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setNewCode() {
|
||||
params.callbacks.onNewAccessCodeInput(params.userWalletId, uiState.value.accessCode)
|
||||
|
||||
uiState.update { currentState ->
|
||||
currentState.copy(
|
||||
accessCode = "",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSimpleAccessCodeDialog() {
|
||||
uiMessageSender.send(
|
||||
DialogMessage(
|
||||
title = resourceReference(R.string.access_code_alert_validation_title),
|
||||
message = resourceReference(R.string.access_code_alert_validation_description),
|
||||
firstAction = EventMessageAction(
|
||||
title = resourceReference(R.string.access_code_alert_validation_cancel),
|
||||
onClick = {
|
||||
uiState.update { currentState ->
|
||||
currentState.copy(onAccessCodeChange = ::onAccessCodeChange)
|
||||
}
|
||||
},
|
||||
),
|
||||
secondAction = EventMessageAction(
|
||||
title = resourceReference(R.string.access_code_alert_validation_ok),
|
||||
onClick = ::setNewCode,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun showErrorAndReset() {
|
||||
uiState.update { currentState ->
|
||||
currentState.copy(
|
||||
|
|
@ -152,11 +182,6 @@ internal class AccessCodeModel @Inject constructor(
|
|||
|
||||
tryToAskForBiometry()
|
||||
|
||||
userWalletsListRepository.saveWithoutLock(
|
||||
userWallet.copy(backedUp = true),
|
||||
canOverride = true,
|
||||
)
|
||||
|
||||
userWalletsListRepository.setLock(
|
||||
userWallet.walletId,
|
||||
UserWalletsListRepository.LockMethod.AccessCode(accessCode.toCharArray()),
|
||||
|
|
@ -179,30 +204,25 @@ internal class AccessCodeModel @Inject constructor(
|
|||
hotWalletAccessor.unlockContextual(userWallet.hotWalletId)
|
||||
}
|
||||
|
||||
launch(NonCancellable) {
|
||||
var updatedHotWalletId = tangemHotSdk.changeAuth(
|
||||
var updatedHotWalletId = tangemHotSdk.changeAuth(
|
||||
unlockHotWallet = unlockHotWallet,
|
||||
auth = HotAuth.Password(accessCode.toCharArray()),
|
||||
)
|
||||
|
||||
if (walletsRepository.requireAccessCode().not()) {
|
||||
updatedHotWalletId = tangemHotSdk.changeAuth(
|
||||
unlockHotWallet = unlockHotWallet,
|
||||
auth = HotAuth.Password(accessCode.toCharArray()),
|
||||
auth = HotAuth.Biometry,
|
||||
)
|
||||
|
||||
if (walletsRepository.requireAccessCode().not()) {
|
||||
updatedHotWalletId = tangemHotSdk.changeAuth(
|
||||
unlockHotWallet = unlockHotWallet,
|
||||
auth = HotAuth.Biometry,
|
||||
)
|
||||
}
|
||||
|
||||
userWalletsListRepository.saveWithoutLock(
|
||||
userWallet.copy(
|
||||
hotWalletId = updatedHotWalletId,
|
||||
backedUp = true,
|
||||
),
|
||||
canOverride = true,
|
||||
)
|
||||
|
||||
clearHotWalletContextualUnlockUseCase.invoke(params.userWalletId)
|
||||
}
|
||||
|
||||
userWalletsListRepository.saveWithoutLock(
|
||||
userWallet.copy(hotWalletId = updatedHotWalletId),
|
||||
canOverride = true,
|
||||
)
|
||||
|
||||
clearHotWalletContextualUnlockUseCase.invoke(params.userWalletId)
|
||||
|
||||
params.callbacks.onAccessCodeUpdated(params.userWalletId)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ internal class AddExistingWalletStepperStateManager {
|
|||
title = resourceReference(R.string.wallet_import_seed_navtitle),
|
||||
showBackButton = true,
|
||||
showSkipButton = false,
|
||||
showFeedbackButton = true,
|
||||
)
|
||||
|
||||
is AddExistingWalletRoute.BackupCompleted -> HotWalletStepperComponent.StepperUM(
|
||||
|
|
@ -24,7 +23,6 @@ internal class AddExistingWalletStepperStateManager {
|
|||
title = resourceReference(R.string.wallet_import_title),
|
||||
showBackButton = false,
|
||||
showSkipButton = false,
|
||||
showFeedbackButton = false,
|
||||
)
|
||||
|
||||
is AddExistingWalletRoute.SetAccessCode -> HotWalletStepperComponent.StepperUM(
|
||||
|
|
@ -33,7 +31,6 @@ internal class AddExistingWalletStepperStateManager {
|
|||
title = resourceReference(R.string.access_code_navtitle),
|
||||
showBackButton = false,
|
||||
showSkipButton = true,
|
||||
showFeedbackButton = false,
|
||||
)
|
||||
|
||||
is AddExistingWalletRoute.ConfirmAccessCode -> HotWalletStepperComponent.StepperUM(
|
||||
|
|
@ -42,7 +39,6 @@ internal class AddExistingWalletStepperStateManager {
|
|||
title = resourceReference(R.string.access_code_navtitle),
|
||||
showBackButton = true,
|
||||
showSkipButton = true,
|
||||
showFeedbackButton = false,
|
||||
)
|
||||
|
||||
is AddExistingWalletRoute.PushNotifications -> HotWalletStepperComponent.StepperUM(
|
||||
|
|
@ -51,7 +47,6 @@ internal class AddExistingWalletStepperStateManager {
|
|||
title = resourceReference(R.string.onboarding_title_notifications),
|
||||
showBackButton = false,
|
||||
showSkipButton = false,
|
||||
showFeedbackButton = false,
|
||||
)
|
||||
|
||||
is AddExistingWalletRoute.SetupFinished -> HotWalletStepperComponent.StepperUM(
|
||||
|
|
@ -60,7 +55,6 @@ internal class AddExistingWalletStepperStateManager {
|
|||
title = resourceReference(R.string.common_done),
|
||||
showBackButton = false,
|
||||
showSkipButton = false,
|
||||
showFeedbackButton = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,7 @@ import com.tangem.common.core.TangemSdkError
|
|||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic.SignedIn
|
||||
import com.tangem.core.analytics.models.Basic.SignedIn.SignInType
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
|
|
@ -16,8 +15,7 @@ import com.tangem.core.ui.R
|
|||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.DialogMessage
|
||||
import com.tangem.domain.card.ScanCardProcessor
|
||||
import com.tangem.domain.card.analytics.ParamCardCurrencyConverter
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.card.analytics.IntroductionProcess
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.common.wallets.error.SaveWalletError
|
||||
|
|
@ -74,12 +72,16 @@ internal class CreateHardwareWalletModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun onBuyTangemWalletClick() {
|
||||
analyticsEventHandler.send(Basic.ButtonBuy(source = AnalyticsParam.ScreensSources.CreateWallet))
|
||||
modelScope.launch {
|
||||
generateBuyTangemCardLinkUseCase.invoke().let { urlOpener.openUrl(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun onScanDeviceClick() {
|
||||
analyticsEventHandler.send(
|
||||
event = IntroductionProcess.ButtonScanCard(AnalyticsParam.ScreensSources.CreateWallet),
|
||||
)
|
||||
scanCard()
|
||||
}
|
||||
|
||||
|
|
@ -143,28 +145,11 @@ internal class CreateHardwareWalletModel @Inject constructor(
|
|||
},
|
||||
ifRight = {
|
||||
setLoading(false)
|
||||
sendSignedInCardAnalyticsEvent(scanResponse = scanResponse, isImported = userWallet.isImported)
|
||||
router.replaceAll(AppRoute.Wallet)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun sendSignedInCardAnalyticsEvent(scanResponse: ScanResponse, isImported: Boolean) {
|
||||
val currency = ParamCardCurrencyConverter().convert(value = scanResponse.cardTypesResolver)
|
||||
if (currency != null) {
|
||||
analyticsEventHandler.send(
|
||||
SignedIn(
|
||||
currency = currency,
|
||||
batch = scanResponse.card.batchId,
|
||||
signInType = SignInType.Card,
|
||||
walletsCount = userWalletsListRepository.userWalletsSync().size.toString(),
|
||||
isImported = isImported,
|
||||
hasBackup = scanResponse.card.backupStatus?.isActive,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setLoading(isLoading: Boolean) {
|
||||
uiState.update { it.copy(isScanInProgress = isLoading) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ import com.tangem.core.analytics.utils.TrackingContextProxy
|
|||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.message.dialog.Dialogs.hotWalletCreationNotSupportedDialog
|
||||
import com.tangem.domain.hotwallet.IsHotWalletCreationSupported
|
||||
import com.tangem.domain.wallets.builder.HotUserWalletBuilder
|
||||
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
|
||||
import com.tangem.domain.wallets.usecase.SyncWalletWithRemoteUseCase
|
||||
|
|
@ -32,6 +35,8 @@ internal class CreateMobileWalletModel @Inject constructor(
|
|||
private val router: Router,
|
||||
private val tangemHotSdk: TangemHotSdk,
|
||||
private val trackingContextProxy: TrackingContextProxy,
|
||||
private val isHotWalletCreationSupported: IsHotWalletCreationSupported,
|
||||
private val uiMessageSender: UiMessageSender,
|
||||
) : Model() {
|
||||
|
||||
internal val uiState: StateFlow<CreateMobileWalletUM>
|
||||
|
|
@ -54,10 +59,13 @@ internal class CreateMobileWalletModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun onImportClick() {
|
||||
checkHotWalletCreationSupported(notSupported = { return })
|
||||
router.push(AppRoute.AddExistingWallet)
|
||||
}
|
||||
|
||||
private fun onCreateClick() {
|
||||
checkHotWalletCreationSupported(notSupported = { return })
|
||||
|
||||
modelScope.launch {
|
||||
uiState.update {
|
||||
it.copy(createButtonLoading = true)
|
||||
|
|
@ -70,9 +78,10 @@ internal class CreateMobileWalletModel @Inject constructor(
|
|||
|
||||
saveUserWalletUseCase(userWallet)
|
||||
|
||||
launch(NonCancellable) {
|
||||
launch(dispatchers.main + NonCancellable) {
|
||||
syncWalletWithRemoteUseCase(userWalletId = userWallet.walletId)
|
||||
}
|
||||
|
||||
router.replaceAll(AppRoute.Wallet)
|
||||
}.onFailure { throwable ->
|
||||
Timber.e(throwable)
|
||||
|
|
@ -81,4 +90,13 @@ internal class CreateMobileWalletModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun checkHotWalletCreationSupported(notSupported: () -> Unit) {
|
||||
if (!isHotWalletCreationSupported()) {
|
||||
uiMessageSender.send(
|
||||
hotWalletCreationNotSupportedDialog(isHotWalletCreationSupported.getLeastVersionName()),
|
||||
)
|
||||
notSupported()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,9 +3,12 @@ package com.tangem.features.hotwallet.manualbackup.start.ui
|
|||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
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
|
||||
|
|
@ -22,8 +25,8 @@ import com.tangem.features.hotwallet.manualbackup.start.entity.ManualBackupStart
|
|||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
internal fun ManualBackupStartContent(state: ManualBackupStartUM, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
Box(
|
||||
modifier
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.fillMaxSize()
|
||||
.padding(
|
||||
|
|
@ -33,53 +36,57 @@ internal fun ManualBackupStartContent(state: ManualBackupStartUM, modifier: Modi
|
|||
bottom = 16.dp,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = 16.dp,
|
||||
vertical = 8.dp,
|
||||
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = 16.dp,
|
||||
vertical = 8.dp,
|
||||
),
|
||||
text = stringResourceSafe(R.string.backup_info_title),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = 16.dp,
|
||||
vertical = 8.dp,
|
||||
),
|
||||
text = stringResourceSafe(
|
||||
R.string.backup_info_description,
|
||||
state.seepPhraseLength.toString(),
|
||||
),
|
||||
text = stringResourceSafe(R.string.backup_info_title),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = 16.dp,
|
||||
vertical = 8.dp,
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
FeatureBlock(
|
||||
modifier = Modifier
|
||||
.padding(top = 24.dp),
|
||||
title = stringResourceSafe(R.string.backup_info_save_title),
|
||||
description = stringResourceSafe(
|
||||
R.string.backup_info_save_description,
|
||||
state.seepPhraseLength.toString(),
|
||||
),
|
||||
text = stringResourceSafe(
|
||||
R.string.backup_info_description,
|
||||
state.seepPhraseLength.toString(),
|
||||
),
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
FeatureBlock(
|
||||
modifier = Modifier
|
||||
.padding(top = 24.dp),
|
||||
title = stringResourceSafe(R.string.backup_info_save_title),
|
||||
description = stringResourceSafe(
|
||||
R.string.backup_info_save_description,
|
||||
state.seepPhraseLength.toString(),
|
||||
),
|
||||
iconRes = R.drawable.ic_lock_24,
|
||||
)
|
||||
FeatureBlock(
|
||||
modifier = Modifier
|
||||
.padding(top = 24.dp),
|
||||
title = stringResourceSafe(R.string.backup_info_keep_title),
|
||||
description = stringResourceSafe(R.string.backup_info_keep_description),
|
||||
iconRes = R.drawable.ic_settings_24,
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
iconRes = R.drawable.ic_lock_24,
|
||||
)
|
||||
FeatureBlock(
|
||||
modifier = Modifier
|
||||
.padding(top = 24.dp),
|
||||
title = stringResourceSafe(R.string.backup_info_keep_title),
|
||||
description = stringResourceSafe(R.string.backup_info_keep_description),
|
||||
iconRes = R.drawable.ic_settings_24,
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
}
|
||||
|
||||
PrimaryButton(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp),
|
||||
text = stringResourceSafe(R.string.common_continue),
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ interface HotWalletStepperComponent : ComposableContentComponent {
|
|||
val title: TextReference,
|
||||
val showBackButton: Boolean,
|
||||
val showSkipButton: Boolean,
|
||||
val showFeedbackButton: Boolean,
|
||||
) {
|
||||
companion object {
|
||||
fun initialState() = StepperUM(
|
||||
|
|
@ -23,7 +22,6 @@ interface HotWalletStepperComponent : ComposableContentComponent {
|
|||
title = TextReference.EMPTY,
|
||||
showBackButton = false,
|
||||
showSkipButton = false,
|
||||
showFeedbackButton = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ internal class DefaultHotWalletStepperComponent @AssistedInject constructor(
|
|||
modifier = modifier,
|
||||
onBackClick = model::onBackClick,
|
||||
onSkipClick = model::onSkipClick,
|
||||
onFeedbackClick = model::onFeedbackClick,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,9 +34,4 @@ internal class HotWalletStepperModel @Inject constructor(
|
|||
// TODO send analytics
|
||||
params.callback.onSkipClick()
|
||||
}
|
||||
|
||||
fun onFeedbackClick() {
|
||||
// TODO send analytics
|
||||
// openFeedback()
|
||||
}
|
||||
}
|
||||
|
|
@ -31,7 +31,6 @@ internal fun HotWalletStepper(
|
|||
state: HotWalletStepperComponent.StepperUM,
|
||||
onBackClick: () -> Unit,
|
||||
onSkipClick: () -> Unit,
|
||||
onFeedbackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val fraction = state.currentStep.toFloat() / state.steps.coerceAtLeast(1)
|
||||
|
|
@ -47,20 +46,16 @@ internal fun HotWalletStepper(
|
|||
} else {
|
||||
null
|
||||
},
|
||||
endButton = when {
|
||||
state.showSkipButton -> TopAppBarButtonUM.Text(
|
||||
endButton = if (state.showSkipButton) {
|
||||
TopAppBarButtonUM.Text(
|
||||
text = resourceReference(R.string.common_skip),
|
||||
onClicked = onSkipClick,
|
||||
)
|
||||
state.showFeedbackButton -> TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_chat_24,
|
||||
onClicked = onFeedbackClick,
|
||||
)
|
||||
else -> null
|
||||
} else {
|
||||
null
|
||||
},
|
||||
title = state.title,
|
||||
containerColor = TangemTheme.colors.background.primary,
|
||||
modifier = modifier,
|
||||
titleAlignment = Alignment.CenterHorizontally,
|
||||
)
|
||||
|
||||
|
|
@ -95,11 +90,9 @@ private fun HotWalletStepper_Preview() {
|
|||
title = resourceReference(R.string.common_done),
|
||||
showBackButton = true,
|
||||
showSkipButton = false,
|
||||
showFeedbackButton = true,
|
||||
),
|
||||
onBackClick = {},
|
||||
onSkipClick = {},
|
||||
onFeedbackClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import com.tangem.common.doOnResult
|
|||
import com.tangem.common.doOnSuccess
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
|
|
@ -18,7 +20,7 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.core.ui.extensions.toWrappedList
|
||||
import com.tangem.core.ui.message.DialogMessage
|
||||
import com.tangem.core.ui.message.EventMessageAction
|
||||
import com.tangem.domain.card.BackupValidator
|
||||
import com.tangem.domain.card.analytics.IntroductionProcess
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||
|
|
@ -86,12 +88,14 @@ internal class UpgradeWalletModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun onBuyTangemWalletClick() {
|
||||
analyticsEventHandler.send(Basic.ButtonBuy(source = AnalyticsParam.ScreensSources.Upgrade))
|
||||
modelScope.launch {
|
||||
generateBuyTangemCardLinkUseCase.invoke().let { urlOpener.openUrl(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun onContinueClick() {
|
||||
analyticsEventHandler.send(IntroductionProcess.ButtonScanCard(AnalyticsParam.ScreensSources.Upgrade))
|
||||
analyticsEventHandler.send(WalletSettingsAnalyticEvents.ButtonStartUpgrade)
|
||||
scanCard()
|
||||
}
|
||||
|
|
@ -131,12 +135,9 @@ internal class UpgradeWalletModel @Inject constructor(
|
|||
scanResponse: ScanResponse,
|
||||
onSuccess: suspend () -> Unit,
|
||||
) {
|
||||
// Check if user attempted to upgrade before but something went wrong and a full reset is required
|
||||
val userWallet = coldUserWalletBuilderFactory.create(scanResponse).build()
|
||||
val isSameWalletButNotFinishedBackup = userWallet?.walletId == params.userWalletId &&
|
||||
BackupValidator.isValidFull(scanResponse.card).not()
|
||||
|
||||
if (userWallet != null && isSameWalletButNotFinishedBackup) {
|
||||
if (userWallet?.walletId == params.userWalletId) {
|
||||
startResetCardsFlow.emit(userWallet)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ internal class WalletActivationStepperStateManager @Inject constructor() {
|
|||
title = resourceReference(R.string.common_backup),
|
||||
showBackButton = true,
|
||||
showSkipButton = false,
|
||||
showFeedbackButton = true,
|
||||
)
|
||||
is WalletActivationRoute.ManualBackupPhrase -> HotWalletStepperComponent.StepperUM(
|
||||
currentStep = STEP_BACKUP_PHRASE,
|
||||
|
|
@ -24,7 +23,6 @@ internal class WalletActivationStepperStateManager @Inject constructor() {
|
|||
title = resourceReference(R.string.common_backup),
|
||||
showBackButton = true,
|
||||
showSkipButton = false,
|
||||
showFeedbackButton = true,
|
||||
)
|
||||
is WalletActivationRoute.ManualBackupCheck -> HotWalletStepperComponent.StepperUM(
|
||||
currentStep = STEP_BACKUP_CHECK,
|
||||
|
|
@ -32,7 +30,6 @@ internal class WalletActivationStepperStateManager @Inject constructor() {
|
|||
title = resourceReference(R.string.common_backup),
|
||||
showBackButton = true,
|
||||
showSkipButton = false,
|
||||
showFeedbackButton = true,
|
||||
)
|
||||
is WalletActivationRoute.ManualBackupCompleted -> HotWalletStepperComponent.StepperUM(
|
||||
currentStep = STEP_BACKUP_COMPLETED,
|
||||
|
|
@ -40,7 +37,6 @@ internal class WalletActivationStepperStateManager @Inject constructor() {
|
|||
title = resourceReference(R.string.common_backup),
|
||||
showBackButton = false,
|
||||
showSkipButton = false,
|
||||
showFeedbackButton = false,
|
||||
)
|
||||
is WalletActivationRoute.SetAccessCode -> HotWalletStepperComponent.StepperUM(
|
||||
currentStep = STEP_ACCESS_CODE,
|
||||
|
|
@ -48,7 +44,6 @@ internal class WalletActivationStepperStateManager @Inject constructor() {
|
|||
title = resourceReference(R.string.access_code_navtitle),
|
||||
showBackButton = false,
|
||||
showSkipButton = true,
|
||||
showFeedbackButton = false,
|
||||
)
|
||||
is WalletActivationRoute.ConfirmAccessCode -> HotWalletStepperComponent.StepperUM(
|
||||
currentStep = STEP_ACCESS_CODE,
|
||||
|
|
@ -56,7 +51,6 @@ internal class WalletActivationStepperStateManager @Inject constructor() {
|
|||
title = resourceReference(R.string.access_code_navtitle),
|
||||
showBackButton = true,
|
||||
showSkipButton = true,
|
||||
showFeedbackButton = false,
|
||||
)
|
||||
is WalletActivationRoute.PushNotifications -> HotWalletStepperComponent.StepperUM(
|
||||
currentStep = STEP_NOTIFICATIONS,
|
||||
|
|
@ -64,7 +58,6 @@ internal class WalletActivationStepperStateManager @Inject constructor() {
|
|||
title = resourceReference(R.string.onboarding_title_notifications),
|
||||
showBackButton = false,
|
||||
showSkipButton = false,
|
||||
showFeedbackButton = false,
|
||||
)
|
||||
is WalletActivationRoute.SetupFinished -> HotWalletStepperComponent.StepperUM(
|
||||
currentStep = STEP_DONE,
|
||||
|
|
@ -72,7 +65,6 @@ internal class WalletActivationStepperStateManager @Inject constructor() {
|
|||
title = resourceReference(R.string.common_done),
|
||||
showBackButton = false,
|
||||
showSkipButton = false,
|
||||
showFeedbackButton = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.features.hotwallet.walletbackup.model
|
|||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
|
|
@ -111,6 +112,7 @@ internal class WalletBackupModel @Inject constructor(
|
|||
)
|
||||
|
||||
private fun onBuyClick() {
|
||||
analyticsEventHandler.send(Basic.ButtonBuy(source = AnalyticsParam.ScreensSources.Backup))
|
||||
modelScope.launch {
|
||||
generateBuyTangemCardLinkUseCase.invoke().let { urlOpener.openUrl(it) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import arrow.core.getOrElse
|
|||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
|
|
@ -160,6 +161,7 @@ internal class WalletHardwareBackupModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun onBuyClick() {
|
||||
analyticsEventHandler.send(Basic.ButtonBuy(source = AnalyticsParam.ScreensSources.HardwareWallet))
|
||||
modelScope.launch {
|
||||
generateBuyTangemCardLinkUseCase.invoke().let { urlOpener.openUrl(it) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@
|
|||
<ID>MultilineLambdaItParameter:AddToPortfolioBSContentUMFactory.kt$AddToPortfolioBSContentUMFactory${ if (it != selectedWalletId) { onAnotherWalletSelect(it) onWalletSelectorVisibilityChange(false) } }</ID>
|
||||
<ID>MultilineLambdaItParameter:AddToPortfolioBottomSheet.kt${ Content( modifier = Modifier.fillMaxWidth(), state = it, ) WalletSelectorBottomSheet(it.walletSelectorConfig) }</ID>
|
||||
<ID>MultilineLambdaItParameter:AddToPortfolioManager.kt$AddToPortfolioManager${ it.toMutableMap().apply { this[userWalletId] = if (isAddAction) { this[userWalletId].orEmpty() + network } else { this[userWalletId].orEmpty() - network } } }</ID>
|
||||
<ID>MultilineLambdaItParameter:AddToPortfolioModel.kt$AddToPortfolioModel${ PortfolioData.CryptoCurrencyData( userWallet = selectedPortfolio.userWallet, status = addedToken, actions = it.states, ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:AddToPortfolioModel.kt$AddToPortfolioModel${ Timber.e(it) params.callback.onDismiss() }</ID>
|
||||
<ID>MultilineLambdaItParameter:AddToPortfolioModel.kt$AddToPortfolioModel${ tokenActionsData.emit(it) navigation.replaceAll(AddToPortfolioRoutes.TokenActions) }</ID>
|
||||
<ID>MultilineLambdaItParameter:AddTokenModel.kt$AddTokenModel${ processError(error = it) uiState.value = um.toggleProgress(false) return@launch }</ID>
|
||||
|
|
@ -118,12 +117,6 @@
|
|||
<ID>NoNameShadowing:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel${ it.copy( chartState = it.chartState.copy( status = MarketsTokenDetailsUM.ChartState.Status.ERROR, ), body = if (it.body is MarketsTokenDetailsUM.Body.Error) { MarketsTokenDetailsUM.Body.Nothing } else { it.body }, ) }</ID>
|
||||
<ID>NoNameShadowing:MyPortfolioUMFactory.kt$MyPortfolioUMFactory${ networkIds.contains(it.status.currency.network.backendId) }</ID>
|
||||
<ID>NoNameShadowing:NewMarketsPortfolioDelegate.kt$NewMarketsPortfolioDelegate$portfolio</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:MarketsListBatchFlowManager.kt$MarketsListBatchFlowManager$val isInInitialLoadingErrorState = batchFlow.state .map { it.status is PaginationStatus.InitialLoadingError } .distinctUntilChanged() .stateIn( scope = modelScope, started = SharingStarted.Eagerly, initialValue = false, )</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:MarketsListBatchFlowManager.kt$MarketsListBatchFlowManager$val isSearchNotFoundState = batchFlow.state .map { currentSearchText().isNullOrEmpty().not() && it.status is PaginationStatus.EndOfPagination && it.data.isEmpty() } .distinctUntilChanged() .stateIn( scope = modelScope, started = SharingStarted.Eagerly, initialValue = false, )</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:MarketsListModel.kt$MarketsListModel$val isVisibleOnScreen = MutableStateFlow(false)</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:MarketsListUMStateManager.kt$MarketsListUMStateManager$val isInSearchStateFlow = state.map { it.searchBar.isActive }.distinctUntilChanged()</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel$val isVisibleOnScreen = MutableStateFlow(false)</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:TokenActionsHandler.kt$TokenActionsHandler$private val isDemoCardUseCase: IsDemoCardUseCase</ID>
|
||||
<ID>NullableToStringCall:MarketsListItemUM.kt$MarketsListItemUM$marketCap.toString()</ID>
|
||||
<ID>PropertyUsedBeforeDeclaration:MarketsListModel.kt$MarketsListModel$activeListManager</ID>
|
||||
<ID>PropertyUsedBeforeDeclaration:MarketsListUMStateManager.kt$MarketsListUMStateManager$state</ID>
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ internal class DefaultAddToPortfolioManager @AssistedInject constructor(
|
|||
|
||||
override val allAvailableNetworks: Flow<List<TokenMarketInfo.Network>> = _allAvailableNetworks.asSharedFlow()
|
||||
override val portfolioFetcher: PortfolioFetcher = portfolioFetcherFactory.create(
|
||||
mode = PortfolioFetcher.Mode.All(onlyMultiCurrency = true),
|
||||
mode = PortfolioFetcher.Mode.All(isOnlyMultiCurrency = true),
|
||||
scope = scope,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@
|
|||
<ID>MultilineLambdaItParameter:NFTReceiveModel.kt$NFTReceiveModel${ it.copy( bottomSheetConfig = it.bottomSheetConfig?.copy(isShown = false), ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:UpdateDataStateTransformer.kt$UpdateDataStateTransformer${ NFTCollectionUM( id = it.collectionIdProvider(), networkIconId = getActiveIconRes(it.network.rawId), name = it.name.orEmpty(), description = TextReference.PluralRes( R.plurals.nft_collections_count, it.count, wrappedList(it.count), ), logoUrl = it.logoUrl, assets = it.transformAssets(), onExpandClick = { onExpandCollectionClick(it) }, isExpanded = it.isExpanded(state), ) }</ID>
|
||||
<ID>NoNameShadowing:NFTCollectionsModel.kt$NFTCollectionsModel${ val assetsFulfillQuery = if (query.isEmpty()) { true } else { when (val assets = it.assets) { is NFTCollection.Assets.Empty, is NFTCollection.Assets.Failed, is NFTCollection.Assets.Loading, -> false is NFTCollection.Assets.Value -> { assets.items.any { asset -> asset.name?.lowercase()?.contains(query.lowercase()) == true } } } } val collectionFulfillQuery = query.isEmpty() || it.name?.lowercase()?.contains(query.lowercase()) == true collectionFulfillQuery || assetsFulfillQuery }</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:NFTCollectionsModel.kt$NFTCollectionsModel$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:NFTReceiveModel.kt$NFTReceiveModel$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
|
||||
<ID>NullableBooleanCheck:UpdateDataStateTransformer.kt$UpdateDataStateTransformer$(state.content as? NFTCollectionsUM.Content) ?.collections ?.filterIsInstance<NFTCollectionUM>() ?.firstOrNull { it.id == this.collectionIdProvider() } ?.isExpanded ?: false</ID>
|
||||
<ID>NullableToStringCall:NFTCollectionsContent.kt$${item2?.id}</ID>
|
||||
<ID>NullableToStringCall:NFTCollectionsModel.kt$NFTCollectionsModel$${network.derivationPath.value}</ID>
|
||||
|
|
|
|||
|
|
@ -107,7 +107,6 @@
|
|||
<ID>NoNameShadowing:MultiWalletCreateWalletModel.kt$MultiWalletCreateWalletModel${ it.copy(resultUserWallet = userWallet) }</ID>
|
||||
<ID>NoNameShadowing:MultiWalletUpgradeWalletModel.kt$MultiWalletUpgradeWalletModel${ it.copy(resultUserWallet = userWallet) }</ID>
|
||||
<ID>NoNameShadowing:Wallet1ChooseOptionModel.kt$Wallet1ChooseOptionModel${ it.copy(resultUserWallet = userWallet) }</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:MultiWalletSeedPhraseModel.kt$MultiWalletSeedPhraseModel$private val isWalletAlreadySavedUseCase: IsWalletAlreadySavedUseCase</ID>
|
||||
<ID>PropertyUsedBeforeDeclaration:MultiWalletCreateWalletModel.kt$MultiWalletCreateWalletModel$onDone</ID>
|
||||
<ID>RedundantSuspendModifier:MultiWalletCreateWalletModel.kt$MultiWalletCreateWalletModel$suspend</ID>
|
||||
<ID>RedundantSuspendModifier:OnboardingNoteCreateWalletModel.kt$OnboardingNoteCreateWalletModel$suspend</ID>
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ import com.tangem.sdk.api.BackupServiceHolder
|
|||
import com.tangem.sdk.api.TangemSdkManager
|
||||
import com.tangem.utils.StringsSigns
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.runSuspendCatching
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
|
@ -91,7 +92,8 @@ internal class MultiWalletFinalizeModel @Inject constructor(
|
|||
// sets proper artwork state for initial step
|
||||
// (if we start from backup cards, we need to show proper artwork) ([REDACTED_TASK_KEY])
|
||||
when (getInitialStep()) {
|
||||
MultiWalletFinalizeUM.Step.Primary -> { /* state is already set */ }
|
||||
MultiWalletFinalizeUM.Step.Primary -> { /* state is already set */
|
||||
}
|
||||
MultiWalletFinalizeUM.Step.BackupDevice1 -> {
|
||||
onEvent.emit(MultiWalletFinalizeComponent.Event.OneBackupCardAdded)
|
||||
}
|
||||
|
|
@ -299,7 +301,11 @@ internal class MultiWalletFinalizeModel @Inject constructor(
|
|||
.updateWithHotWallet(wallet),
|
||||
)
|
||||
},
|
||||
).getOrElse {
|
||||
).onRight {
|
||||
launch(NonCancellable) {
|
||||
runSuspendCatching { walletsRepository.upgradeWallet(userWalletCreated.walletId) }
|
||||
}
|
||||
}.getOrElse {
|
||||
error("Failed to upgrade to cold wallet. Error: $it")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,5 @@
|
|||
<?xml version="1.0" ?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:AvailableSwapPairsModel.kt$AvailableSwapPairsModel$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:HotCryptoModel.kt$HotCryptoModel$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:OnrampAddTokenUiBuilder.kt$OnrampAddTokenUiBuilder$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:OnrampMainComponentModel.kt$OnrampMainComponentModel$private val isDemoCardUseCase: IsDemoCardUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:OnrampOperationModel.kt$OnrampOperationModel$private val isDemoCardUseCase: IsDemoCardUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:OnrampTokenListModel.kt$OnrampTokenListModel$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SwapSelectTokensModel.kt$SwapSelectTokensModel$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
|
||||
</CurrentIssues>
|
||||
<CurrentIssues/>
|
||||
</SmellBaseline>
|
||||
|
|
|
|||
|
|
@ -48,14 +48,15 @@ internal class OnrampOperationModel @Inject constructor(
|
|||
private val messageSender: UiMessageSender,
|
||||
private val rampStateManager: RampStateManager,
|
||||
) : Model() {
|
||||
private val params: Params = paramsContainer.require()
|
||||
|
||||
private val selectedUserWallet = getWalletsUseCase.invokeSync()
|
||||
.first { it.walletId == params.userWalletId }
|
||||
private val params: Params = paramsContainer.require()
|
||||
|
||||
val state: StateFlow<OnrampOperationUM>
|
||||
field = MutableStateFlow(value = getInitialState())
|
||||
|
||||
private val selectedUserWallet = getWalletsUseCase.invokeSync()
|
||||
.first { it.walletId == params.userWalletId }
|
||||
|
||||
init {
|
||||
analyticsEventHandler.send(
|
||||
event = when (params) {
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
<?xml version="1.0" ?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>MultilineLambdaItParameter:ReferralInteractorImpl.kt$ReferralInteractorImpl${ Timber.e("Failed to derive public keys: $it") throw it.mapToDomainError() }</ID>
|
||||
<ID>ObjectExtendsThrowable:ReferralError.kt$ReferralError$SdkError : ReferralError</ID>
|
||||
<ID>ObjectExtendsThrowable:ReferralError.kt$ReferralError$UserCancelledException : ReferralError</ID>
|
||||
<ID>UselessCallOnNotNull:ReferralInteractorImpl.kt$ReferralInteractorImpl$listOfNotNull(cryptoCurrency)</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
|
@ -72,9 +72,9 @@ internal class ReferralInteractorImpl(
|
|||
manageCryptoCurrenciesUseCase(accountId = portfolioId.accountId, add = cryptoCurrency)
|
||||
}
|
||||
is PortfolioId.Wallet -> {
|
||||
derivePublicKeysUseCase(userWallet.walletId, listOfNotNull(cryptoCurrency)).getOrElse {
|
||||
Timber.e("Failed to derive public keys: $it")
|
||||
throw it.mapToDomainError()
|
||||
derivePublicKeysUseCase(userWallet.walletId, listOf(cryptoCurrency)).getOrElse { throwable ->
|
||||
Timber.e("Failed to derive public keys: $throwable")
|
||||
throw throwable.mapToDomainError()
|
||||
}
|
||||
|
||||
addCryptoCurrenciesUseCase(
|
||||
|
|
@ -118,9 +118,9 @@ internal class ReferralInteractorImpl(
|
|||
private fun Throwable.mapToDomainError(): ReferralError {
|
||||
if (this !is TangemSdkError) return ReferralError.DataError(this)
|
||||
return if (this is TangemSdkError.UserCancelled) {
|
||||
ReferralError.UserCancelledException
|
||||
ReferralError.UserCancelledException()
|
||||
} else {
|
||||
ReferralError.SdkError
|
||||
ReferralError.SdkError()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.feature.referral.domain.errors
|
||||
|
||||
sealed class ReferralError : Exception() {
|
||||
data object UserCancelledException : ReferralError()
|
||||
data object SdkError : ReferralError()
|
||||
class UserCancelledException : ReferralError()
|
||||
class SdkError : ReferralError()
|
||||
|
||||
data class DataError(val throwable: Throwable) : ReferralError()
|
||||
class DataError(val throwable: Throwable) : ReferralError()
|
||||
}
|
||||
|
|
@ -7,8 +7,6 @@
|
|||
<ID>MultilineLambdaItParameter:ReferralScreen.kt${ // TODO: use StateEvent if (stateHolder.errorSnackbar != null) { TangemSnackbar(data = it, actionOnNewLine = true) } else { CopiedTextSnackbar(it) } }</ID>
|
||||
<ID>MultilineLambdaItParameter:ReferralScreen.kt${ ReferralContent( stateHolder = stateHolder, snackbarHostState = snackbarHostState, onAgreementClick = stateHolder.analytics.onAgreementClicked, modifier = Modifier.padding(it), ) }</ID>
|
||||
<ID>NamedArguments:ReferralScreen.kt$Text( formatAwardConditionsString( quantity = award, network = networkName, address = if (!address.isNullOrBlank()) " $address" else "", ), color = TangemTheme.colors.text.tertiary, style = TangemTheme.typography.body2, modifier = Modifier.testTag(ReferralProgramScreenTestTags.INFO_FOR_YOU_TEXT), )</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:ParticipateBottomBlock.kt$val isExpanded = remember { mutableStateOf(false) }</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:ReferralModel.kt$ReferralModel$private val isDemoCardUseCase: IsDemoCardUseCase</ID>
|
||||
<ID>SuspendFunSwallowedCancellation:ReferralModel.kt$ReferralModel$runCatching</ID>
|
||||
<ID>UseOrEmpty:ReferralScreen.kt$matchResult.groups[1]?.value ?: ""</ID>
|
||||
<ID>VarCouldBeVal:ReferralModel.kt$ReferralModel$private var referralData: MutableStateFlow<ReferralData?> = MutableStateFlow(null)</ID>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
<CurrentIssues>
|
||||
<ID>BooleanPropertyNaming:FeeSelectorData.kt$FeeSelectorData$val removeSuggestedFee: Boolean = false</ID>
|
||||
<ID>BooleanPropertyNaming:SendEntryRoute.kt$SendEntryRoute.ChooseToken$val showSendViaSwapNotification: Boolean</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendDestinationComponentParams.kt$SendDestinationComponentParams.DestinationParams$val isBalanceHidingFlow: StateFlow<Boolean></ID>
|
||||
<ID>UseEmptyCounterpart:CommonSendAmountAnalyticEvents.kt$CommonSendAmountAnalyticEvents$mapOf()</ID>
|
||||
<ID>UseEmptyCounterpart:CommonSendAnalyticEvents.kt$CommonSendAnalyticEvents$mapOf()</ID>
|
||||
<ID>UseEmptyCounterpart:CommonSendFeeAnalyticEvents.kt$CommonSendFeeAnalyticEvents$mapOf()</ID>
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ class FeeCalculationUtilsTest {
|
|||
fiatAmount = BigDecimal.ZERO,
|
||||
fiatRate = BigDecimal.ZERO,
|
||||
priceChange = BigDecimal.ZERO,
|
||||
yieldBalance = null,
|
||||
stakingBalance = null,
|
||||
hasCurrentNetworkTransactions = false,
|
||||
pendingTransactions = emptySet(),
|
||||
networkAddress = mockk(relaxed = true),
|
||||
|
|
|
|||
|
|
@ -46,29 +46,6 @@
|
|||
<ID>NestedScopeFunctions:KaspaCustomFeeConverter.kt$KaspaCustomFeeConverter$let { val valueDecimal = it.value.parseToBigDecimal(it.decimals) // krc-20 transaction will be failed if custom fee value is less than minimum, // so we set value to minimum in this case if (valueDecimal < minimumFee.amount.value) { val fixedValue = minimumFeeAmountValue.parseBigDecimal(it.decimals) set( FEE_AMOUNT_INDEX, it.copy( value = fixedValue, label = getFiatReference( rate = currencyStatus.fiatRate, value = valueDecimal, appCurrency = appCurrency, ), ), ) } }</ID>
|
||||
<ID>NoNameShadowing:FeeSelectorAlertFactory.kt$FeeSelectorAlertFactory$newFee</ID>
|
||||
<ID>NoNameShadowing:SendContent.kt$navigationUM</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:FeeSelectorModel.kt$FeeSelectorModel$private val isFeeApproximateUseCase: IsFeeApproximateUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:NFTSendConfirmComponent.kt$NFTSendConfirmComponent.Params$val isBalanceHidingFlow: StateFlow<Boolean></ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:NFTSendConfirmModel.kt$NFTSendConfirmModel$private val isSendTapHelpEnabledUseCase: IsSendTapHelpEnabledUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:NFTSendModel.kt$NFTSendModel$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:NFTSendModel.kt$NFTSendModel$val isBalanceHiddenFlow: StateFlow<Boolean> field = MutableStateFlow(false)</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:NotificationsModel.kt$NotificationsModel$private val isAmountSubtractAvailableUseCase: IsAmountSubtractAvailableUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendAmountComponentParams.kt$SendAmountComponentParams$abstract val isAccountModeFlow: StateFlow<Boolean></ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendAmountComponentParams.kt$SendAmountComponentParams$abstract val isBalanceHidingFlow: StateFlow<Boolean></ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendAmountComponentParams.kt$SendAmountComponentParams.AmountBlockParams$override val isAccountModeFlow: StateFlow<Boolean></ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendAmountComponentParams.kt$SendAmountComponentParams.AmountBlockParams$override val isBalanceHidingFlow: StateFlow<Boolean></ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendAmountComponentParams.kt$SendAmountComponentParams.AmountParams$override val isAccountModeFlow: StateFlow<Boolean></ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendAmountComponentParams.kt$SendAmountComponentParams.AmountParams$override val isBalanceHidingFlow: StateFlow<Boolean></ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendAmountModel.kt$SendAmountModel$val isSendWithSwapAvailable: StateFlow<Boolean> field = MutableStateFlow(false)</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendConfirmComponent.kt$SendConfirmComponent.Params$val isAccountModeFlow: StateFlow<Boolean></ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendConfirmComponent.kt$SendConfirmComponent.Params$val isBalanceHidingFlow: StateFlow<Boolean></ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendConfirmModel.kt$SendConfirmModel$private val isAmountSubtractAvailableUseCase: IsAmountSubtractAvailableUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendConfirmModel.kt$SendConfirmModel$private val isSendTapHelpEnabledUseCase: IsSendTapHelpEnabledUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendConfirmModel.kt$SendConfirmModel$val isBalanceHiddenFlow: StateFlow<Boolean> field = MutableStateFlow(false)</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendDestinationModel.kt$SendDestinationModel$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendDestinationModel.kt$SendDestinationModel$private val isSelfSendAvailableUseCase: IsSelfSendAvailableUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendModel.kt$SendModel$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendModel.kt$SendModel$val isAccountModeFlow: StateFlow<Boolean> field = MutableStateFlow(false)</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendModel.kt$SendModel$val isBalanceHiddenFlow: StateFlow<Boolean> field = MutableStateFlow(false)</ID>
|
||||
<ID>NullCheckOnMutableProperty:SendAmountModel.kt$SendAmountModel$if (uiState.value is AmountState.Empty && userWallet != null) { val isOnlyOneWallet = getWalletsUseCase.invokeSync().size == 1 val walletTitle = if (isOnlyOneWallet) { resourceReference(R.string.send_from_title) } else { resourceReference( R.string.send_from_wallet_name, WrappedList(listOf(userWallet?.name.orEmpty())), // TODO AND-11440 ) } _uiState.update { AmountStateConverter( clickIntents = this, appCurrency = appCurrency, cryptoCurrencyStatus = cryptoCurrencyStatus, maxEnterAmount = maxAmountBoundary, iconStateConverter = CryptoCurrencyToIconStateConverter(), isBalanceHidden = params.isBalanceHidingFlow.value, accountTitleUM = AmountAccountConverter( isAccountsMode = isAccountsMode, walletTitle = walletTitle, prefixText = resourceReference(R.string.common_from), ).convert(account), ).convert( AmountParameters( title = walletTitle, value = "", ), ) } }</ID>
|
||||
<ID>NullableToStringCall:BitcoinCustomFeeConverter.kt$BitcoinCustomFeeConverter$toSatoshiPerByte( amount = feeValue, decimals = value.amount.decimals, txSize = value.txSize, ).toString()</ID>
|
||||
<ID>NullableToStringCall:NFTSendConfirmModel.kt$NFTSendConfirmModel$params.nftAsset.amount.toString()</ID>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
<ID>BooleanPropertyNaming:AddStakingNotificationsTransformer.kt$AddStakingNotificationsTransformer$val showNotification = sendingAmount + feeAmount > balance</ID>
|
||||
<ID>BooleanPropertyNaming:AmountCurrencyChangeStateTransformer.kt$AmountCurrencyChangeStateTransformer$private val value: Boolean</ID>
|
||||
<ID>BooleanPropertyNaming:StakingModel.kt$StakingModel$val noBalanceState = balanceState == null</ID>
|
||||
<ID>BooleanPropertyNaming:StakingModel.kt$StakingModel$val noYieldBalanceData = cryptoCurrencyStatus.value.yieldBalance !is YieldBalance.Data</ID>
|
||||
<ID>BooleanPropertyNaming:StakingUiState.kt$StakingStates.InitialInfoState.Data$val showBanner: Boolean</ID>
|
||||
<ID>BooleanPropertyNaming:StakingUiState.kt$StakingUiState$val showColdWalletInteractionIcon: Boolean</ID>
|
||||
<ID>CanBeNonNullable:StakingScreen.kt$bottomSheetConfig: TangemBottomSheetConfig?</ID>
|
||||
|
|
@ -28,14 +27,6 @@
|
|||
<ID>MultilineLambdaItParameter:StakingTransactionSender.kt$StakingTransactionSender${ onConstructError(it) return emptyList() }</ID>
|
||||
<ID>NoNameShadowing:StakingFeeTransactionLoader.kt$StakingFeeTransactionLoader$amount</ID>
|
||||
<ID>NoNameShadowing:StakingFeeTransactionLoader.kt$StakingFeeTransactionLoader${ if (!it.amount.isZero()) return feeResult }</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:AddStakingNotificationsTransformer.kt$AddStakingNotificationsTransformer$private val isAccountInitializedProvider: Provider<Boolean></ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:DefaultStakingDeepLinkHandler.kt$DefaultStakingDeepLinkHandler$val isStakingEnabled = getStakingAvailabilityUseCase.invokeSync( userWalletId = selectedUserWalletId, cryptoCurrency = cryptoCurrency, ).getOrNull()</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:StakingFeeTransactionLoader.kt$StakingFeeTransactionLoader$private val isFeeApproximateUseCase: IsFeeApproximateUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:StakingModel.kt$StakingModel$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:StakingModel.kt$StakingModel$private val isAmountSubtractAvailableUseCase: IsAmountSubtractAvailableUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:StakingModel.kt$StakingModel$private val isAnyTokenStakedUseCase: IsAnyTokenStakedUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:StakingModel.kt$StakingModel$private val isBalanceHiddenFlow: StateFlow<Boolean> field = MutableStateFlow(false)</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:StakingTransactionSender.kt$StakingTransactionSender$private val isFeeApproximateUseCase: IsFeeApproximateUseCase</ID>
|
||||
<ID>NullCheckOnMutableProperty:StakingModel.kt$StakingModel$if (feeCryptoCurrencyStatus != null && fee != null) { getBalanceNotEnoughForFeeWarningUseCase( fee = fee, userWalletId = userWalletId, tokenStatus = cryptoCurrencyStatus, coinStatus = feeCryptoCurrencyStatus ?: cryptoCurrencyStatus, ).getOrNull() } else { null }</ID>
|
||||
<ID>NullableToStringCall:DefaultStakingDeepLinkHandler.kt$DefaultStakingDeepLinkHandler$$networkId</ID>
|
||||
<ID>NullableToStringCall:DefaultStakingDeepLinkHandler.kt$DefaultStakingDeepLinkHandler$$tokenId</ID>
|
||||
|
|
@ -50,7 +41,6 @@
|
|||
<ID>UnnecessaryLet:StakingTosText.kt$let { onTextClick(PRIVACY_POLICY_URL) }</ID>
|
||||
<ID>UnnecessaryLet:StakingTosText.kt$let { onTextClick(TERMS_OF_USE_URL) }</ID>
|
||||
<ID>UnsafeCallOnNullableType:StakingModel.kt$StakingModel$tonAccountInitializeTransaction!!</ID>
|
||||
<ID>UseOrEmpty:StakingModel.kt$StakingModel$yieldBalance?.balance?.items ?: emptyList()</ID>
|
||||
<ID>UseOrEmpty:StakingTransactionSender.kt$StakingTransactionSender$getExplorerTransactionUrlUseCase( txHash = transactionHashes.last(), networkId = cryptoCurrencyStatus.currency.network.id, ).getOrNull() ?: ""</ID>
|
||||
<ID>VarCouldBeVal:StakingModel.kt$StakingModel$private var actionsJobHolder: JobHolder = JobHolder()</ID>
|
||||
<ID>VarCouldBeVal:StakingModel.kt$StakingModel$private var approvalJobHolder: JobHolder = JobHolder()</ID>
|
||||
|
|
|
|||
|
|
@ -190,9 +190,9 @@ internal class StakingModel @Inject constructor(
|
|||
|
||||
private val balancesToShow: List<BalanceItem>
|
||||
get() {
|
||||
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
|
||||
val stakeKitBalance = cryptoCurrencyStatus.value.stakingBalance as? StakingBalance.Data.StakeKit
|
||||
return invalidatePendingTransactionsUseCase(
|
||||
balanceItems = yieldBalance?.balance?.items ?: emptyList(),
|
||||
balanceItems = stakeKitBalance?.balance?.items.orEmpty(),
|
||||
stakingActions = stakingActions,
|
||||
token = yield.token,
|
||||
).getOrElse { emptyList() }
|
||||
|
|
@ -277,10 +277,10 @@ internal class StakingModel @Inject constructor(
|
|||
modelScope.launch {
|
||||
val isInitialInfoStep = value.currentStep == StakingStep.InitialInfo
|
||||
val noBalanceState = balanceState == null
|
||||
val noYieldBalanceData = cryptoCurrencyStatus.value.yieldBalance !is YieldBalance.Data
|
||||
val hasNoYieldBalanceData = cryptoCurrencyStatus.value.stakingBalance !is StakingBalance.Data.StakeKit
|
||||
|
||||
when {
|
||||
isInitialInfoStep && noBalanceState && yield.allValidatorsFull && noYieldBalanceData -> {
|
||||
isInitialInfoStep && noBalanceState && yield.allValidatorsFull && hasNoYieldBalanceData -> {
|
||||
stakingEventFactory.createStakingValidatorsUnavailableAlert()
|
||||
return@launch
|
||||
}
|
||||
|
|
@ -1116,7 +1116,7 @@ internal class StakingModel @Inject constructor(
|
|||
private suspend fun onDataLoaded(status: CryptoCurrencyStatus) {
|
||||
if (!isInitialInfoAnalyticSent) {
|
||||
isInitialInfoAnalyticSent = true
|
||||
val balances = status.value.yieldBalance as? YieldBalance.Data
|
||||
val balances = status.value.stakingBalance as? StakingBalance.Data.StakeKit
|
||||
paramsInterceptorHolder.addParamsInterceptor(
|
||||
interceptor = StakingParamsInterceptor(status.currency.symbol),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
|||
import com.tangem.domain.models.staking.BalanceItem
|
||||
import com.tangem.domain.models.staking.BalanceType
|
||||
import com.tangem.domain.models.staking.BalanceType.Companion.isClickable
|
||||
import com.tangem.domain.models.staking.YieldBalance
|
||||
import com.tangem.domain.models.staking.StakingBalance
|
||||
import com.tangem.domain.models.staking.action.StakingActionType
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.utils.getRewardStakingBalance
|
||||
|
|
@ -77,11 +77,11 @@ internal class BalanceItemConverter(
|
|||
val isIncludeStakingTotalBalance = BlockchainUtils.isIncludeStakingTotalBalance(
|
||||
blockchainId = cryptoCurrencyStatus.currency.network.rawId,
|
||||
)
|
||||
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
|
||||
val stakeKitBalance = cryptoCurrencyStatus.value.stakingBalance as? StakingBalance.Data.StakeKit
|
||||
return if (isIncludeStakingTotalBalance) {
|
||||
amount
|
||||
} else {
|
||||
amount - yieldBalance?.getRewardStakingBalance().orZero()
|
||||
amount - stakeKitBalance?.getRewardStakingBalance().orZero()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency
|
|||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.staking.BalanceItem
|
||||
import com.tangem.domain.models.staking.BalanceType
|
||||
import com.tangem.domain.models.staking.YieldBalance
|
||||
import com.tangem.domain.models.staking.StakingBalance
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.features.staking.impl.presentation.state.BalanceState
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
|
|
@ -24,9 +24,9 @@ internal class RewardsValidatorStateConverter(
|
|||
private val yield: Yield,
|
||||
) : Converter<Unit, StakingStates.RewardsValidatorsState> {
|
||||
override fun convert(value: Unit): StakingStates.RewardsValidatorsState {
|
||||
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance
|
||||
return if (yieldBalance is YieldBalance.Data) {
|
||||
val balances = yieldBalance.balance.items
|
||||
val stakingBalance = cryptoCurrencyStatus.value.stakingBalance
|
||||
return if (stakingBalance is StakingBalance.Data.StakeKit) {
|
||||
val balances = stakingBalance.balance.items
|
||||
StakingStates.RewardsValidatorsState.Data(
|
||||
isPrimaryButtonEnabled = true,
|
||||
rewards = balances
|
||||
|
|
@ -35,6 +35,7 @@ internal class RewardsValidatorStateConverter(
|
|||
.toPersistentList(),
|
||||
)
|
||||
} else {
|
||||
// TODO p2p
|
||||
StakingStates.RewardsValidatorsState.Empty()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
|||
import com.tangem.domain.models.staking.BalanceItem
|
||||
import com.tangem.domain.models.staking.BalanceType
|
||||
import com.tangem.domain.models.staking.RewardBlockType
|
||||
import com.tangem.domain.models.staking.YieldBalance
|
||||
import com.tangem.domain.models.staking.StakingBalance
|
||||
import com.tangem.domain.models.staking.action.StakingActionType
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.utils.getRewardStakingBalance
|
||||
|
|
@ -36,11 +36,11 @@ internal class YieldBalancesConverter(
|
|||
val appCurrency = appCurrencyProvider()
|
||||
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
|
||||
val stakeKitBalance = cryptoCurrencyStatus.value.stakingBalance as? StakingBalance.Data.StakeKit
|
||||
val balanceToShowItems = balancesToShowProvider()
|
||||
|
||||
return if (yieldBalance != null || balanceToShowItems.any { it.isPending }) {
|
||||
val cryptoRewardsValue = yieldBalance?.getRewardStakingBalance()
|
||||
return if (stakeKitBalance != null || balanceToShowItems.any { it.isPending }) {
|
||||
val cryptoRewardsValue = stakeKitBalance?.getRewardStakingBalance()
|
||||
|
||||
val fiatRate = cryptoCurrencyStatus.value.fiatRate
|
||||
val fiatRewardsValue = if (fiatRate != null && cryptoRewardsValue != null) {
|
||||
|
|
@ -49,13 +49,13 @@ internal class YieldBalancesConverter(
|
|||
null
|
||||
}
|
||||
val type = getRewardBlockType()
|
||||
val pendingRewardsConstraints = yieldBalance?.balance?.items
|
||||
val pendingRewardsConstraints = stakeKitBalance?.balance?.items
|
||||
?.firstOrNull { it.type == BalanceType.REWARDS }
|
||||
?.pendingActionsConstraints
|
||||
?.firstOrNull { it.type == StakingActionType.CLAIM_REWARDS }
|
||||
|
||||
InnerYieldBalanceState.Data(
|
||||
integrationId = yieldBalance?.stakingId?.integrationId,
|
||||
integrationId = stakeKitBalance?.stakingId?.integrationId,
|
||||
reward = YieldReward(
|
||||
rewardsCrypto = cryptoRewardsValue.format { crypto(cryptoCurrency) },
|
||||
rewardsFiat = fiatRewardsValue.format {
|
||||
|
|
@ -71,6 +71,7 @@ internal class YieldBalancesConverter(
|
|||
balances = balanceToShowItems.mapBalances(),
|
||||
)
|
||||
} else {
|
||||
// TODO p2p
|
||||
InnerYieldBalanceState.Empty
|
||||
}
|
||||
}
|
||||
|
|
@ -84,8 +85,8 @@ internal class YieldBalancesConverter(
|
|||
|
||||
private fun getRewardBlockType(): RewardBlockType {
|
||||
val blockchainId = cryptoCurrencyStatus.currency.network.rawId
|
||||
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
|
||||
val rewards = yieldBalance?.balance?.items
|
||||
val stakeKitBalance = cryptoCurrencyStatus.value.stakingBalance as? StakingBalance.Data.StakeKit
|
||||
val rewards = stakeKitBalance?.balance?.items
|
||||
?.filter { it.type == BalanceType.REWARDS && !it.amount.isZero() }
|
||||
|
||||
val isActionable = rewards?.any { it.pendingActions.isNotEmpty() } == true
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ internal class SetConfirmationStateAssentTransformer(
|
|||
isFeeApproximate = isFeeApproximate,
|
||||
),
|
||||
isPrimaryButtonEnabled = with(cryptoCurrencyStatus.value) {
|
||||
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
|
||||
sources.stakingBalanceSource.isActual() && sources.networkSource.isActual()
|
||||
},
|
||||
)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ internal class SetConfirmationStateCompletedTransformer(
|
|||
return if (this is StakingStates.ConfirmationState.Data) {
|
||||
copy(
|
||||
isPrimaryButtonEnabled = with(cryptoCurrencyStatus.value) {
|
||||
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
|
||||
sources.stakingBalanceSource.isActual() && sources.networkSource.isActual()
|
||||
},
|
||||
innerState = InnerConfirmationStakingState.COMPLETED,
|
||||
footerText = TextReference.EMPTY,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ internal class SetConfirmationStateResetAssentTransformer(
|
|||
confirmationState = if (confirmationState is StakingStates.ConfirmationState.Data) {
|
||||
confirmationState.copy(
|
||||
isPrimaryButtonEnabled = with(cryptoCurrencyStatus.value) {
|
||||
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
|
||||
sources.stakingBalanceSource.isActual() && sources.networkSource.isActual()
|
||||
},
|
||||
innerState = InnerConfirmationStakingState.ASSENT,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ internal class SetInitialDataStateTransformer(
|
|||
val status = cryptoCurrencyStatus.value
|
||||
return StakingStates.InitialInfoState.Data(
|
||||
isPrimaryButtonEnabled = with(status) {
|
||||
!amount.isNullOrZero() && sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
|
||||
!amount.isNullOrZero() && sources.stakingBalanceSource.isActual() && sources.networkSource.isActual()
|
||||
},
|
||||
showBanner = !isAnyTokenStaked && yieldBalance == InnerYieldBalanceState.Empty,
|
||||
infoItems = getInfoItems(),
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ internal class SetConfirmationStateAssentApprovalTransformer(
|
|||
isFeeApproximate = false,
|
||||
),
|
||||
isPrimaryButtonEnabled = with(cryptoCurrencyStatus.value) {
|
||||
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
|
||||
sources.stakingBalanceSource.isActual() && sources.networkSource.isActual()
|
||||
},
|
||||
isApprovalNeeded = true,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ internal class AddStakingNotificationsTransformer(
|
|||
}
|
||||
|
||||
val isActualSources = with(cryptoCurrencyStatus.value) {
|
||||
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
|
||||
sources.stakingBalanceSource.isActual() && sources.networkSource.isActual()
|
||||
}
|
||||
|
||||
return prevState.copy(
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.staking.BalanceType
|
||||
import com.tangem.domain.models.staking.YieldBalance
|
||||
import com.tangem.domain.models.staking.StakingBalance
|
||||
import com.tangem.domain.models.staking.action.StakingActionType
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
|
|
@ -134,7 +134,7 @@ internal class StakingInfoNotificationsFactory(
|
|||
private fun MutableList<NotificationUM>.addTronRevoteNotification() {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val isTron = isTron(cryptoCurrencyStatus.currency.network.rawId)
|
||||
val hasStakedBalance = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.balance
|
||||
val hasStakedBalance = (cryptoCurrencyStatus.value.stakingBalance as? StakingBalance.Data.StakeKit)?.balance
|
||||
?.items?.any {
|
||||
it.type == BalanceType.PREPARING ||
|
||||
it.type == BalanceType.STAKED ||
|
||||
|
|
|
|||
|
|
@ -32,19 +32,6 @@
|
|||
<ID>NamedArguments:SwapTransactionSender.kt$SwapTransactionSender$getSwapDataUseCase( userWallet = userWallet, fromCryptoCurrencyStatus = fromStatus, fromAmount = fromAmount.toStringWithRightOffset(fromStatus.currency.decimals), toCryptoCurrency = toStatus.currency, toAddress = destination, expressProvider = provider, rateType = rateType, expressOperationType, )</ID>
|
||||
<ID>NoNameShadowing:SendWithSwapContent.kt$navigationUM</ID>
|
||||
<ID>NoNameShadowing:SwapAmountContent.kt$amountFieldUM</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendWithSwapConfirmComponent.kt$SendWithSwapConfirmComponent.Params$val isAccountModeFlow: StateFlow<Boolean></ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendWithSwapConfirmComponent.kt$SendWithSwapConfirmComponent.Params$val isBalanceHidingFlow: StateFlow<Boolean></ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendWithSwapConfirmModel.kt$SendWithSwapConfirmModel$private val isAmountSubtractAvailableUseCase: IsAmountSubtractAvailableUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendWithSwapConfirmModel.kt$SendWithSwapConfirmModel$private val isSendTapHelpEnabledUseCase: IsSendTapHelpEnabledUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendWithSwapModel.kt$SendWithSwapModel$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendWithSwapModel.kt$SendWithSwapModel$val isAccountModeFlow: StateFlow<Boolean> field = MutableStateFlow(false)</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SendWithSwapModel.kt$SendWithSwapModel$val isBalanceHiddenFlow: StateFlow<Boolean> field = MutableStateFlow(false)</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SwapAmountComponentParams.kt$SwapAmountComponentParams$abstract val isAccountModeFlow: StateFlow<Boolean></ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SwapAmountComponentParams.kt$SwapAmountComponentParams$abstract val isBalanceHidingFlow: StateFlow<Boolean></ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SwapAmountComponentParams.kt$SwapAmountComponentParams.AmountBlockParams$override val isAccountModeFlow: StateFlow<Boolean></ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SwapAmountComponentParams.kt$SwapAmountComponentParams.AmountBlockParams$override val isBalanceHidingFlow: StateFlow<Boolean></ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SwapAmountComponentParams.kt$SwapAmountComponentParams.AmountParams$override val isAccountModeFlow: StateFlow<Boolean></ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SwapAmountComponentParams.kt$SwapAmountComponentParams.AmountParams$override val isBalanceHidingFlow: StateFlow<Boolean></ID>
|
||||
<ID>NullableToStringCall:SendWithSwapConfirmModel.kt$SendWithSwapConfirmModel$error.toString()</ID>
|
||||
<ID>NullableToStringCall:SwapAmountModel.kt$SwapAmountModel$$primaryStatus</ID>
|
||||
<ID>NullableToStringCall:SwapAmountModel.kt$SwapAmountModel$$secondaryStatus</ID>
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ dependencies {
|
|||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.legacy)
|
||||
implementation(projects.domain.walletManager)
|
||||
implementation(projects.libs.blockchainSdk)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.wallets)
|
||||
implementation(projects.domain.wallets.models)
|
||||
|
|
@ -45,6 +44,9 @@ dependencies {
|
|||
implementation(projects.domain.express.models)
|
||||
implementation(projects.domain.account.status)
|
||||
|
||||
implementation(projects.libs.blockchainSdk)
|
||||
implementation(projects.libs.crypto)
|
||||
|
||||
/** Data */
|
||||
implementation(projects.data.common)
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import com.tangem.feature.swap.domain.models.domain.ExchangeStatusModel
|
|||
import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionListModel
|
||||
import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionListModelInner
|
||||
import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionModel
|
||||
import com.tangem.lib.crypto.derivation.AccountNodeRecognizer
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class SavedSwapTransactionListConverter(
|
||||
|
|
@ -75,9 +76,23 @@ internal class SavedSwapTransactionListConverter(
|
|||
.map { tx ->
|
||||
val status = txStatuses[tx.txId]
|
||||
val refundCurrency = status?.refundTokensResponse?.let { id ->
|
||||
val blockchain = Blockchain.fromNetworkId(id.networkId) ?: return@let null
|
||||
val derivationPath = id.derivationPath ?: return@let null
|
||||
|
||||
val accountIndex = if (blockchain == Blockchain.Chia) {
|
||||
DerivationIndex.Main
|
||||
} else {
|
||||
val recognizer = AccountNodeRecognizer(blockchain = blockchain)
|
||||
val index = recognizer.recognize(derivationPathValue = derivationPath)?.toInt()
|
||||
?: return@let null
|
||||
|
||||
DerivationIndex(index).getOrNull() ?: return@let null
|
||||
}
|
||||
|
||||
responseCryptoCurrenciesFactory.createCurrency(
|
||||
responseToken = id,
|
||||
userWallet = userWallet,
|
||||
accountIndex = accountIndex,
|
||||
)
|
||||
}
|
||||
val statusWithRefundCurrency = status?.copy(refundCurrency = refundCurrency)
|
||||
|
|
|
|||
|
|
@ -25,8 +25,6 @@
|
|||
<ID>NamedArguments:SwapInteractorImpl.kt$SwapInteractorImpl$tryGetFromCacheV2(userWallet, initialCryptoCurrency, state, isReverseFromTo)</ID>
|
||||
<ID>NoNameShadowing:SwapInteractorImpl.kt$SwapInteractorImpl$account</ID>
|
||||
<ID>NoNameShadowing:SwapInteractorImpl.kt$SwapInteractorImpl${ it.isAvailable }</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SwapInteractorImpl.kt$SwapInteractorImpl$private val isDemoCardUseCase: IsDemoCardUseCase</ID>
|
||||
<ID>NullableToStringCall:SwapInteractorImpl.kt$SwapInteractorImpl$$swapData</ID>
|
||||
<ID>NullableToStringCall:SwapInteractorImpl.kt$SwapInteractorImpl$${e.message}</ID>
|
||||
<ID>SuspendFunSwallowedCancellation:SwapInteractorImpl.kt$SwapInteractorImpl$runCatching</ID>
|
||||
</CurrentIssues>
|
||||
|
|
|
|||
|
|
@ -48,8 +48,6 @@
|
|||
<ID>MultilineLambdaItParameter:SwapModel.kt$SwapModel${ uiState = stateBuilder.dismissBottomSheet(uiState) dataState = dataState.copy(selectedFee = it) modelScope.launch(dispatchers.io) { startLoadingQuotesFromLastState(false) } }</ID>
|
||||
<ID>MultilineLambdaItParameter:SwapModel.kt$SwapModel${ val balance = swapInteractor.getTokenBalance(it) onAmountChanged(balance.formatToUIRepresentation()) }</ID>
|
||||
<ID>MultilineLambdaItParameter:SwapModel.kt$SwapModel${ val provider = findAndSelectProvider(it) val swapState = dataState.lastLoadedSwapStates[provider] val fromToken = dataState.fromCryptoCurrency if (provider != null && swapState != null && fromToken != null) { analyticsEventHandler.send(SwapEvents.ProviderChosen(provider)) uiState = stateBuilder.dismissBottomSheet(uiState) setupLoadedState( provider = provider, state = swapState, fromToken = fromToken, ) } }</ID>
|
||||
<ID>MultilineLambdaItParameter:SwapModel.kt$SwapModel${ when (it) { is SwapTransactionState.TxSent -> { sendApproveSuccessEvent(fromToken, feeForPermission.feeType, approveType) updateWalletBalance() uiState = stateBuilder.loadingPermissionState(uiState) uiState = stateBuilder.dismissBottomSheet(uiState) startLoadingQuotesFromLastState(isSilent = true) } is SwapTransactionState.Error -> { uiState = stateBuilder.createErrorTransactionAlert( uiState = uiState, error = it, onDismiss = { uiState = stateBuilder.clearAlert(uiState) }, onSupportClick = ::onFailedTxEmailClick, isReverseSwapPossible = isReverseSwapPossible(), ) } SwapTransactionState.DemoMode -> { uiState = stateBuilder.createDemoModeAlert( uiState = uiState, onDismiss = { uiState = stateBuilder.clearAlert(uiState) }, isReverseSwapPossible = isReverseSwapPossible(), ) } } }</ID>
|
||||
<ID>MultilineLambdaItParameter:SwapModel.kt$SwapModel${ when (it) { is SwapTransactionState.TxSent -> { sendSuccessSwapEvent(fromCurrency.currency, fee.feeType) val url = getExplorerTransactionUrlUseCase( txHash = it.txHash, networkId = fromCurrency.currency.network.id, ).getOrElse { Timber.i("tx hash explore not supported") "" } updateWalletBalance() uiState = stateBuilder.createSuccessState( uiState = uiState, swapTransactionState = it, dataState = dataState, txUrl = url, onExploreClick = { if (it.txHash.isNotEmpty()) { urlOpener.openUrl(url) } analyticsEventHandler.send( event = SwapEvents.ButtonExplore(initialCurrencyFrom.symbol), ) }, onStatusClick = { val txExternalUrl = it.txExternalUrl if (!txExternalUrl.isNullOrBlank()) { urlOpener.openUrl(txExternalUrl) analyticsEventHandler.send( event = SwapEvents.ButtonStatus(initialCurrencyFrom.symbol), ) } }, ) sendSuccessEvent() swapRouter.openScreen(SwapNavScreen.Success) } SwapTransactionState.DemoMode -> { uiState = stateBuilder.createDemoModeAlert( uiState = uiState, onDismiss = { uiState = stateBuilder.clearAlert(uiState) }, isReverseSwapPossible = isReverseSwapPossible(), ) } is SwapTransactionState.Error -> { startLoadingQuotesFromLastState() uiState = stateBuilder.createErrorTransactionAlert( uiState = uiState, error = it, onDismiss = { uiState = stateBuilder.clearAlert(uiState) }, onSupportClick = ::onFailedTxEmailClick, isReverseSwapPossible = isReverseSwapPossible(), ) } } }</ID>
|
||||
<ID>MultilineLambdaItParameter:TransactionCard.kt${ Text( text = it, color = TangemTheme.colors.text.tertiary, style = TangemTheme.typography.body2, ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:TransactionCard.kt${ Text( text = it, color = TangemTheme.colors.text.tertiary, style = TangemTheme.typography.body2, modifier = Modifier .align(Alignment.CenterVertically) .testTag(SwapTokenScreenTestTags.BALANCE), ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:TransactionCard.kt${ Text( text = it, color = TangemTheme.colors.text.tertiary, style = TangemTheme.typography.body2, modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size20), ) }</ID>
|
||||
|
|
@ -57,10 +55,6 @@
|
|||
<ID>NoNameShadowing:SwapModel.kt$SwapModel${ it.cryptoCurrencyStatus }</ID>
|
||||
<ID>NoNameShadowing:SwapModel.kt$SwapModel${ it.cryptoCurrencyStatus.currency.id.value == id }</ID>
|
||||
<ID>NoNameShadowing:SwapModel.kt$SwapModel${ it.key == selectedSwapProvider }</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:StateBuilder.kt$StateBuilder$private val isAccountsModeProvider: Provider<Boolean></ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:StateBuilder.kt$StateBuilder$private val isBalanceHiddenProvider: Provider<Boolean></ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:SwapModel.kt$SwapModel$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:TokensDataConverter.kt$TokensDataConverter$private val isBalanceHiddenProvider: Provider<Boolean></ID>
|
||||
<ID>NullableToStringCall:SwapModel.kt$SwapModel$${currencyStatus.value.amount}</ID>
|
||||
<ID>NullableToStringCall:SwapModel.kt$SwapModel$${it.value.amount}</ID>
|
||||
<ID>NullableToStringCall:TransactionCard.kt$data.toString()</ID>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
<ID>MultilineLambdaItParameter:TangemPayDetailsScreen.kt${ TangemDropdownItem( item = it.dropdownItem, dismissParent = { showDropdownMenu = false }, ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:TangemPayTxHistoryUiManager.kt$TangemPayTxHistoryUiManager${ it.status !is PaginationStatus.None && it.status !is PaginationStatus.InitialLoading && it.status !is PaginationStatus.InitialLoadingError }</ID>
|
||||
<ID>NullCheckOnMutableProperty:GoogleWalletUtil.kt$GoogleWalletUtil$if (walletIntent != null) { walletIntent } else { try { context.packageManager.getLaunchIntentForPackage(WALLET_PACKAGE_NAME) ?.apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) } .also { walletIntent = it } } catch (exception: Exception) { Timber.tag(TAG).e(exception) null } }</ID>
|
||||
<ID>NullableBooleanCheck:TangemPayDetailsModel.kt$TangemPayDetailsModel$cardDetailsRepository.isAddToWalletDone().getOrNull() ?: false</ID>
|
||||
<ID>ReusedModifierInstance:DefaultTangemPayDetailsContainerComponent.kt$DefaultTangemPayDetailsContainerComponent$Content(modifier = modifier)</ID>
|
||||
<ID>ReusedModifierInstance:TangemPayChangePinCodeSuccessScreen.kt$Column( modifier .fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, ) { SuccessContent( modifier = Modifier .fillMaxWidth() .weight(1f), ) PrimaryButton( modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp) .padding(bottom = 16.dp) .navigationBarsPadding(), text = stringResourceSafe(R.string.common_done), onClick = onClick, ) }</ID>
|
||||
<ID>ReusedModifierInstance:TangemPayChangePinScreen.kt$Column( modifier = modifier .fillMaxWidth() .padding(top = 48.dp) .padding(horizontal = 36.dp) .weight(1f), horizontalAlignment = Alignment.CenterHorizontally, ) { Text( text = stringResourceSafe(R.string.visa_onboarding_pin_code_title), style = TangemTheme.typography.h2, color = TangemTheme.colors.text.primary1, textAlign = TextAlign.Center, ) SpacerH16() Text( text = stringResourceSafe(R.string.visa_onboarding_pin_code_description), style = TangemTheme.typography.body1, color = TangemTheme.colors.text.secondary, textAlign = TextAlign.Center, ) SpacerH(26.dp) PinCodeSection(state) }</ID>
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0" ?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>MultilineLambdaItParameter:TangemPayOnboardingModel.kt$TangemPayOnboardingModel${ TangemPayOnboardingScreenState.Content( onBack = it.onBack, onTermsClick = ::onTermsClick, buttonConfig = TangemPayOnboardingScreenState.Content.ButtonConfig( isLoading = false, onClick = ::onGetCardClick, ), ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:TangemPayOnboardingModel.kt$TangemPayOnboardingModel${ Timber.e("Error getCustomerInfo: ${it.errorCode}") uiState.transformerUpdate(TangemPayOnboardingButtonLoadingTransformer(isLoading = false)) }</ID>
|
||||
<ID>NullableToStringCall:TangemPayOnboardingModel.kt$TangemPayOnboardingModel$${result.leftOrNull()?.message}</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
|
@ -66,9 +66,9 @@ internal class TangemPayOnboardingModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun showOnboarding() {
|
||||
uiState.update {
|
||||
uiState.update { state ->
|
||||
TangemPayOnboardingScreenState.Content(
|
||||
onBack = it.onBack,
|
||||
onBack = state.onBack,
|
||||
onTermsClick = ::onTermsClick,
|
||||
buttonConfig = TangemPayOnboardingScreenState.Content.ButtonConfig(
|
||||
isLoading = false,
|
||||
|
|
@ -122,6 +122,7 @@ internal class TangemPayOnboardingModel @Inject constructor(
|
|||
urlOpener.openUrl(TangemPayConstants.TERMS_AND_LIMITS_LINK)
|
||||
}
|
||||
|
||||
@Suppress("NullableToStringCall")
|
||||
private fun onGetCardClick() {
|
||||
analytics.send(TangemPayAnalyticsEvents.GetCardClicked)
|
||||
uiState.transformerUpdate(TangemPayOnboardingButtonLoadingTransformer(isLoading = true))
|
||||
|
|
@ -139,8 +140,8 @@ internal class TangemPayOnboardingModel @Inject constructor(
|
|||
repository.getCustomerInfo(
|
||||
userWalletId = userWalletId,
|
||||
).fold(
|
||||
ifLeft = {
|
||||
Timber.e("Error getCustomerInfo: ${it.errorCode}")
|
||||
ifLeft = { error ->
|
||||
Timber.e("Error getCustomerInfo: ${error.errorCode}")
|
||||
uiState.transformerUpdate(TangemPayOnboardingButtonLoadingTransformer(isLoading = false))
|
||||
},
|
||||
ifRight = { customerInfo ->
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
<ID>ExplicitCollectionElementAccessMethod:TestPushAddKeyDataTransformer.kt$TestPushAddKeyDataTransformer$mutableData.set(index = index, updated)</ID>
|
||||
<ID>ExplicitCollectionElementAccessMethod:TestPushAddValueDataTransformer.kt$TestPushAddValueDataTransformer$mutableData.set(index = index, updated)</ID>
|
||||
<ID>MaxChainedCallsOnSameLine:BlockchainProvidersScreen.kt$ProvidersDnDTarget$event.toAndroidDragEvent().clipData.getItemAt(0).text.toString().toInt()</ID>
|
||||
<ID>MultilineLambdaItParameter:ApiEnvironmentComparator.kt$ApiEnvironmentComparator${ when (it) { ApiEnvironment.DEV -> 0 ApiEnvironment.DEV_2 -> 1 ApiEnvironment.DEV_3 -> 2 ApiEnvironment.STAGE -> 3 ApiEnvironment.MOCK -> 4 ApiEnvironment.PROD -> 5 } }</ID>
|
||||
<ID>MultilineLambdaItParameter:BlockchainProvidersScreen.kt${ value = it state.onValueChange(it.text) }</ID>
|
||||
<ID>MultilineLambdaItParameter:BlockchainProvidersViewModel.kt$BlockchainProvidersViewModel${ if (it.blockchainId == blockchainId) { it.update() } else { it } }</ID>
|
||||
<ID>MultilineLambdaItParameter:BlockchainProvidersViewModel.kt$BlockchainProvidersViewModel${ it.copyProvidersUM(blockchainId = id) { copy( addPublicProviderDialog = addPublicProviderDialog.copy( hasError = !PatternsCompat.WEB_URL.matcher(url).matches(), ), ) } }</ID>
|
||||
|
|
|
|||
|
|
@ -13,11 +13,40 @@ internal data class AccountsUM(
|
|||
) {
|
||||
|
||||
data class Button(
|
||||
val title: String,
|
||||
val id: ID,
|
||||
val title: String = createTitle(id),
|
||||
val isInProgress: Boolean = false,
|
||||
val isEnabled: Boolean = true,
|
||||
val onClick: (title: String) -> Unit,
|
||||
)
|
||||
val onClick: () -> Unit,
|
||||
) {
|
||||
|
||||
enum class ID {
|
||||
ShowAccountList,
|
||||
FetchAccounts,
|
||||
FillOutList,
|
||||
FillOutArchivedList,
|
||||
ArchiveAll,
|
||||
SortByDerivationIndex,
|
||||
ClearETag,
|
||||
}
|
||||
|
||||
fun reset(): Button {
|
||||
return copy(title = createTitle(id), isInProgress = false, isEnabled = true)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private fun createTitle(id: ID): String = when (id) {
|
||||
ID.ShowAccountList -> "Show the account list"
|
||||
ID.FetchAccounts -> "Fetch accounts"
|
||||
ID.FillOutList -> "Fill out the list (up to 20)"
|
||||
ID.FillOutArchivedList -> "Fill out the archived list"
|
||||
ID.ArchiveAll -> "Archive all"
|
||||
ID.SortByDerivationIndex -> "Sort by derivation index"
|
||||
ID.ClearETag -> "Clear ETag"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class WalletSelector(
|
||||
val selected: UserWallet?,
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ private fun LazyListScope.ManageAccountsButtons(buttons: ImmutableList<AccountsU
|
|||
items(buttons) { button ->
|
||||
PrimaryButton(
|
||||
text = button.title,
|
||||
onClick = { button.onClick(button.title) },
|
||||
onClick = button.onClick,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||
.fillMaxWidth(),
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ internal class TesterAccountsViewModel @Inject constructor(
|
|||
|
||||
userWallets.firstOrNull()?.let(::onWalletSelect)
|
||||
|
||||
userWallets.mapIndexed { index, userWallet ->
|
||||
userWallets.map { userWallet ->
|
||||
singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId = userWallet.walletId))
|
||||
.distinctUntilChanged()
|
||||
.onEach { accountList ->
|
||||
|
|
@ -102,12 +102,16 @@ internal class TesterAccountsViewModel @Inject constructor(
|
|||
},
|
||||
),
|
||||
buttons = persistentListOf(
|
||||
AccountsUM.Button(title = "Show the account list") { updateAccountsList() },
|
||||
AccountsUM.Button(title = "Fetch accounts", onClick = ::fetchAccounts),
|
||||
AccountsUM.Button(title = "Fill out the list (up to 20)", onClick = ::fillOutAccountList),
|
||||
AccountsUM.Button(title = "Archive all", onClick = ::archiveAllAccounts),
|
||||
AccountsUM.Button(title = "Sort by derivation index", onClick = ::sortAccountsByIndex),
|
||||
AccountsUM.Button(title = "Clear ETag") { clearETag() },
|
||||
AccountsUM.Button(id = AccountsUM.Button.ID.ShowAccountList, onClick = ::updateAccountsList),
|
||||
AccountsUM.Button(id = AccountsUM.Button.ID.FetchAccounts, onClick = ::fetchAccounts),
|
||||
AccountsUM.Button(id = AccountsUM.Button.ID.FillOutList, onClick = ::onFillOutClick),
|
||||
AccountsUM.Button(
|
||||
id = AccountsUM.Button.ID.FillOutArchivedList,
|
||||
onClick = ::fillOutArchivedAccountList,
|
||||
),
|
||||
AccountsUM.Button(id = AccountsUM.Button.ID.ArchiveAll, onClick = ::onArchiveAllClick),
|
||||
AccountsUM.Button(id = AccountsUM.Button.ID.SortByDerivationIndex, onClick = ::sortAccountsByIndex),
|
||||
AccountsUM.Button(id = AccountsUM.Button.ID.ClearETag) { clearETag() },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -137,19 +141,15 @@ internal class TesterAccountsViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun fetchAccounts(title: String) {
|
||||
viewModelScope.launch {
|
||||
val userWalletId = uiState.value.walletSelector.selected?.walletId ?: return@launch
|
||||
|
||||
toggleButtonProgress(title = title, isInProgress = true)
|
||||
private fun fetchAccounts() {
|
||||
val userWalletId = uiState.value.walletSelector.selected?.walletId ?: return
|
||||
|
||||
withProgress(id = AccountsUM.Button.ID.FetchAccounts) {
|
||||
withContext(dispatchers.default) {
|
||||
singleAccountListFetcher(
|
||||
params = SingleAccountListFetcher.Params(userWalletId = userWalletId),
|
||||
)
|
||||
}
|
||||
|
||||
toggleButtonProgress(title = title, isInProgress = false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -160,84 +160,113 @@ internal class TesterAccountsViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun fillOutAccountList(title: String) {
|
||||
private fun onFillOutClick() {
|
||||
val userWalletId = getUserWallet()?.walletId ?: return
|
||||
|
||||
viewModelScope.launch {
|
||||
toggleButtonProgress(title = title, isInProgress = true)
|
||||
withProgress(id = AccountsUM.Button.ID.FillOutList) {
|
||||
fillOutAccountList(userWalletId = userWalletId)
|
||||
}
|
||||
}
|
||||
|
||||
withContext(dispatchers.default) {
|
||||
var accountList = walletAccounts.value[userWalletId] ?: return@withContext
|
||||
val occupiedIndexes = accountList.accounts
|
||||
.filterIsInstance<Account.CryptoPortfolio>()
|
||||
.map { it.derivationIndex.value }
|
||||
.toSet()
|
||||
private fun fillOutArchivedAccountList() {
|
||||
val userWalletId = getUserWallet()?.walletId ?: return
|
||||
val accountList = walletAccounts.value[userWalletId] ?: return
|
||||
if (accountList.totalArchivedAccounts == AccountList.MAX_ARCHIVED_ACCOUNTS_COUNT) return
|
||||
|
||||
var nextIndex = 0
|
||||
@Suppress("LoopWithTooManyJumpStatements") // never mind for Tester Menu
|
||||
while (accountList.canAddMoreAccounts) {
|
||||
while (occupiedIndexes.contains(nextIndex)) {
|
||||
nextIndex++
|
||||
}
|
||||
val id = AccountsUM.Button.ID.FillOutArchivedList
|
||||
|
||||
val derivationIndex = DerivationIndex(nextIndex).getOrNull() ?: break
|
||||
viewModelScope.launch(dispatchers.default) {
|
||||
var currentTotalArchived = accountList.totalArchivedAccounts
|
||||
|
||||
val newAccount = Account.CryptoPortfolio.invoke(
|
||||
accountId = AccountId.forCryptoPortfolio(userWalletId, derivationIndex),
|
||||
name = "Account #$nextIndex",
|
||||
icon = CryptoPortfolioIcon.ofDefaultCustomAccount(),
|
||||
derivationIndex = nextIndex,
|
||||
cryptoCurrencies = emptySet(),
|
||||
while (currentTotalArchived < AccountList.MAX_ARCHIVED_ACCOUNTS_COUNT) {
|
||||
updateButton(id) { button ->
|
||||
button.copy(
|
||||
title = "In progress... ($currentTotalArchived/${AccountList.MAX_ARCHIVED_ACCOUNTS_COUNT})",
|
||||
isEnabled = false,
|
||||
)
|
||||
.getOrNull()
|
||||
?: break
|
||||
|
||||
(accountList + newAccount)
|
||||
.onRight { accountList = it }
|
||||
.getOrNull()
|
||||
?: break
|
||||
|
||||
nextIndex++
|
||||
}
|
||||
|
||||
accountsCRUDRepository.saveAccounts(accountList)
|
||||
fillOutAccountList(userWalletId = userWalletId)
|
||||
archiveAll(userWalletId = userWalletId)
|
||||
|
||||
accountsCRUDRepository.getAccountListSync(userWalletId).onSome {
|
||||
currentTotalArchived = it.totalArchivedAccounts
|
||||
}
|
||||
}
|
||||
|
||||
toggleButtonProgress(title = title, isInProgress = false)
|
||||
updateButton(id = id, button = AccountsUM.Button::reset)
|
||||
}
|
||||
}
|
||||
|
||||
private fun archiveAllAccounts(title: String) {
|
||||
val userWalletId = getUserWallet()?.walletId ?: return
|
||||
val accountList = walletAccounts.value[userWalletId] ?: return
|
||||
private suspend fun fillOutAccountList(userWalletId: UserWalletId) {
|
||||
withContext(dispatchers.default) {
|
||||
var accountList = walletAccounts.value[userWalletId] ?: return@withContext
|
||||
|
||||
viewModelScope.launch {
|
||||
toggleButtonProgress(title = title, isInProgress = true)
|
||||
var nextIndex = accountList.totalAccounts
|
||||
@Suppress("LoopWithTooManyJumpStatements") // never mind for Tester Menu
|
||||
while (accountList.canAddMoreAccounts) {
|
||||
val derivationIndex = DerivationIndex(nextIndex).getOrNull() ?: break
|
||||
|
||||
withContext(dispatchers.default) {
|
||||
val updatedAccountList = AccountList.invoke(
|
||||
userWalletId = accountList.userWalletId,
|
||||
accounts = listOf(accountList.mainAccount),
|
||||
totalAccounts = accountList.totalAccounts,
|
||||
sortType = accountList.sortType,
|
||||
groupType = accountList.groupType,
|
||||
val newAccount = Account.CryptoPortfolio.invoke(
|
||||
accountId = AccountId.forCryptoPortfolio(userWalletId, derivationIndex),
|
||||
name = "Account #$nextIndex",
|
||||
icon = CryptoPortfolioIcon.ofDefaultCustomAccount(),
|
||||
derivationIndex = nextIndex,
|
||||
cryptoCurrencies = emptySet(),
|
||||
)
|
||||
.getOrElse { return@withContext }
|
||||
.getOrNull()
|
||||
?: break
|
||||
|
||||
accountsCRUDRepository.saveAccounts(accountList = updatedAccountList)
|
||||
(accountList + newAccount)
|
||||
.onRight { accountList = it }
|
||||
.getOrNull()
|
||||
?: break
|
||||
|
||||
nextIndex++
|
||||
}
|
||||
|
||||
toggleButtonProgress(title = title, isInProgress = false)
|
||||
accountsCRUDRepository.saveAccounts(accountList)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sortAccountsByIndex(title: String) {
|
||||
private fun onArchiveAllClick() {
|
||||
val userWalletId = getUserWallet()?.walletId ?: return
|
||||
|
||||
withProgress(id = AccountsUM.Button.ID.ArchiveAll) {
|
||||
archiveAll(userWalletId = userWalletId)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun archiveAll(userWalletId: UserWalletId) {
|
||||
val accountList = walletAccounts.value[userWalletId] ?: return
|
||||
val possibleToArchive = AccountList.MAX_ARCHIVED_ACCOUNTS_COUNT - accountList.totalArchivedAccounts
|
||||
|
||||
if (possibleToArchive == 0) return
|
||||
|
||||
withContext(dispatchers.default) {
|
||||
val updatedAccountList = AccountList.invoke(
|
||||
userWalletId = accountList.userWalletId,
|
||||
accounts = if (possibleToArchive > AccountList.MAX_ACCOUNTS_COUNT - 1) {
|
||||
listOf(accountList.mainAccount)
|
||||
} else {
|
||||
accountList.accounts.subList(fromIndex = 0, toIndex = accountList.accounts.size - possibleToArchive)
|
||||
},
|
||||
totalAccounts = accountList.totalAccounts,
|
||||
totalArchivedAccounts = accountList.totalArchivedAccounts,
|
||||
sortType = accountList.sortType,
|
||||
groupType = accountList.groupType,
|
||||
)
|
||||
.getOrElse { return@withContext }
|
||||
|
||||
accountsCRUDRepository.saveAccounts(accountList = updatedAccountList)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sortAccountsByIndex() {
|
||||
val userWalletId = getUserWallet()?.walletId ?: return
|
||||
val accountList = walletAccounts.value[userWalletId] ?: return
|
||||
|
||||
viewModelScope.launch {
|
||||
toggleButtonProgress(title = title, isInProgress = true)
|
||||
|
||||
withProgress(id = AccountsUM.Button.ID.SortByDerivationIndex) {
|
||||
withContext(dispatchers.default) {
|
||||
val sortedAccountList = AccountList.invoke(
|
||||
userWalletId = accountList.userWalletId,
|
||||
|
|
@ -245,6 +274,7 @@ internal class TesterAccountsViewModel @Inject constructor(
|
|||
.filterIsInstance<Account.CryptoPortfolio>()
|
||||
.sortedBy { it.derivationIndex.value },
|
||||
totalAccounts = accountList.totalAccounts,
|
||||
totalArchivedAccounts = accountList.totalArchivedAccounts,
|
||||
sortType = accountList.sortType,
|
||||
groupType = accountList.groupType,
|
||||
)
|
||||
|
|
@ -252,8 +282,6 @@ internal class TesterAccountsViewModel @Inject constructor(
|
|||
|
||||
accountsCRUDRepository.saveAccounts(accountList = sortedAccountList)
|
||||
}
|
||||
|
||||
toggleButtonProgress(title = title, isInProgress = false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -266,22 +294,30 @@ internal class TesterAccountsViewModel @Inject constructor(
|
|||
?: persistentListOf()
|
||||
}
|
||||
|
||||
private fun toggleButtonProgress(title: String, isInProgress: Boolean) {
|
||||
uiState.update { state ->
|
||||
state.copy(
|
||||
buttons = state.buttons.updateButton(title) {
|
||||
it.copy(isInProgress = isInProgress)
|
||||
},
|
||||
)
|
||||
private fun withProgress(id: AccountsUM.Button.ID, block: suspend () -> Unit) {
|
||||
viewModelScope.launch {
|
||||
toggleButtonProgress(id = id, isInProgress = true)
|
||||
try {
|
||||
block()
|
||||
} finally {
|
||||
toggleButtonProgress(id = id, isInProgress = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ImmutableList<AccountsUM.Button>.updateButton(
|
||||
title: String,
|
||||
button: (AccountsUM.Button) -> AccountsUM.Button,
|
||||
): ImmutableList<AccountsUM.Button> {
|
||||
return this
|
||||
.map { if (it.title == title) button(it) else it }
|
||||
.toImmutableList()
|
||||
private fun toggleButtonProgress(id: AccountsUM.Button.ID, isInProgress: Boolean) {
|
||||
updateButton(id) {
|
||||
it.copy(isInProgress = isInProgress)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateButton(id: AccountsUM.Button.ID, button: (AccountsUM.Button) -> AccountsUM.Button) {
|
||||
uiState.update { state ->
|
||||
state.copy(
|
||||
buttons = state.buttons
|
||||
.map { if (it.id == id) button(it) else it }
|
||||
.toImmutableList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,14 +10,15 @@ import com.tangem.datasource.api.common.config.ApiEnvironmentConfig
|
|||
*/
|
||||
internal object ApiEnvironmentComparator : Comparator<ApiEnvironmentConfig> {
|
||||
|
||||
private val apiEnvironmentPriorityMap = ApiEnvironment.entries.associateWith {
|
||||
when (it) {
|
||||
private val apiEnvironmentPriorityMap = ApiEnvironment.entries.associateWith { environment ->
|
||||
when (environment) {
|
||||
ApiEnvironment.DEV -> 0
|
||||
ApiEnvironment.DEV_2 -> 1
|
||||
ApiEnvironment.DEV_3 -> 2
|
||||
ApiEnvironment.STAGE -> 3
|
||||
ApiEnvironment.MOCK -> 4
|
||||
ApiEnvironment.PROD -> 5
|
||||
ApiEnvironment.STAGE_2 -> 4
|
||||
ApiEnvironment.MOCK -> 5
|
||||
ApiEnvironment.PROD -> 6
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,13 +31,11 @@
|
|||
<ID>NamedArguments:TokenDetailsLoadedBalanceConverter.kt$TokenDetailsLoadedBalanceConverter$formatFiatAmount( status.value, stakingFiatAmount, currentState.selectedBalanceType, appCurrencyProvider(), )</ID>
|
||||
<ID>NamedArguments:TokenDetailsSwapTransactionsStateConverter.kt$TokenDetailsSwapTransactionsStateConverter$createStateInfo( transaction, toCryptoCurrency, fromCryptoCurrency, toFiatAmount, fromFiatAmount, )</ID>
|
||||
<ID>NestedScopeFunctions:TokenDetailsBalanceSelectStateConverter.kt$TokenDetailsBalanceSelectStateConverter$let { cryptoCurrencyStatus.value.fiatRate?.multiply(it) }</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:TokenDetailsModel.kt$TokenDetailsModel$private val isDemoCardUseCase: IsDemoCardUseCase</ID>
|
||||
<ID>NullableBooleanCheck:TokenDetailsSwapTransactionsStateConverter.kt$TokenDetailsSwapTransactionsStateConverter$transaction.status?.hasLongTime ?: false</ID>
|
||||
<ID>NullableToStringCall:DefaultTokenDetailsDeepLinkHandler.kt$DefaultTokenDetailsDeepLinkHandler$$networkId</ID>
|
||||
<ID>NullableToStringCall:DefaultTokenDetailsDeepLinkHandler.kt$DefaultTokenDetailsDeepLinkHandler$$tokenId</ID>
|
||||
<ID>NullableToStringCall:TokenDetailsStakingInfoConverter.kt$TokenDetailsStakingInfoConverter$$stakingCryptoAmount</ID>
|
||||
<ID>NullableToStringCall:TokenDetailsStakingInfoConverter.kt$TokenDetailsStakingInfoConverter$$stakingEntryInfo</ID>
|
||||
<ID>NullableToStringCall:TokenDetailsStakingInfoConverter.kt$TokenDetailsStakingInfoConverter$$yieldBalance</ID>
|
||||
<ID>PropertyUsedBeforeDeclaration:ExpressStatusBottomSheetStateProvider.kt$ExpressStatusBottomSheetStateProvider$network</ID>
|
||||
<ID>PropertyUsedBeforeDeclaration:ExpressStatusBottomSheetStateProvider.kt$ExpressStatusBottomSheetStateProvider$token</ID>
|
||||
<ID>PropertyUsedBeforeDeclaration:TokenDetailsModel.kt$TokenDetailsModel$uiState</ID>
|
||||
|
|
@ -49,7 +47,6 @@
|
|||
<ID>UseEmptyCounterpart:TokenDetailsAnalyticsEvent.kt$TokenDetailsAnalyticsEvent$mapOf()</ID>
|
||||
<ID>UseEmptyCounterpart:TokenDetailsAnalyticsEvent.kt$TokenDetailsAnalyticsEvent.Notice$mapOf()</ID>
|
||||
<ID>UseOrEmpty:ExchangeStatusFactory.kt$ExchangeStatusFactory$savedTransactions ?.flatMap { setOf(it.fromCryptoCurrency.id, it.toCryptoCurrency.id) } ?.toSet() ?.getQuotesOrEmpty() ?: emptySet()</ID>
|
||||
<ID>UseOrEmpty:TokenDetailsStakingInfoConverter.kt$TokenDetailsStakingInfoConverter$yieldBalance?.balance?.items ?: emptyList()</ID>
|
||||
<ID>VarCouldBeVal:TokenDetailsModel.kt$TokenDetailsModel$private var expressTxStatusTaskScheduler = SingleTaskScheduler<PersistentList<ExpressTransactionStateUM>>()</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ internal class DefaultTokenDetailsComponent @AssistedInject constructor(
|
|||
params = YieldSupplyComponent.Params(
|
||||
userWalletId = params.userWalletId,
|
||||
cryptoCurrency = params.currency,
|
||||
handleNavigation = (params.navigationAction as? NavigationAction.YieldSupply)
|
||||
shouldHandleNavigation = (params.navigationAction as? NavigationAction.YieldSupply)
|
||||
?.isActive,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import com.tangem.core.ui.format.bigdecimal.fiat
|
|||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.staking.YieldBalance
|
||||
import com.tangem.domain.models.staking.StakingBalance
|
||||
import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.*
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.utils.getBalance
|
||||
|
|
@ -29,8 +29,8 @@ internal class TokenDetailsBalanceSelectStateConverter(
|
|||
return this
|
||||
}
|
||||
|
||||
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
|
||||
val stakingCryptoAmount = yieldBalance?.getTotalWithRewardsStakingBalance(
|
||||
val stakingBalance = cryptoCurrencyStatus.value.stakingBalance as? StakingBalance.Data
|
||||
val stakingCryptoAmount = stakingBalance?.getTotalWithRewardsStakingBalance(
|
||||
cryptoCurrencyStatus.currency.network.rawId,
|
||||
)
|
||||
val stakingFiatAmount = stakingCryptoAmount?.let { cryptoCurrencyStatus.value.fiatRate?.multiply(it) }
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import com.tangem.core.ui.format.bigdecimal.*
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.staking.YieldBalance
|
||||
import com.tangem.domain.models.staking.StakingBalance
|
||||
import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
|
||||
|
|
@ -73,7 +73,7 @@ internal class TokenDetailsLoadedBalanceConverter(
|
|||
status: CryptoCurrencyStatus,
|
||||
): TokenDetailsBalanceBlockState {
|
||||
val stakingCryptoAmount =
|
||||
(status.value.yieldBalance as? YieldBalance.Data)?.getTotalWithRewardsStakingBalance(
|
||||
(status.value.stakingBalance as? StakingBalance.Data)?.getTotalWithRewardsStakingBalance(
|
||||
status.currency.network.rawId,
|
||||
)
|
||||
val stakingFiatAmount = stakingCryptoAmount?.let { status.value.fiatRate?.multiply(it) }
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import com.tangem.core.ui.format.bigdecimal.format
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.staking.RewardBlockType
|
||||
import com.tangem.domain.models.staking.YieldBalance
|
||||
import com.tangem.domain.models.staking.StakingBalance
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
import com.tangem.domain.staking.utils.getRewardStakingBalance
|
||||
|
|
@ -54,18 +54,29 @@ internal class TokenDetailsStakingInfoConverter(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
private fun getStakingInfoBlock(status: CryptoCurrencyStatus, state: TokenDetailsState): StakingBlockUM? {
|
||||
val yieldBalance = status.value.yieldBalance as? YieldBalance.Data
|
||||
val stakingBalance = status.value.stakingBalance as? StakingBalance.Data
|
||||
|
||||
val stakingCryptoAmount = yieldBalance?.getTotalStakingBalance(status.currency.network.rawId)
|
||||
val pendingBalances = yieldBalance?.balance?.items ?: emptyList()
|
||||
val stakingCryptoAmount = stakingBalance?.getTotalStakingBalance(status.currency.network.rawId)
|
||||
|
||||
val hasPendingBalances = when (stakingBalance) {
|
||||
is StakingBalance.Data.StakeKit -> stakingBalance.balance.items.isNotEmpty()
|
||||
is StakingBalance.Data.P2P -> !stakingBalance.unstakingAmount.isNullOrZero()
|
||||
null -> false
|
||||
}
|
||||
val pendingAmount = when (stakingBalance) {
|
||||
is StakingBalance.Data.StakeKit -> stakingBalance.balance.items.sumOf { it.amount }
|
||||
is StakingBalance.Data.P2P -> stakingBalance.unstakingAmount
|
||||
null -> BigDecimal.ZERO
|
||||
}
|
||||
|
||||
val iconState = state.tokenInfoBlockState.iconState
|
||||
|
||||
Timber.i(
|
||||
"""
|
||||
getStakingInfoBlock:
|
||||
– yieldBalance: $yieldBalance
|
||||
– stakingBalance: ${stakingBalance ?: "null"}
|
||||
– stakingCryptoAmount: $stakingCryptoAmount
|
||||
– stakingEntryInfo: $stakingEntryInfo
|
||||
""".trimIndent(),
|
||||
|
|
@ -73,16 +84,24 @@ internal class TokenDetailsStakingInfoConverter(
|
|||
|
||||
return when {
|
||||
stakingCryptoAmount.isNullOrZero() && stakingEntryInfo != null -> {
|
||||
if (pendingBalances.isEmpty()) {
|
||||
if (!hasPendingBalances) {
|
||||
getStakeAvailableState(stakingEntryInfo, iconState, isStakingButtonEnabled(status))
|
||||
} else {
|
||||
getStakedBlockWithFiatAmount(status, pendingBalances.sumOf { it.amount }, null)
|
||||
getStakedBlockWithFiatAmount(status, pendingAmount, null)
|
||||
}
|
||||
}
|
||||
stakingCryptoAmount.isNullOrZero() && stakingEntryInfo == null -> {
|
||||
null
|
||||
}
|
||||
else -> getStakedBlockWithFiatAmount(status, stakingCryptoAmount, yieldBalance?.getRewardStakingBalance())
|
||||
else -> getStakedBlockWithFiatAmount(
|
||||
status = status,
|
||||
stakingAmount = stakingCryptoAmount,
|
||||
rewardAmount = when (stakingBalance) {
|
||||
is StakingBalance.Data.StakeKit -> stakingBalance.getRewardStakingBalance()
|
||||
is StakingBalance.Data.P2P -> stakingBalance.totalRewards
|
||||
else -> BigDecimal.ZERO
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0" ?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>BooleanPropertyNaming:TxHistoryListManager.kt$TxHistoryListManager$val clearUiBatches = state.status is PaginationStatus.InitialLoading && batchListState.status is PaginationStatus.Paginating</ID>
|
||||
<ID>MultilineLambdaItParameter:TxHistoryUiManager.kt$TxHistoryUiManager${ it.status !is PaginationStatus.None && it.status !is PaginationStatus.InitialLoading && it.status !is PaginationStatus.InitialLoadingError }</ID>
|
||||
<ID>UseEmptyCounterpart:TxHistoryListState.kt$TxHistoryListState$listOf()</ID>
|
||||
</CurrentIssues>
|
||||
</SmellBaseline>
|
||||
|
|
@ -88,13 +88,13 @@ internal class TxHistoryListManager(
|
|||
|
||||
private fun updateState(batchListState: BatchListState<Int, PaginationWrapper<TxInfo>>) {
|
||||
state.update { state ->
|
||||
val clearUiBatches =
|
||||
val shouldClearUiBatches =
|
||||
state.status is PaginationStatus.InitialLoading && batchListState.status is PaginationStatus.Paginating
|
||||
state.copy(
|
||||
status = batchListState.status,
|
||||
uiBatches = uiManager.createOrUpdateUiBatches(
|
||||
newCurrencyBatches = batchListState.data,
|
||||
clearUiBatches = clearUiBatches,
|
||||
shouldClearUiBatches = shouldClearUiBatches,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@ import com.tangem.pagination.PaginationStatus
|
|||
|
||||
data class TxHistoryListState(
|
||||
val status: PaginationStatus<*> = PaginationStatus.None,
|
||||
val uiBatches: List<Batch<Int, List<TxHistoryUM.TxHistoryItemUM>>> = listOf(),
|
||||
val uiBatches: List<Batch<Int, List<TxHistoryUM.TxHistoryItemUM>>> = emptyList(),
|
||||
)
|
||||
|
|
@ -22,10 +22,10 @@ internal class TxHistoryUiManager(
|
|||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
val items: Flow<ImmutableList<TxHistoryUM.TxHistoryItemUM>> = state
|
||||
// filter initial states, since we dont emit loading items as UI items
|
||||
.filter {
|
||||
it.status !is PaginationStatus.None &&
|
||||
it.status !is PaginationStatus.InitialLoading &&
|
||||
it.status !is PaginationStatus.InitialLoadingError
|
||||
.filter { state ->
|
||||
state.status !is PaginationStatus.None &&
|
||||
state.status !is PaginationStatus.InitialLoading &&
|
||||
state.status !is PaginationStatus.InitialLoadingError
|
||||
}
|
||||
.mapLatest { state ->
|
||||
state.uiBatches.asSequence()
|
||||
|
|
@ -36,10 +36,10 @@ internal class TxHistoryUiManager(
|
|||
|
||||
fun createOrUpdateUiBatches(
|
||||
newCurrencyBatches: List<Batch<Int, PaginationWrapper<TxInfo>>>,
|
||||
clearUiBatches: Boolean,
|
||||
shouldClearUiBatches: Boolean,
|
||||
): List<Batch<Int, List<TxHistoryUM.TxHistoryItemUM>>> {
|
||||
val currentUiBatches = state.value.uiBatches
|
||||
val batches = if (clearUiBatches) mutableListOf() else currentUiBatches.toMutableList()
|
||||
val batches = if (shouldClearUiBatches) mutableListOf() else currentUiBatches.toMutableList()
|
||||
|
||||
for ((key, data) in newCurrencyBatches) {
|
||||
// Find if batch with same key exists
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
<?xml version="1.0" ?>
|
||||
<SmellBaseline>
|
||||
<ManuallySuppressedIssues/>
|
||||
<CurrentIssues>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:AccountItemsDelegate.kt$AccountItemsDelegate$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:WalletSettingsModel.kt$WalletSettingsModel$private val isDemoCardUseCase: IsDemoCardUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:WalletSettingsModel.kt$WalletSettingsModel$private val isUpgradeWalletNotificationEnabledUseCase: IsUpgradeWalletNotificationEnabledUseCase</ID>
|
||||
</CurrentIssues>
|
||||
<CurrentIssues/>
|
||||
</SmellBaseline>
|
||||
|
|
|
|||
|
|
@ -438,7 +438,7 @@ internal class WalletSettingsModel @Inject constructor(
|
|||
AppRoute.CreateWalletBackup(
|
||||
userWalletId = params.userWalletId,
|
||||
isUpgradeFlow = isUpgradeFlow,
|
||||
setAccessCode = true,
|
||||
shouldSetAccessCode = true,
|
||||
analyticsSource = AnalyticsParam.ScreensSources.WalletSettings.value,
|
||||
analyticsAction = if (isUpgradeFlow) {
|
||||
RecoveryPhraseScreenAction.Backup.value
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
<ID>BooleanPropertyNaming:DefaultUserWalletsFetcher.kt$DefaultUserWalletsFetcher$@Assisted("onlyMultiCurrency") private val onlyMultiCurrency: Boolean</ID>
|
||||
<ID>BooleanPropertyNaming:DraggableItem.kt$DraggableItem$abstract val showShadow: Boolean</ID>
|
||||
<ID>BooleanPropertyNaming:DraggableItem.kt$DraggableItem.RoundingMode$abstract val showGap: Boolean</ID>
|
||||
<ID>BooleanPropertyNaming:GetMultiWalletWarningsFactory.kt$GetMultiWalletWarningsFactory$val accessCodeSkipped = array[7] as Boolean</ID>
|
||||
<ID>BooleanPropertyNaming:GetSingleWalletWarningsFactory.kt$GetSingleWalletWarningsFactory$private var readyForRateAppNotification = false</ID>
|
||||
<ID>BooleanPropertyNaming:GetSingleWalletWarningsFactory.kt$GetSingleWalletWarningsFactory$val userHasWalletOrWallet2 = userWallets.filterIsInstance<UserWallet.Cold>().any { val typesResolver = it.scanResponse.cardTypesResolver typesResolver.isTangemWallet() || typesResolver.isWallet2() }</ID>
|
||||
<ID>BooleanPropertyNaming:OrganizeTokensState.kt$OrganizeTokensState.ActionsConfig$val showApplyProgress: Boolean = false</ID>
|
||||
|
|
@ -20,7 +19,6 @@
|
|||
<ID>BooleanPropertyNaming:WalletManageButton.kt$WalletManageButton$/** Whether to dim content */ abstract val dimContent: Boolean</ID>
|
||||
<ID>BooleanPropertyNaming:WalletManageButton.kt$WalletManageButton.Swap$val showBadge: Boolean = false</ID>
|
||||
<ID>BooleanPropertyNaming:WalletModel.kt$WalletModel$private var needToRefreshWallet = false</ID>
|
||||
<ID>BooleanPropertyNaming:WalletModel.kt$WalletModel$val initialDataProduced = tangemPayOnboardingRepository.isTangemPayInitialDataProduced()</ID>
|
||||
<ID>BooleanPropertyNaming:WalletNameMigrationUseCase.kt$WalletNameMigrationUseCase$private val useNewListRepository: Boolean</ID>
|
||||
<ID>BooleanPropertyNaming:WalletScreen.kt$val portfolioContent = state is WalletState.MultiCurrency.Content && state.tokensListState is WalletTokensListState.ContentState.PortfolioContent</ID>
|
||||
<ID>BooleanPropertyNaming:WalletScreen.kt$val showMarketsHint by remember { derivedStateOf { // Show hint only when there are items in the list // and when there a no items to scroll listState.layoutInfo.totalItemsCount > 0 && !listState.canScrollBackward && !listState.canScrollForward || listState.canScrollBackward && !listState.canScrollForward } }</ID>
|
||||
|
|
@ -73,12 +71,10 @@
|
|||
<ID>MultilineLambdaItParameter:WalletCurrencyActionsClickIntents.kt$WalletCurrencyActionsClickIntentsImplementor${ analyticsEventHandler.send(TokenReceiveAnalyticsEvent.ButtonShareAddress(currency.symbol)) shareManager.shareText(text = it) }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletCurrencyActionsClickIntents.kt$WalletCurrencyActionsClickIntentsImplementor${ onAddressTypeSelected( userWalletId = userWalletId, currency = currency, addressModel = it, ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletLoaderStorage.kt$WalletLoaderStorage${ it.forEach(Job::cancel) loaders.remove(id) }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletModel.kt$WalletModel${ it .conflate() .distinctUntilChanged() .onEach { selectedWallet -> if (selectedWallet.isMultiCurrency) { selectedWalletAnalyticsSender.send(selectedWallet) } subscribeOnExpressTransactionsUpdates(selectedWallet) observeAndClearNFTCacheIfNeedUseCase(selectedWallet) } .flowOn(dispatchers.main) .launchIn(modelScope) }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletModel.kt$WalletModel${ walletScreenContentLoader.load( userWallet = it, clickIntents = clickIntents, coroutineScope = modelScope, isRefresh = true, ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletModel.kt$WalletModel${ walletsUpdateActionResolver.resolve( wallets = it, currentState = stateHolder.value, ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletNFTListSubscriber.kt$WalletNFTListSubscriber${ stateHolder.update( SetNFTCollectionsTransformer( userWalletId = userWallet.walletId, nftCollections = it, onItemClick = { clickIntents.onNFTClick(userWallet) }, ), ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletNameMigrationUseCase.kt$WalletNameMigrationUseCase${ val defaultName = it.name val suggestedWalletName = suggestedWalletName(defaultName, existingNames) if (defaultName != suggestedWalletName) { userWalletsListRepository.saveWithoutLock(it.copy(name = suggestedWalletName), canOverride = true) } Timber.tag("Migrated names").e(it.walletId.toString() + " " + suggestedWalletName) }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletNotifications.kt${ // TODO develop promo banner general component when (it) { is WalletNotification.SwapPromo -> { // Use it on new promo action } is WalletNotification.NoteMigration -> { NoteMigrationNotification( config = it.config, modifier = modifier.animateItem(fadeInSpec = null, fadeOutSpec = null), ) } is WalletNotification.FinishWalletActivation -> { Notification( config = it.config, modifier = modifier.animateItem(fadeInSpec = null, fadeOutSpec = null), ) } else -> { Notification( config = it.config, modifier = modifier.animateItem(fadeInSpec = null, fadeOutSpec = null), iconTint = when (it) { is WalletNotification.Critical -> TangemTheme.colors.icon.warning is WalletNotification.Informational -> TangemTheme.colors.icon.accent is WalletNotification.RateApp -> TangemTheme.colors.icon.attention is WalletNotification.UnlockWallets -> TangemTheme.colors.icon.primary1 is WalletNotification.UsedOutdatedData -> TangemTheme.colors.text.attention else -> null }, ) } } }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletScreen.kt${ PaddingValues( bottom = it.calculateBottomPadding() + marketHintAproxHeight + 52.dp, ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletScreen.kt${ WalletSnackbarHost( snackbarHostState = it, event = state.event, modifier = Modifier .padding(bottom = TangemTheme.dimens.spacing4) .navigationBarsPadding(), ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletScreen.kt${ balancesAndLimitsBlock( modifier = itemModifier, state = it.balancesAndLimitBlockState, ) }</ID>
|
||||
|
|
@ -87,11 +83,8 @@
|
|||
<ID>MultilineLambdaItParameter:WalletScreen.kt${ nftCollections( modifier = itemModifier, state = it.nftState, ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletWarningsClickIntents.kt$WalletWarningsClickIntentsImplementor${ Timber.e( """ Unable to get user wallet |- ID: $userWalletId |- Exception: $it """.trimIndent(), ) null }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletWarningsClickIntents.kt$WalletWarningsClickIntentsImplementor${ router.openOnboardingScreen( scanResponse = it.scanResponse, continueBackup = true, ) }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletWarningsClickIntents.kt$WalletWarningsClickIntentsImplementor${ when (it) { UnlockWalletError.AlreadyUnlocked -> Unit UnlockWalletError.ScannedCardWalletNotMatched -> { uiMessageSender.send( message = DialogMessage( title = resourceReference(R.string.common_warning), message = resourceReference(R.string.error_wrong_wallet_tapped), ), ) } UnlockWalletError.UnableToUnlock -> { Timber.e("Unable to unlock wallet with id: $selectedUserWalletId") uiMessageSender.send( SnackbarMessage(TextReference.Res(R.string.generic_error)), ) } UnlockWalletError.UserCancelled -> Unit UnlockWalletError.UserWalletNotFound -> { // This should never happen in this flow Timber.e("User wallet not found for unlock: $selectedUserWalletId") uiMessageSender.send( SnackbarMessage(TextReference.Res(R.string.generic_error)), ) } } }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletWithFundsChecker.kt$WalletWithFundsChecker${ val amount = it.value.amount ?: return@any false !amount.isZero() }</ID>
|
||||
<ID>MultilineLambdaItParameter:WalletsUpdateActionResolver.kt$WalletsUpdateActionResolver${ if (it.warnings.any { it is WalletNotification.FinishWalletActivation }) { it.walletCardState.id } else { null } }</ID>
|
||||
<ID>NamedArguments:BasicAccountListSubscriber.kt$BasicAccountListSubscriber$updateContent(convertParams, appCurrency, yieldSupplyApyMap, stakingApyMap)</ID>
|
||||
<ID>NamedArguments:GetMultiWalletWarningsFactory.kt$GetMultiWalletWarningsFactory$addFinishWalletActivationNotification(userWallet, totalFiatBalance, clickIntents, accessCodeSkipped)</ID>
|
||||
<ID>NamedArguments:GetMultiWalletWarningsFactory.kt$GetMultiWalletWarningsFactory$addInformationalNotifications(userWallet, cardTypesResolver, flattenCurrencies, clickIntents)</ID>
|
||||
<ID>NamedArguments:GetMultiWalletWarningsFactory.kt$GetMultiWalletWarningsFactory$addWarningNotifications(cardTypesResolver, flattenCurrencies, isNeedToBackup, clickIntents)</ID>
|
||||
<ID>NamedArguments:TangemSnapFlingBehavior.kt$HighVelocityApproachAnimation$animateDecay(offset, animationState, decayAnimationSpec, onAnimationStep)</ID>
|
||||
|
|
@ -114,23 +107,6 @@
|
|||
<ID>NoNameShadowing:WalletCurrencyActionsClickIntents.kt$WalletCurrencyActionsClickIntentsImplementor${ it is TokensListItemUM.Token }</ID>
|
||||
<ID>NoNameShadowing:WalletNFTItem.kt$modifier</ID>
|
||||
<ID>NoNameShadowing:WalletScreen.kt${ it.organizeTokensButtonConfig?.let { config -> organizeTokensButton( modifier = itemModifier, isEnabled = config.isEnabled, onClick = config.onClick, ) } }</ID>
|
||||
<ID>NoNameShadowing:WalletWarningsClickIntents.kt$WalletWarningsClickIntentsImplementor${ when (it) { UnlockWalletError.AlreadyUnlocked -> Unit UnlockWalletError.ScannedCardWalletNotMatched -> { uiMessageSender.send( message = DialogMessage( title = resourceReference(R.string.common_warning), message = resourceReference(R.string.error_wrong_wallet_tapped), ), ) } UnlockWalletError.UnableToUnlock -> { Timber.e("Unable to unlock wallet with id: $selectedUserWalletId") uiMessageSender.send( SnackbarMessage(TextReference.Res(R.string.generic_error)), ) } UnlockWalletError.UserCancelled -> Unit UnlockWalletError.UserWalletNotFound -> { // This should never happen in this flow Timber.e("User wallet not found for unlock: $selectedUserWalletId") uiMessageSender.send( SnackbarMessage(TextReference.Res(R.string.generic_error)), ) } } }</ID>
|
||||
<ID>NoNameShadowing:WalletsUpdateActionResolver.kt$WalletsUpdateActionResolver${ it is WalletNotification.FinishWalletActivation }</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:AccountDependencies.kt$AccountDependencies$val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:ExpandedAccountsHolder.kt$ExpandedAccountsHolder$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:GetMultiWalletWarningsFactory.kt$GetMultiWalletWarningsFactory$private val isDemoCardUseCase: IsDemoCardUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:GetMultiWalletWarningsFactory.kt$GetMultiWalletWarningsFactory$private val isNeedToBackupUseCase: IsNeedToBackupUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:GetMultiWalletWarningsFactory.kt$GetMultiWalletWarningsFactory$private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:GetSingleWalletWarningsFactory.kt$GetSingleWalletWarningsFactory$private val isDemoCardUseCase: IsDemoCardUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:GetSingleWalletWarningsFactory.kt$GetSingleWalletWarningsFactory$private val isNeedToBackupUseCase: IsNeedToBackupUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:GetSingleWalletWarningsFactory.kt$GetSingleWalletWarningsFactory$private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:OrganizeTokensModel.kt$OrganizeTokensModel$private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:ScreenLifecycleProvider.kt$ScreenLifecycleProvider$val isBackgroundState: StateFlow<Boolean> = _isBackgroundState</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:WalletCurrencyActionsClickIntents.kt$WalletCurrencyActionsClickIntentsImplementor$private val isCryptoCurrencyCoinCouldHide: IsCryptoCurrencyCoinCouldHideUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:WalletCurrencyActionsClickIntents.kt$WalletCurrencyActionsClickIntentsImplementor$private val isDemoCardUseCase: IsDemoCardUseCase</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:WalletModel.kt$WalletModel$private val isWalletsScrollPreviewEnabled: IsWalletsScrollPreviewEnabled</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:WalletScreen.kt$val isAutoScroll = remember { mutableStateOf(value = false) }</ID>
|
||||
<ID>NonBooleanPropertyPrefixedWithIs:WalletScreen.kt$val isNavBarVisible = remember { mutableStateOf(true) }</ID>
|
||||
<ID>NullableToStringCall:DefaultPromoDeeplinkHandler.kt$DefaultPromoDeeplinkHandler$$bitcoinCurrency</ID>
|
||||
<ID>NullableToStringCall:DefaultPromoDeeplinkHandler.kt$DefaultPromoDeeplinkHandler$$bitcoinStatus</ID>
|
||||
<ID>NullableToStringCall:DefaultPromoDeeplinkHandler.kt$DefaultPromoDeeplinkHandler$${cryptoCurrencies?.size}</ID>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import com.arkivanov.decompose.extensions.compose.subscribeAsState
|
||||
import com.arkivanov.decompose.router.slot.childSlot
|
||||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
import com.arkivanov.essenty.lifecycle.doOnResume
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.child
|
||||
|
|
@ -48,6 +49,7 @@ internal class WalletComponent @AssistedInject constructor(
|
|||
|
||||
init {
|
||||
lifecycle.subscribe(model.screenLifecycleProvider)
|
||||
doOnResume { model.onResume() }
|
||||
componentScope.launch { model.innerWalletRouter.navigateToFlow.collect { navigate(it) } }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@ import androidx.compose.runtime.Stable
|
|||
import arrow.core.getOrElse
|
||||
import com.arkivanov.decompose.router.slot.activate
|
||||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
import com.squareup.sqldelight.internal.AtomicBoolean
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
|
||||
import com.tangem.core.analytics.utils.TrackingContextProxy
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
|
|
@ -96,6 +98,7 @@ internal class WalletModel @Inject constructor(
|
|||
private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles,
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
private val tangemPayMainScreenCustomerInfoUseCase: TangemPayMainScreenCustomerInfoUseCase,
|
||||
private val trackingContextProxy: TrackingContextProxy,
|
||||
val screenLifecycleProvider: ScreenLifecycleProvider,
|
||||
val innerWalletRouter: InnerWalletRouter,
|
||||
) : Model() {
|
||||
|
|
@ -111,16 +114,12 @@ internal class WalletModel @Inject constructor(
|
|||
private val updateTangemPayJobHolder = JobHolder()
|
||||
|
||||
private var expressTxStatusTaskScheduler = SingleTaskScheduler<Unit>()
|
||||
private val hasMainScreenOpenedEventSent = AtomicBoolean(false)
|
||||
|
||||
init {
|
||||
analyticsEventsHandler.send(WalletScreenAnalyticsEvent.MainScreen.ScreenOpened)
|
||||
|
||||
screenLifecycleProvider.isBackgroundState
|
||||
.onEach { isBackground ->
|
||||
if (isBackground.not()) {
|
||||
suggestToEnableBiometrics()
|
||||
}
|
||||
}.launchIn(modelScope)
|
||||
if (!hotWalletFeatureToggles.isHotWalletEnabled) {
|
||||
analyticsEventsHandler.send(WalletScreenAnalyticsEvent.MainScreen.ScreenOpenedLegacy)
|
||||
}
|
||||
|
||||
suggestToOpenMarkets()
|
||||
|
||||
|
|
@ -138,6 +137,12 @@ internal class WalletModel @Inject constructor(
|
|||
clickIntents.initialize(innerWalletRouter, modelScope)
|
||||
}
|
||||
|
||||
fun onResume() {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
suggestToEnableBiometrics()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateYieldSupplyApy() {
|
||||
if (yieldSupplyFeatureToggles.isYieldSupplyFeatureEnabled) {
|
||||
modelScope.launch(dispatchers.default) {
|
||||
|
|
@ -193,7 +198,6 @@ internal class WalletModel @Inject constructor(
|
|||
private suspend fun shouldShowAskBiometryBottomSheet(): Boolean {
|
||||
return if (hotWalletFeatureToggles.isHotWalletEnabled) {
|
||||
userWalletsListRepository.userWalletsSync().any { it is UserWallet.Cold } &&
|
||||
innerWalletRouter.isWalletLastScreen() &&
|
||||
shouldShowAskBiometryUseCase() &&
|
||||
canUseBiometryUseCase()
|
||||
} else {
|
||||
|
|
@ -271,11 +275,25 @@ internal class WalletModel @Inject constructor(
|
|||
// It's okay here because we need to be able to observe the selected wallet changes
|
||||
@Suppress("DEPRECATION")
|
||||
private fun subscribeOnSelectedWalletFlow() {
|
||||
getSelectedWalletUseCase().onRight {
|
||||
it
|
||||
getSelectedWalletUseCase().onRight { walletFlow ->
|
||||
walletFlow
|
||||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
.onEach { selectedWallet ->
|
||||
trackingContextProxy.setContext(selectedWallet)
|
||||
|
||||
if (hotWalletFeatureToggles.isHotWalletEnabled && !hasMainScreenOpenedEventSent.get()) {
|
||||
// send it here because we need context to be set
|
||||
modelScope.launch {
|
||||
val hasMobileWallet = userWalletsListRepository.userWalletsSync()
|
||||
.any { it is UserWallet.Hot }
|
||||
analyticsEventsHandler.send(
|
||||
WalletScreenAnalyticsEvent.MainScreen.ScreenOpened(hasMobileWallet),
|
||||
)
|
||||
hasMainScreenOpenedEventSent.set(true)
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedWallet.isMultiCurrency) {
|
||||
selectedWalletAnalyticsSender.send(selectedWallet)
|
||||
}
|
||||
|
|
@ -416,6 +434,7 @@ internal class WalletModel @Inject constructor(
|
|||
when (action) {
|
||||
is WalletsUpdateActionResolver.Action.InitializeWallets -> initializeWallets(action)
|
||||
is WalletsUpdateActionResolver.Action.ReinitializeWallet -> reinitializeWallet(action)
|
||||
is WalletsUpdateActionResolver.Action.ReinitializeWallets -> reinitializeWallets(action)
|
||||
is WalletsUpdateActionResolver.Action.AddWallet -> addWallet(action)
|
||||
is WalletsUpdateActionResolver.Action.DeleteWallet -> deleteWallet(action)
|
||||
is WalletsUpdateActionResolver.Action.UnlockWallet -> unlockWallet(action)
|
||||
|
|
@ -520,6 +539,32 @@ internal class WalletModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun reinitializeWallets(action: WalletsUpdateActionResolver.Action.ReinitializeWallets) {
|
||||
action.wallets.forEach { userWallet ->
|
||||
walletScreenContentLoader.cancel(userWallet.walletId)
|
||||
tokenListStore.remove(userWallet.walletId)
|
||||
|
||||
walletScreenContentLoader.load(
|
||||
userWallet = userWallet,
|
||||
clickIntents = clickIntents,
|
||||
coroutineScope = modelScope,
|
||||
)
|
||||
|
||||
modelScope.launch(dispatchers.main) {
|
||||
fetchWalletContent(userWallet = userWallet)
|
||||
}
|
||||
|
||||
stateHolder.update(
|
||||
ReinitializeWalletTransformer(
|
||||
prevWalletId = userWallet.walletId,
|
||||
newUserWallet = userWallet,
|
||||
clickIntents = clickIntents,
|
||||
walletImageResolver = walletImageResolver,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun addWallet(action: WalletsUpdateActionResolver.Action.AddWallet) {
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
fetchWalletContent(userWallet = action.selectedWallet)
|
||||
|
|
|
|||
|
|
@ -64,8 +64,11 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
|||
selectedWallet: UserWallet,
|
||||
): Action {
|
||||
return when {
|
||||
isHotWalletUpgraded(state, wallets) -> {
|
||||
getHotWalletsUpgradedAction(state, wallets)
|
||||
isAnyHotWalletUpgraded(state, wallets) -> {
|
||||
getHotWalletsUpgradedAction(state, wallets, selectedWallet)
|
||||
}
|
||||
isAnyHotWalletBackedUpChange(state, wallets) -> {
|
||||
getHotWalletsBackedUpAction(state, wallets)
|
||||
}
|
||||
isWalletsCountChanged(state, wallets) -> {
|
||||
getChangeWalletsListAction(state, wallets, selectedWallet)
|
||||
|
|
@ -79,31 +82,35 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
|||
isAnyWalletNameChanged(state, wallets) -> {
|
||||
getRenameWalletsAction(state, wallets)
|
||||
}
|
||||
isAnyHotWalletBackedUpChange(state, wallets) -> {
|
||||
getHotWalletsBackedUpAction(state, wallets)
|
||||
isAnyWalletUnlocked(state, wallets) -> {
|
||||
Action.UnlockWallet(
|
||||
selectedWallet = selectedWallet,
|
||||
unlockedWallets = wallets.filterNot(UserWallet::isLocked),
|
||||
)
|
||||
}
|
||||
else -> getUpdateSelectedWalletAction(state, wallets, selectedWallet)
|
||||
isSelectedWalletCardsCountChanged(state, selectedWallet) -> {
|
||||
Action.UpdateWalletCardCount(selectedWallet)
|
||||
}
|
||||
else -> Action.Unknown
|
||||
}
|
||||
}
|
||||
|
||||
private fun isAnyHotWalletBackedUpChange(state: WalletScreenState, wallets: List<UserWallet>): Boolean {
|
||||
val incompleteActivationWalletIds = state.incompleteActivationWalletIds()
|
||||
val walletsToUpdate = wallets.filter {
|
||||
it is UserWallet.Hot && it.backedUp == incompleteActivationWalletIds.contains(it.walletId)
|
||||
return wallets.any {
|
||||
it is UserWallet.Hot && it.backedUp && incompleteActivationWalletIds.contains(it.walletId)
|
||||
}
|
||||
return walletsToUpdate.isNotEmpty()
|
||||
}
|
||||
|
||||
private fun isHotWalletUpgraded(state: WalletScreenState, wallets: List<UserWallet>): Boolean {
|
||||
val previousWallet = state
|
||||
.wallets
|
||||
.getOrNull(state.selectedWalletIndex)
|
||||
return when (previousWallet) {
|
||||
is WalletState.MultiCurrency -> {
|
||||
val wallet = wallets.firstOrNull { it.walletId == previousWallet.walletCardState.id }
|
||||
previousWallet.type == WalletState.MultiCurrency.WalletType.Hot && wallet is UserWallet.Cold
|
||||
private fun isAnyHotWalletUpgraded(state: WalletScreenState, wallets: List<UserWallet>): Boolean {
|
||||
return state.wallets.any { walletState ->
|
||||
when (walletState) {
|
||||
is WalletState.MultiCurrency -> {
|
||||
val wallet = wallets.firstOrNull { it.walletId == walletState.walletCardState.id }
|
||||
walletState.type == WalletState.MultiCurrency.WalletType.Hot && wallet is UserWallet.Cold
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -189,9 +196,15 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
|||
private fun getHotWalletsUpgradedAction(
|
||||
state: WalletScreenState,
|
||||
wallets: List<UserWallet>,
|
||||
): Action.ReloadWallets {
|
||||
val walletsToUpdate = wallets.filter { it.walletId == state.getPrevSelectedWallet().id }
|
||||
return Action.ReloadWallets(walletsToUpdate)
|
||||
selectedWallet: UserWallet,
|
||||
): Action.ReinitializeWallets {
|
||||
val walletsToUpdate = wallets.filter { wallet ->
|
||||
val previousState = state.wallets.firstOrNull { it.walletCardState.id == wallet.walletId }
|
||||
?: return@filter false
|
||||
wallet is UserWallet.Cold && previousState is WalletState.MultiCurrency &&
|
||||
previousState.type == WalletState.MultiCurrency.WalletType.Hot
|
||||
}
|
||||
return Action.ReinitializeWallets(selectedWallet, walletsToUpdate)
|
||||
}
|
||||
|
||||
private fun getRenameWalletsAction(state: WalletScreenState, wallets: List<UserWallet>): Action.RenameWallets {
|
||||
|
|
@ -205,29 +218,14 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun getUpdateSelectedWalletAction(
|
||||
state: WalletScreenState,
|
||||
wallets: List<UserWallet>,
|
||||
selectedWallet: UserWallet,
|
||||
): Action {
|
||||
return when {
|
||||
isSelectedWalletUnlocked(state, selectedWallet) -> {
|
||||
Action.UnlockWallet(
|
||||
selectedWallet = selectedWallet,
|
||||
unlockedWallets = wallets.filterNot(UserWallet::isLocked),
|
||||
)
|
||||
}
|
||||
isSelectedWalletCardsCountChanged(state, selectedWallet) -> {
|
||||
Action.UpdateWalletCardCount(selectedWallet)
|
||||
}
|
||||
else -> Action.Unknown
|
||||
private fun isAnyWalletUnlocked(state: WalletScreenState, wallets: List<UserWallet>): Boolean {
|
||||
return state.wallets.any { walletState ->
|
||||
val wallet = wallets.firstOrNull { it.walletId == walletState.walletCardState.id } ?: return@any false
|
||||
!wallet.isLocked &&
|
||||
(walletState is WalletState.MultiCurrency.Locked || walletState is WalletState.SingleCurrency.Locked)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSelectedWalletUnlocked(state: WalletScreenState, selectedWallet: UserWallet): Boolean {
|
||||
return state.isSelectedWalletLocked() && !selectedWallet.isLocked
|
||||
}
|
||||
|
||||
private fun isSelectedWalletCardsCountChanged(state: WalletScreenState, selectedWallet: UserWallet): Boolean {
|
||||
if (selectedWallet !is UserWallet.Cold) return false
|
||||
val prevSelectedWallet = state.getPrevSelectedWallet()
|
||||
|
|
@ -235,12 +233,6 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
|||
prevSelectedWallet.cardCount != selectedWallet.getCardsCount()
|
||||
}
|
||||
|
||||
private fun WalletScreenState.isSelectedWalletLocked(): Boolean {
|
||||
val selectedWalletState = wallets.getOrNull(selectedWalletIndex) ?: error("Selected wallet is not found")
|
||||
return selectedWalletState is WalletState.MultiCurrency.Locked ||
|
||||
selectedWalletState is WalletState.SingleCurrency.Locked
|
||||
}
|
||||
|
||||
private fun WalletScreenState.getPrevSelectedWallet(): WalletCardState {
|
||||
return wallets
|
||||
.map(WalletState::walletCardState)
|
||||
|
|
@ -249,9 +241,12 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
|||
}
|
||||
|
||||
private fun WalletScreenState.incompleteActivationWalletIds(): List<UserWalletId> {
|
||||
return wallets.mapNotNull {
|
||||
if (it.warnings.any { it is WalletNotification.FinishWalletActivation }) {
|
||||
it.walletCardState.id
|
||||
return wallets.mapNotNull { wallet ->
|
||||
if (wallet.warnings.any { it is WalletNotification.FinishWalletActivation } ||
|
||||
wallet.walletCardState is WalletState.MultiCurrency &&
|
||||
wallet.walletCardState.additionalInfo?.isHotBackedUp == false
|
||||
) {
|
||||
wallet.walletCardState.id
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
|
@ -306,6 +301,19 @@ internal class WalletsUpdateActionResolver @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reinitialize wallets
|
||||
*/
|
||||
data class ReinitializeWallets(
|
||||
val selectedWallet: UserWallet,
|
||||
val wallets: List<UserWallet>,
|
||||
) : Action() {
|
||||
|
||||
override fun toString(): String {
|
||||
return "ReinitializeWallets(wallets = ${wallets.joinToString { it.walletId.toString() }}"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename wallets
|
||||
*
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
|
|||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.staking.YieldBalance
|
||||
import com.tangem.domain.models.staking.StakingBalance
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.wallet.isLocked
|
||||
|
|
@ -250,7 +250,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
token = currencyStatus.currency.symbol,
|
||||
blockchain = currencyStatus.currency.network.name,
|
||||
action = "Staking",
|
||||
state = if (currencyStatus.value.yieldBalance is YieldBalance.Data) {
|
||||
state = if (currencyStatus.value.stakingBalance is StakingBalance.Data) {
|
||||
"Enabled"
|
||||
} else {
|
||||
"Disabled"
|
||||
|
|
|
|||
|
|
@ -11,9 +11,7 @@ import com.tangem.core.analytics.models.AnalyticsParam
|
|||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.components.bottomsheets.message.*
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.bottomSheetMessage
|
||||
import com.tangem.domain.card.SetCardWasScannedUseCase
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
|
||||
|
|
@ -33,14 +31,13 @@ import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
|
|||
import com.tangem.domain.settings.NeverToSuggestRateAppUseCase
|
||||
import com.tangem.domain.settings.RemindToRateAppLaterUseCase
|
||||
import com.tangem.domain.staking.StakingIdFactory
|
||||
import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher
|
||||
import com.tangem.domain.staking.multi.MultiStakingBalanceFetcher
|
||||
import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent
|
||||
import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent.Program
|
||||
import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent.PromotionBannerClicked
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager.Lockable.UnlockType
|
||||
import com.tangem.domain.wallets.models.UnlockWalletsError
|
||||
import com.tangem.domain.wallets.usecase.*
|
||||
import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.Basic
|
||||
|
|
@ -106,7 +103,7 @@ internal interface WalletWarningsClickIntents {
|
|||
|
||||
fun onDenyPermissions()
|
||||
|
||||
fun onFinishWalletActivationClick(bannerType: WalletActivationBannerType, isBackupExists: Boolean)
|
||||
fun onFinishWalletActivationClick(isBackupExists: Boolean)
|
||||
}
|
||||
|
||||
@Suppress("LargeClass", "LongParameterList", "TooManyFunctions")
|
||||
|
|
@ -131,7 +128,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
private val urlOpener: UrlOpener,
|
||||
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
||||
private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
|
||||
private val multiYieldBalanceFetcher: MultiYieldBalanceFetcher,
|
||||
private val multiStakingBalanceFetcher: MultiStakingBalanceFetcher,
|
||||
private val stakingIdFactory: StakingIdFactory,
|
||||
private val appRouter: AppRouter,
|
||||
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
|
||||
|
|
@ -460,40 +457,10 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onFinishWalletActivationClick(bannerType: WalletActivationBannerType, isBackupExists: Boolean) {
|
||||
when (bannerType) {
|
||||
WalletActivationBannerType.Attention -> {
|
||||
val userWallet = getSelectedUserWallet() ?: return
|
||||
appRouter.push(WalletActivation(userWallet.walletId, isBackupExists))
|
||||
}
|
||||
WalletActivationBannerType.Warning -> {
|
||||
val message = bottomSheetMessage {
|
||||
infoBlock {
|
||||
icon(R.drawable.img_knight_shield_32) {
|
||||
type = MessageBottomSheetUMV2.Icon.Type.Warning
|
||||
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint
|
||||
}
|
||||
title = resourceReference(R.string.hw_activation_need_title)
|
||||
body = resourceReference(R.string.hw_activation_need_description)
|
||||
}
|
||||
secondaryButton {
|
||||
text = resourceReference(R.string.common_later)
|
||||
onClick {
|
||||
closeBs()
|
||||
}
|
||||
}
|
||||
primaryButton {
|
||||
text = resourceReference(R.string.hw_activation_need_backup)
|
||||
onClick {
|
||||
val userWallet = getSelectedUserWallet() ?: return@onClick
|
||||
appRouter.push(WalletActivation(userWallet.walletId, isBackupExists))
|
||||
closeBs()
|
||||
}
|
||||
}
|
||||
}
|
||||
uiMessageSender.send(message)
|
||||
}
|
||||
}
|
||||
override fun onFinishWalletActivationClick(isBackupExists: Boolean) {
|
||||
analyticsEventHandler.send(MainScreen.ButtonFinalizeActivation)
|
||||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
appRouter.push(WalletActivation(userWalletId, isBackupExists))
|
||||
}
|
||||
|
||||
override fun onAllowPermissions() {
|
||||
|
|
@ -562,8 +529,11 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
|
|||
stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = it).getOrNull()
|
||||
}
|
||||
|
||||
multiYieldBalanceFetcher(
|
||||
params = MultiYieldBalanceFetcher.Params(userWalletId = userWalletId, stakingIds = stakingIds),
|
||||
multiStakingBalanceFetcher(
|
||||
params = MultiStakingBalanceFetcher.Params(
|
||||
userWalletId = userWalletId,
|
||||
stakingIds = stakingIds,
|
||||
),
|
||||
)
|
||||
.onLeft { Timber.e("Unable to fetch yield balances: $it") }
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.tangem.feature.wallet.presentation.common.preview
|
|||
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsState
|
||||
import com.tangem.core.ui.components.token.AccountItemPreviewData
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
|
|
@ -14,6 +13,7 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData.topBarConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.*
|
||||
|
|
@ -167,11 +167,12 @@ internal object WalletScreenPreviewData {
|
|||
warnings = persistentListOf(
|
||||
WalletNotification.Warning.SomeNetworksUnreachable,
|
||||
WalletNotification.FinishWalletActivation(
|
||||
iconTint = NotificationConfig.IconTint.Attention,
|
||||
type = WalletActivationBannerType.Attention,
|
||||
buttonsState = ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.hw_activation_need_finish),
|
||||
onClick = { },
|
||||
),
|
||||
isBackupExists = false,
|
||||
),
|
||||
),
|
||||
bottomSheetConfig = null,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import com.tangem.core.ui.format.bigdecimal.fiat
|
|||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.staking.YieldBalance
|
||||
import com.tangem.domain.models.staking.StakingBalance
|
||||
import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getGroupHeaderId
|
||||
|
|
@ -63,12 +63,12 @@ internal class CryptoCurrencyToDraggableItemConverter(
|
|||
}
|
||||
|
||||
private fun getFormattedFiatAmount(currency: CryptoCurrencyStatus, appCurrency: AppCurrency): String {
|
||||
val yieldBalance = currency.value.yieldBalance as? YieldBalance.Data
|
||||
val stakingBalance = currency.value.stakingBalance as? StakingBalance.Data
|
||||
val fiatRate = currency.value.fiatRate ?: BigDecimal.ZERO
|
||||
val fiatYieldBalance = yieldBalance?.getTotalWithRewardsStakingBalance(currency.currency.network.rawId)
|
||||
val fiatStakingBalance = stakingBalance?.getTotalWithRewardsStakingBalance(currency.currency.network.rawId)
|
||||
?.multiply(fiatRate).orZero()
|
||||
|
||||
val fiatAmount = currency.value.fiatAmount ?: return BigDecimalFormatConstants.EMPTY_BALANCE_SIGN
|
||||
return (fiatAmount + fiatYieldBalance).format { fiat(appCurrency.code, appCurrency.symbol) }
|
||||
return (fiatAmount + fiatStakingBalance).format { fiat(appCurrency.code, appCurrency.symbol) }
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ import com.tangem.core.ui.format.bigdecimal.format
|
|||
import com.tangem.domain.account.status.model.AccountCryptoCurrencyStatus
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.staking.YieldBalance
|
||||
import com.tangem.domain.models.staking.StakingBalance
|
||||
import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem
|
||||
import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getGroupHeaderId
|
||||
|
|
@ -56,12 +56,12 @@ internal class CryptoCurrencyToDraggableItemConverterV2(
|
|||
}
|
||||
|
||||
private fun getFormattedFiatAmount(currency: CryptoCurrencyStatus, appCurrency: AppCurrency): String {
|
||||
val yieldBalance = currency.value.yieldBalance as? YieldBalance.Data
|
||||
val stakingBalance = currency.value.stakingBalance as? StakingBalance.Data
|
||||
val fiatRate = currency.value.fiatRate ?: BigDecimal.ZERO
|
||||
val fiatYieldBalance = yieldBalance?.getTotalWithRewardsStakingBalance(currency.currency.network.rawId)
|
||||
val fiatStakingBalance = stakingBalance?.getTotalWithRewardsStakingBalance(currency.currency.network.rawId)
|
||||
?.multiply(fiatRate).orZero()
|
||||
|
||||
val fiatAmount = currency.value.fiatAmount ?: return BigDecimalFormatConstants.EMPTY_BALANCE_SIGN
|
||||
return (fiatAmount + fiatYieldBalance).format { fiat(appCurrency.code, appCurrency.symbol) }
|
||||
return (fiatAmount + fiatStakingBalance).format { fiat(appCurrency.code, appCurrency.symbol) }
|
||||
}
|
||||
}
|
||||
|
|
@ -51,7 +51,30 @@ sealed class WalletScreenAnalyticsEvent {
|
|||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent(category = "Main Screen", event = event, params = params) {
|
||||
|
||||
data object ScreenOpened : MainScreen(event = "Screen opened")
|
||||
data object ScreenOpenedLegacy : MainScreen(
|
||||
event = "Screen opened",
|
||||
)
|
||||
|
||||
data class ScreenOpened(
|
||||
private val hasMobileWallet: Boolean,
|
||||
) : MainScreen(
|
||||
event = "Screen opened",
|
||||
params = mapOf("Mobile Wallet" to if (hasMobileWallet) "Yes" else "No"),
|
||||
)
|
||||
|
||||
data class NoticeFinishActivation(private val activationState: ActivationState) : MainScreen(
|
||||
event = "Notice - Finish Activation",
|
||||
params = mapOf("Activation State" to activationState.value),
|
||||
) {
|
||||
enum class ActivationState(val value: String) {
|
||||
NotStarted("Not Started"),
|
||||
Unfinished("Unfinished"),
|
||||
}
|
||||
}
|
||||
|
||||
data object ButtonFinalizeActivation : MainScreen(
|
||||
event = "Button - Finalize Activation",
|
||||
)
|
||||
|
||||
class WalletSelected(val isImported: Boolean) : MainScreen(
|
||||
event = "Wallet Selected",
|
||||
|
|
|
|||
|
|
@ -70,9 +70,16 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
|
|||
is WalletNotification.Warning.NetworksUnreachable,
|
||||
is WalletNotification.UsedOutdatedData,
|
||||
is WalletNotification.UnlockVisaAccess,
|
||||
is WalletNotification.FinishWalletActivation,
|
||||
is WalletNotification.Warning.YeildSupplyApprove, // TODO apply correct event
|
||||
-> null
|
||||
is WalletNotification.FinishWalletActivation -> {
|
||||
val activationState = if (warning.isBackupExists) {
|
||||
MainScreen.NoticeFinishActivation.ActivationState.Unfinished
|
||||
} else {
|
||||
MainScreen.NoticeFinishActivation.ActivationState.NotStarted
|
||||
}
|
||||
MainScreen.NoticeFinishActivation(activationState)
|
||||
}
|
||||
is WalletNotification.Critical.SeedPhraseNotification -> MainScreen.NoticeSeedPhraseSupport
|
||||
is WalletNotification.Critical.SeedPhraseSecondNotification -> MainScreen.NoticeSeedPhraseSupportSecond
|
||||
is WalletNotification.PushNotifications -> WalletScreenAnalyticsEvent.PushBannerPromo.PushBanner
|
||||
|
|
|
|||
|
|
@ -1,8 +1,24 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.analytics.utils
|
||||
|
||||
import arrow.atomic.AtomicBoolean
|
||||
import com.tangem.common.routing.AppRoute.WalletActivation
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.components.bottomsheets.message.MessageBottomSheetUMV2
|
||||
import com.tangem.core.ui.components.bottomsheets.message.icon
|
||||
import com.tangem.core.ui.components.bottomsheets.message.infoBlock
|
||||
import com.tangem.core.ui.components.bottomsheets.message.onClick
|
||||
import com.tangem.core.ui.components.bottomsheets.message.primaryButton
|
||||
import com.tangem.core.ui.components.bottomsheets.message.secondaryButton
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.bottomSheetMessage
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.domain.wallets.usecase.SeedPhraseNotificationUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider
|
||||
|
|
@ -12,8 +28,13 @@ import javax.inject.Inject
|
|||
internal class WalletWarningsSingleEventSender @Inject constructor(
|
||||
private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase,
|
||||
private val screenLifecycleProvider: ScreenLifecycleProvider,
|
||||
private val uiMessageSender: UiMessageSender,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val router: Router,
|
||||
) {
|
||||
|
||||
private val isActivationBottomSheetShown: AtomicBoolean = AtomicBoolean(false)
|
||||
|
||||
suspend fun send(
|
||||
userWalletId: UserWalletId,
|
||||
displayedUiState: WalletState?,
|
||||
|
|
@ -26,9 +47,49 @@ internal class WalletWarningsSingleEventSender @Inject constructor(
|
|||
val events = newWarnings.filter { it !in displayedUiState.warnings }
|
||||
|
||||
events.forEach { event ->
|
||||
if (event is WalletNotification.Critical.SeedPhraseNotification) {
|
||||
seedPhraseNotificationUseCase.notified(userWalletId = userWalletId)
|
||||
when (event) {
|
||||
is WalletNotification.Critical.SeedPhraseNotification -> {
|
||||
seedPhraseNotificationUseCase.notified(userWalletId = userWalletId)
|
||||
}
|
||||
is WalletNotification.FinishWalletActivation -> {
|
||||
if (event.type == WalletActivationBannerType.Warning && !isActivationBottomSheetShown.get()) {
|
||||
showFinishActivationBottomSheet(userWalletId)
|
||||
isActivationBottomSheetShown.set(true)
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showFinishActivationBottomSheet(userWalletId: UserWalletId) {
|
||||
val userWallet = getUserWalletUseCase(userWalletId).getOrNull() ?: return
|
||||
if (userWallet !is UserWallet.Hot) return
|
||||
|
||||
val message = bottomSheetMessage {
|
||||
infoBlock {
|
||||
icon(R.drawable.img_knight_shield_32) {
|
||||
type = MessageBottomSheetUMV2.Icon.Type.Warning
|
||||
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint
|
||||
}
|
||||
title = resourceReference(R.string.hw_activation_need_title)
|
||||
body = resourceReference(R.string.hw_activation_need_description)
|
||||
}
|
||||
secondaryButton {
|
||||
text = resourceReference(R.string.common_later)
|
||||
onClick {
|
||||
closeBs()
|
||||
}
|
||||
}
|
||||
primaryButton {
|
||||
text = resourceReference(R.string.hw_activation_need_backup)
|
||||
onClick {
|
||||
router.push(WalletActivation(userWallet.walletId, userWallet.backedUp))
|
||||
closeBs()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uiMessageSender.send(message)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue