Updated on 2026-08-14
This commit is contained in:
parent
d5cbaf22cf
commit
f5be772c91
53 changed files with 219 additions and 276 deletions
|
|
@ -18,9 +18,7 @@
|
||||||
<ID>CanBeNonNullable:NotificationsFactory.kt$NotificationsFactory$minimumSendAmount: BigDecimal?</ID>
|
<ID>CanBeNonNullable:NotificationsFactory.kt$NotificationsFactory$minimumSendAmount: BigDecimal?</ID>
|
||||||
<ID>CanBeNonNullable:NotificationsFactory.kt$NotificationsFactory$rentWarning: CryptoCurrencyWarning.Rent?</ID>
|
<ID>CanBeNonNullable:NotificationsFactory.kt$NotificationsFactory$rentWarning: CryptoCurrencyWarning.Rent?</ID>
|
||||||
<ID>MultilineLambdaItParameter:ExpressStatusItems.kt${ val itemInfo = expressTxs[it].info val (iconRes, tint) = when (itemInfo.iconState) { ExpressTransactionStateIconUM.Warning -> { R.drawable.ic_alert_triangle_20 to TangemTheme.colors.icon.attention } ExpressTransactionStateIconUM.Error -> { R.drawable.ic_alert_circle_24 to TangemTheme.colors.icon.warning } ExpressTransactionStateIconUM.None -> null to null } ExpressStatusItem( title = itemInfo.title, fromTokenIconState = itemInfo.fromCurrencyIcon, toTokenIconState = itemInfo.toCurrencyIcon, fromAmount = itemInfo.fromAmount, fromSymbol = itemInfo.fromAmountSymbol, toAmount = itemInfo.toAmount, toSymbol = itemInfo.toAmountSymbol, onClick = itemInfo.onClick, infoIconRes = iconRes, infoIconTint = tint, modifier = modifier.animateItem(), ) }</ID>
|
<ID>MultilineLambdaItParameter:ExpressStatusItems.kt${ val itemInfo = expressTxs[it].info val (iconRes, tint) = when (itemInfo.iconState) { ExpressTransactionStateIconUM.Warning -> { R.drawable.ic_alert_triangle_20 to TangemTheme.colors.icon.attention } ExpressTransactionStateIconUM.Error -> { R.drawable.ic_alert_circle_24 to TangemTheme.colors.icon.warning } ExpressTransactionStateIconUM.None -> null to null } ExpressStatusItem( title = itemInfo.title, fromTokenIconState = itemInfo.fromCurrencyIcon, toTokenIconState = itemInfo.toCurrencyIcon, fromAmount = itemInfo.fromAmount, fromSymbol = itemInfo.fromAmountSymbol, toAmount = itemInfo.toAmount, toSymbol = itemInfo.toAmountSymbol, onClick = itemInfo.onClick, infoIconRes = iconRes, infoIconTint = tint, modifier = modifier.animateItem(), ) }</ID>
|
||||||
<ID>MultilineLambdaItParameter:TokenItemStateConverter.kt$TokenItemStateConverter${ createTitleState(it, yieldModuleApyMap, stakingApyMap, { onApyLabelClick?.invoke(it) }) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:TokenItemStateConverter.kt$TokenItemStateConverter.Companion${ it.key.equals( other = token.yieldSupplyKey(), ignoreCase = BlockchainUtils.isCaseInsensitiveContractAddress(token.network.rawId), ) }</ID>
|
<ID>MultilineLambdaItParameter:TokenItemStateConverter.kt$TokenItemStateConverter.Companion${ it.key.equals( other = token.yieldSupplyKey(), ignoreCase = BlockchainUtils.isCaseInsensitiveContractAddress(token.network.rawId), ) }</ID>
|
||||||
<ID>NamedArguments:TokenItemStateConverter.kt$TokenItemStateConverter$createTitleState(it, yieldModuleApyMap, stakingApyMap, { onApyLabelClick?.invoke(it) })</ID>
|
|
||||||
<ID>NoNameShadowing:NavigationButtonsBlock.kt$navigationUM</ID>
|
<ID>NoNameShadowing:NavigationButtonsBlock.kt$navigationUM</ID>
|
||||||
<ID>NoNameShadowing:UserWalletItem.kt$balance</ID>
|
<ID>NoNameShadowing:UserWalletItem.kt$balance</ID>
|
||||||
<ID>NullableBooleanCheck:TokenItemStateConverter.kt$TokenItemStateConverter.Companion$cryptoCurrencyStatus.value.yieldSupplyStatus?.isActive ?: false</ID>
|
<ID>NullableBooleanCheck:TokenItemStateConverter.kt$TokenItemStateConverter.Companion$cryptoCurrencyStatus.value.yieldSupplyStatus?.isActive ?: false</ID>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
<ManuallySuppressedIssues/>
|
<ManuallySuppressedIssues/>
|
||||||
<CurrentIssues>
|
<CurrentIssues>
|
||||||
<ID>MultilineLambdaItParameter:DefaultNFTRepository.kt$DefaultNFTRepository${ NFTCollections( network = network, content = NFTCollections.Content.Collections( collections = it ?.map { collection -> nftSdkCollectionConverter.convert(network to collection) } ?.filter { it.id !is NFTCollection.Identifier.Unknown }, source = StatusSource.CACHE, ), ) }</ID>
|
<ID>MultilineLambdaItParameter:DefaultNFTRepository.kt$DefaultNFTRepository${ NFTCollections( network = network, content = NFTCollections.Content.Collections( collections = it ?.map { collection -> nftSdkCollectionConverter.convert(network to collection) } ?.filter { it.id !is NFTCollection.Identifier.Unknown }, source = StatusSource.CACHE, ), ) }</ID>
|
||||||
<ID>MultilineLambdaItParameter:DefaultNFTRepository.kt$DefaultNFTRepository${ getNFTPersistenceStore(userWalletId, it).clear() getNFTRuntimeStore(userWalletId, it).clear() }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:DefaultNFTRepository.kt$DefaultNFTRepository${ if (it !is UnsupportedOperationException) { saveFailedStateInRuntime( userWalletId = userWalletId, network = network, error = it, ) } }</ID>
|
<ID>MultilineLambdaItParameter:DefaultNFTRepository.kt$DefaultNFTRepository${ if (it !is UnsupportedOperationException) { saveFailedStateInRuntime( userWalletId = userWalletId, network = network, error = it, ) } }</ID>
|
||||||
<ID>MultilineLambdaItParameter:DefaultNFTRepository.kt$DefaultNFTRepository${ if (it.id == collectionId) { it.changeAssetsStatusSource(source) } else { it } }</ID>
|
<ID>MultilineLambdaItParameter:DefaultNFTRepository.kt$DefaultNFTRepository${ if (it.id == collectionId) { it.changeAssetsStatusSource(source) } else { it } }</ID>
|
||||||
<ID>MultilineLambdaItParameter:DefaultNFTRepository.kt$DefaultNFTRepository${ if (it.identifier == sdkCollectionId) { it.copy(assets = assets) } else { it } }</ID>
|
<ID>MultilineLambdaItParameter:DefaultNFTRepository.kt$DefaultNFTRepository${ if (it.identifier == sdkCollectionId) { it.copy(assets = assets) } else { it } }</ID>
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@
|
||||||
<ID>NullCheckOnMutableProperty:VisaLibLoader.kt$VisaLibLoader$if (config != null) return@withLock requireNotNull(config)</ID>
|
<ID>NullCheckOnMutableProperty:VisaLibLoader.kt$VisaLibLoader$if (config != null) return@withLock requireNotNull(config)</ID>
|
||||||
<ID>NullCheckOnMutableProperty:VisaLibLoader.kt$VisaLibLoader$if (provider != null) return@withLock requireNotNull(provider)</ID>
|
<ID>NullCheckOnMutableProperty:VisaLibLoader.kt$VisaLibLoader$if (provider != null) return@withLock requireNotNull(provider)</ID>
|
||||||
<ID>NullableToStringCall:DefaultOnboardingRepository.kt$DefaultOnboardingRepository$${error.message}</ID>
|
<ID>NullableToStringCall:DefaultOnboardingRepository.kt$DefaultOnboardingRepository$${error.message}</ID>
|
||||||
<ID>NullableToStringCall:TangemPayRequestPerformer.kt$TangemPayRequestPerformer$${error.message}</ID>
|
|
||||||
<ID>RedundantSuspendModifier:DefaultVisaRepository.kt$DefaultVisaRepository$suspend</ID>
|
<ID>RedundantSuspendModifier:DefaultVisaRepository.kt$DefaultVisaRepository$suspend</ID>
|
||||||
<ID>SuspendFunSwallowedCancellation:DefaultVisaRepository.kt$DefaultVisaRepository$runCatching</ID>
|
<ID>SuspendFunSwallowedCancellation:DefaultVisaRepository.kt$DefaultVisaRepository$runCatching</ID>
|
||||||
<ID>SuspendFunSwallowedCancellation:VisaApiRequestMaker.kt$VisaApiRequestMaker$runCatching</ID>
|
<ID>SuspendFunSwallowedCancellation:VisaApiRequestMaker.kt$VisaApiRequestMaker$runCatching</ID>
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,6 @@
|
||||||
<ID>SuspendFunSwallowedCancellation:DefaultWcPairUseCase.kt$DefaultWcPairUseCase$runCatching</ID>
|
<ID>SuspendFunSwallowedCancellation:DefaultWcPairUseCase.kt$DefaultWcPairUseCase$runCatching</ID>
|
||||||
<ID>UseAnyOrNoneInsteadOfFind:DefaultWcSessionsManager.kt$DefaultWcSessionsManager$find { it.sdkModel.topic == dto.topic }</ID>
|
<ID>UseAnyOrNoneInsteadOfFind:DefaultWcSessionsManager.kt$DefaultWcSessionsManager$find { it.sdkModel.topic == dto.topic }</ID>
|
||||||
<ID>UseEmptyCounterpart:AssociateNetworksDelegate.kt$AssociateNetworksDelegate.Companion$listOf()</ID>
|
<ID>UseEmptyCounterpart:AssociateNetworksDelegate.kt$AssociateNetworksDelegate.Companion$listOf()</ID>
|
||||||
<ID>UseEmptyCounterpart:DefaultWcRequestService.kt$DefaultWcRequestService$setOf()</ID>
|
|
||||||
<ID>UseEmptyCounterpart:WcAppMetaDataConverter.kt$WcAppMetaDataConverter$listOf()</ID>
|
<ID>UseEmptyCounterpart:WcAppMetaDataConverter.kt$WcAppMetaDataConverter$listOf()</ID>
|
||||||
<ID>UseEmptyCounterpart:WcNetworksConverter.kt$WcNetworksConverter$listOf()</ID>
|
<ID>UseEmptyCounterpart:WcNetworksConverter.kt$WcNetworksConverter$listOf()</ID>
|
||||||
<ID>UseEmptyCounterpart:WcSdkSessionConverter.kt$WcSdkSessionConverter$listOf()</ID>
|
<ID>UseEmptyCounterpart:WcSdkSessionConverter.kt$WcSdkSessionConverter$listOf()</ID>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
==========================================
|
==========================================
|
||||||
Detekt Baseline Updater & Issue Counter
|
Detekt Baseline Updater & Issue Counter
|
||||||
==========================================
|
==========================================
|
||||||
Date: 2025-12-04 17:37:53
|
Date: 2025-12-08 12:25:24
|
||||||
|
|
||||||
Step 1: Running detekt to check for new issues...
|
Step 1: Running detekt to check for new issues...
|
||||||
|
|
||||||
|
|
@ -17,13 +17,13 @@ Counting issues in baseline files...
|
||||||
==========================================
|
==========================================
|
||||||
|
|
||||||
Summary:
|
Summary:
|
||||||
Total Issues: 1380
|
Total Issues: 1311
|
||||||
Modules with Issues: 50
|
Modules with Issues: 48
|
||||||
Average Issues per Module: 27
|
Average Issues per Module: 27
|
||||||
|
|
||||||
Progress:
|
Progress:
|
||||||
Fixed: 422 out of 1802 (23%)
|
Fixed: 491 out of 1802 (27%)
|
||||||
Remaining: 1380
|
Remaining: 1311
|
||||||
|
|
||||||
==========================================
|
==========================================
|
||||||
All Modules with Issues (sorted by count)
|
All Modules with Issues (sorted by count)
|
||||||
|
|
@ -35,9 +35,8 @@ features/markets/impl 148
|
||||||
features/wallet/impl 145
|
features/wallet/impl 145
|
||||||
features/onboarding-v2/impl 130
|
features/onboarding-v2/impl 130
|
||||||
features/swap/impl 67
|
features/swap/impl 67
|
||||||
features/send-v2/impl 57
|
data/wallet-connect 54
|
||||||
data/wallet-connect 55
|
features/hot-wallet/impl 51
|
||||||
features/hot-wallet/impl 53
|
|
||||||
features/walletconnect/impl 47
|
features/walletconnect/impl 47
|
||||||
features/tokendetails/impl 46
|
features/tokendetails/impl 46
|
||||||
features/staking/impl 46
|
features/staking/impl 46
|
||||||
|
|
@ -47,13 +46,13 @@ domain/wallets 37
|
||||||
features/nft/impl 34
|
features/nft/impl 34
|
||||||
features/tester/impl 30
|
features/tester/impl 30
|
||||||
core/ui 27
|
core/ui 27
|
||||||
common/ui 26
|
|
||||||
features/swap/domain 25
|
features/swap/domain 25
|
||||||
domain/tokens 25
|
domain/tokens 25
|
||||||
data/visa 22
|
common/ui 24
|
||||||
features/yield-supply/impl 21
|
features/yield-supply/impl 21
|
||||||
features/tangempay/details/impl 21
|
features/tangempay/details/impl 21
|
||||||
data/nft 20
|
data/visa 21
|
||||||
|
data/nft 19
|
||||||
features/swap/data 15
|
features/swap/data 15
|
||||||
data/wallets 13
|
data/wallets 13
|
||||||
data/swap 13
|
data/swap 13
|
||||||
|
|
@ -73,7 +72,6 @@ libs/tangem-sdk-api 7
|
||||||
data/txhistory 7
|
data/txhistory 7
|
||||||
features/welcome/impl 6
|
features/welcome/impl 6
|
||||||
libs/visa 5
|
libs/visa 5
|
||||||
features/send-v2/api 5
|
|
||||||
features/home/impl 5
|
features/home/impl 5
|
||||||
domain/markets 5
|
domain/markets 5
|
||||||
data/account 5
|
data/account 5
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
<ID>BooleanPropertyNaming:ForgetWalletUM.kt$ForgetWalletUM$val secondCheckboxChecked: Boolean</ID>
|
<ID>BooleanPropertyNaming:ForgetWalletUM.kt$ForgetWalletUM$val secondCheckboxChecked: Boolean</ID>
|
||||||
<ID>BooleanPropertyNaming:HotAccessCodeRequestUM.kt$HotAccessCodeRequestUM$val useBiometricVisible: Boolean = true</ID>
|
<ID>BooleanPropertyNaming:HotAccessCodeRequestUM.kt$HotAccessCodeRequestUM$val useBiometricVisible: Boolean = true</ID>
|
||||||
<ID>BooleanPropertyNaming:HotWalletStepperComponent.kt$HotWalletStepperComponent.StepperUM$val showBackButton: Boolean</ID>
|
<ID>BooleanPropertyNaming:HotWalletStepperComponent.kt$HotWalletStepperComponent.StepperUM$val showBackButton: Boolean</ID>
|
||||||
<ID>BooleanPropertyNaming:HotWalletStepperComponent.kt$HotWalletStepperComponent.StepperUM$val showFeedbackButton: Boolean</ID>
|
|
||||||
<ID>BooleanPropertyNaming:HotWalletStepperComponent.kt$HotWalletStepperComponent.StepperUM$val showSkipButton: Boolean</ID>
|
<ID>BooleanPropertyNaming:HotWalletStepperComponent.kt$HotWalletStepperComponent.StepperUM$val showSkipButton: Boolean</ID>
|
||||||
<ID>BooleanPropertyNaming:ManualBackupCheckUM.kt$ManualBackupCheckUM$val completeButtonEnabled: Boolean</ID>
|
<ID>BooleanPropertyNaming:ManualBackupCheckUM.kt$ManualBackupCheckUM$val completeButtonEnabled: Boolean</ID>
|
||||||
<ID>BooleanPropertyNaming:ManualBackupCheckUM.kt$ManualBackupCheckUM$val completeButtonProgress: Boolean</ID>
|
<ID>BooleanPropertyNaming:ManualBackupCheckUM.kt$ManualBackupCheckUM$val completeButtonProgress: Boolean</ID>
|
||||||
|
|
@ -51,7 +50,6 @@
|
||||||
<ID>PropertyUsedBeforeDeclaration:AddExistingWalletImportModel.kt$AddExistingWalletImportModel$uiState</ID>
|
<ID>PropertyUsedBeforeDeclaration:AddExistingWalletImportModel.kt$AddExistingWalletImportModel$uiState</ID>
|
||||||
<ID>ReusedModifierInstance:AddExistingWalletImportContent.kt$OutlineTextFieldWithIcon( modifier = modifier .padding(horizontal = 16.dp) .fillMaxWidth(), value = state.passPhrase, onValueChange = state.passPhraseChange, iconResId = R.drawable.ic_information_24, iconColor = TangemTheme.colors.icon.informative, label = stringResourceSafe(id = R.string.common_passphrase), placeholder = stringResourceSafe(id = R.string.send_optional_field), onIconClick = state.onPassphraseInfoClick, keyboardOptions = KeyboardOptions( autoCorrectEnabled = false, keyboardType = KeyboardType.Password, ), )</ID>
|
<ID>ReusedModifierInstance:AddExistingWalletImportContent.kt$OutlineTextFieldWithIcon( modifier = modifier .padding(horizontal = 16.dp) .fillMaxWidth(), value = state.passPhrase, onValueChange = state.passPhraseChange, iconResId = R.drawable.ic_information_24, iconColor = TangemTheme.colors.icon.informative, label = stringResourceSafe(id = R.string.common_passphrase), placeholder = stringResourceSafe(id = R.string.send_optional_field), onIconClick = state.onPassphraseInfoClick, keyboardOptions = KeyboardOptions( autoCorrectEnabled = false, keyboardType = KeyboardType.Password, ), )</ID>
|
||||||
<ID>ReusedModifierInstance:HotAccessCodeRequestFullScreenContent.kt$AnimatedVisibility( modifier = modifier, visible = state.isShown, enter = fadeIn(), exit = fadeOut(), ) { Column( Modifier .fillMaxSize() .background(TangemTheme.colors.background.primary), horizontalAlignment = Alignment.CenterHorizontally, ) { TangemTopAppBar( modifier = Modifier.statusBarsPadding(), startButton = TopAppBarButtonUM.Back(state.onDismiss), ) SpacerH(68.dp) Column( Modifier .weight(1f) .fillMaxWidth() .padding(horizontal = 24.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { Text( modifier = Modifier.animateEnterExit( enter = slideInVertically( tween(), initialOffsetY = { it + 200 }, ) + fadeIn(tween()), exit = slideOutVertically(tween(300)) { it - 200 } + fadeOut(tween()), ), text = stringResourceSafe(R.string.access_code_check_title), style = TangemTheme.typography.h2, color = TangemTheme.colors.text.primary1, ) SpacerH24() PinTextField( modifier = Modifier.animateEnterExit( enter = slideInVertically( tween(), initialOffsetY = { it + 200 }, ) + fadeIn(tween()), exit = slideOutVertically(tween(300)) { it - 200 } + fadeOut(tween()), ), length = 6, isPasswordVisual = true, value = state.accessCode, pinTextColor = state.accessCodeColor, onValueChange = state.onAccessCodeChange, ) SpacerH(20.dp) AnimatedVisibility( modifier = Modifier.animateEnterExit( enter = slideInVertically( tween(), initialOffsetY = { it + 200 }, ) + fadeIn(tween()), exit = slideOutVertically(tween(300)) { it - 200 } + fadeOut(tween()), ), visible = state.wrongAccessCodeText != null, enter = fadeIn(), exit = fadeOut(), ) { val wrongAccessCodeText = state.wrongAccessCodeText ?: return@AnimatedVisibility Text( text = wrongAccessCodeText.resolveReference(), textAlign = TextAlign.Center, style = TangemTheme.typography.caption2.copy( lineBreak = LineBreak.Heading, ), color = TangemTheme.colors.text.warning, ) } } AnimatedVisibility( visible = state.useBiometricVisible, enter = fadeIn(), exit = fadeOut(), ) { SecondaryButton( modifier = Modifier .padding(16.dp) .fillMaxWidth() .navigationBarsPadding() .imePadding(), text = stringResourceSafe( id = R.string.welcome_unlock, stringResourceSafe(R.string.common_biometrics), ), onClick = state.useBiometricClick, ) } } }</ID>
|
<ID>ReusedModifierInstance:HotAccessCodeRequestFullScreenContent.kt$AnimatedVisibility( modifier = modifier, visible = state.isShown, enter = fadeIn(), exit = fadeOut(), ) { Column( Modifier .fillMaxSize() .background(TangemTheme.colors.background.primary), horizontalAlignment = Alignment.CenterHorizontally, ) { TangemTopAppBar( modifier = Modifier.statusBarsPadding(), startButton = TopAppBarButtonUM.Back(state.onDismiss), ) SpacerH(68.dp) Column( Modifier .weight(1f) .fillMaxWidth() .padding(horizontal = 24.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { Text( modifier = Modifier.animateEnterExit( enter = slideInVertically( tween(), initialOffsetY = { it + 200 }, ) + fadeIn(tween()), exit = slideOutVertically(tween(300)) { it - 200 } + fadeOut(tween()), ), text = stringResourceSafe(R.string.access_code_check_title), style = TangemTheme.typography.h2, color = TangemTheme.colors.text.primary1, ) SpacerH24() PinTextField( modifier = Modifier.animateEnterExit( enter = slideInVertically( tween(), initialOffsetY = { it + 200 }, ) + fadeIn(tween()), exit = slideOutVertically(tween(300)) { it - 200 } + fadeOut(tween()), ), length = 6, isPasswordVisual = true, value = state.accessCode, pinTextColor = state.accessCodeColor, onValueChange = state.onAccessCodeChange, ) SpacerH(20.dp) AnimatedVisibility( modifier = Modifier.animateEnterExit( enter = slideInVertically( tween(), initialOffsetY = { it + 200 }, ) + fadeIn(tween()), exit = slideOutVertically(tween(300)) { it - 200 } + fadeOut(tween()), ), visible = state.wrongAccessCodeText != null, enter = fadeIn(), exit = fadeOut(), ) { val wrongAccessCodeText = state.wrongAccessCodeText ?: return@AnimatedVisibility Text( text = wrongAccessCodeText.resolveReference(), textAlign = TextAlign.Center, style = TangemTheme.typography.caption2.copy( lineBreak = LineBreak.Heading, ), color = TangemTheme.colors.text.warning, ) } } AnimatedVisibility( visible = state.useBiometricVisible, enter = fadeIn(), exit = fadeOut(), ) { SecondaryButton( modifier = Modifier .padding(16.dp) .fillMaxWidth() .navigationBarsPadding() .imePadding(), text = stringResourceSafe( id = R.string.welcome_unlock, stringResourceSafe(R.string.common_biometrics), ), onClick = state.useBiometricClick, ) } } }</ID>
|
||||||
<ID>ReusedModifierInstance:HotWalletStepper.kt$TangemTopAppBar( startButton = if (state.showBackButton) { TopAppBarButtonUM.Back(onBackClick) } else { null }, endButton = when { 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 }, title = state.title, containerColor = TangemTheme.colors.background.primary, modifier = modifier, titleAlignment = Alignment.CenterHorizontally, )</ID>
|
|
||||||
<ID>SuspendFunSwallowedCancellation:AddExistingWalletImportModel.kt$AddExistingWalletImportModel$runCatching</ID>
|
<ID>SuspendFunSwallowedCancellation:AddExistingWalletImportModel.kt$AddExistingWalletImportModel$runCatching</ID>
|
||||||
<ID>SuspendFunSwallowedCancellation:ManualBackupCheckModel.kt$ManualBackupCheckModel$runCatching</ID>
|
<ID>SuspendFunSwallowedCancellation:ManualBackupCheckModel.kt$ManualBackupCheckModel$runCatching</ID>
|
||||||
<ID>SuspendFunSwallowedCancellation:ManualBackupPhraseModel.kt$ManualBackupPhraseModel$runCatching</ID>
|
<ID>SuspendFunSwallowedCancellation:ManualBackupPhraseModel.kt$ManualBackupPhraseModel$runCatching</ID>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
<?xml version="1.0" ?>
|
|
||||||
<SmellBaseline>
|
|
||||||
<ManuallySuppressedIssues/>
|
|
||||||
<CurrentIssues>
|
|
||||||
<ID>BooleanPropertyNaming:FeeSelectorData.kt$FeeSelectorData$val removeSuggestedFee: Boolean = false</ID>
|
|
||||||
<ID>BooleanPropertyNaming:SendEntryRoute.kt$SendEntryRoute.ChooseToken$val showSendViaSwapNotification: Boolean</ID>
|
|
||||||
<ID>UseEmptyCounterpart:CommonSendAmountAnalyticEvents.kt$CommonSendAmountAnalyticEvents$mapOf()</ID>
|
|
||||||
<ID>UseEmptyCounterpart:CommonSendAnalyticEvents.kt$CommonSendAnalyticEvents$mapOf()</ID>
|
|
||||||
<ID>UseEmptyCounterpart:CommonSendFeeAnalyticEvents.kt$CommonSendFeeAnalyticEvents$mapOf()</ID>
|
|
||||||
</CurrentIssues>
|
|
||||||
</SmellBaseline>
|
|
||||||
|
|
@ -12,7 +12,7 @@ import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM
|
||||||
sealed class CommonSendAnalyticEvents(
|
sealed class CommonSendAnalyticEvents(
|
||||||
category: String,
|
category: String,
|
||||||
event: String,
|
event: String,
|
||||||
params: Map<String, String> = mapOf(),
|
params: Map<String, String> = emptyMap(),
|
||||||
) : AnalyticsEvent(category = category, event = event, params = params) {
|
) : AnalyticsEvent(category = category, event = event, params = params) {
|
||||||
|
|
||||||
/** Recipient address screen opened */
|
/** Recipient address screen opened */
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,6 @@ sealed class SendEntryRoute : Route {
|
||||||
data object SendWithSwap : SendEntryRoute()
|
data object SendWithSwap : SendEntryRoute()
|
||||||
/** Route to choose token screen for send via swap */
|
/** Route to choose token screen for send via swap */
|
||||||
data class ChooseToken(
|
data class ChooseToken(
|
||||||
val showSendViaSwapNotification: Boolean,
|
val isShowSendViaSwapNotification: Boolean,
|
||||||
) : SendEntryRoute()
|
) : SendEntryRoute()
|
||||||
}
|
}
|
||||||
|
|
@ -10,7 +10,7 @@ import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents
|
||||||
sealed class CommonSendAmountAnalyticEvents(
|
sealed class CommonSendAmountAnalyticEvents(
|
||||||
category: String,
|
category: String,
|
||||||
event: String,
|
event: String,
|
||||||
params: Map<String, String> = mapOf(),
|
params: Map<String, String> = emptyMap(),
|
||||||
) : AnalyticsEvent(category = category, event = event, params = params) {
|
) : AnalyticsEvent(category = category, event = event, params = params) {
|
||||||
|
|
||||||
/** Selected currency */
|
/** Selected currency */
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents.Common
|
||||||
sealed class CommonSendFeeAnalyticEvents(
|
sealed class CommonSendFeeAnalyticEvents(
|
||||||
category: String,
|
category: String,
|
||||||
event: String,
|
event: String,
|
||||||
params: Map<String, String> = mapOf(),
|
params: Map<String, String> = emptyMap(),
|
||||||
) : AnalyticsEvent(category = category, event = event, params = params) {
|
) : AnalyticsEvent(category = category, event = event, params = params) {
|
||||||
|
|
||||||
abstract val categoryName: String
|
abstract val categoryName: String
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
package com.tangem.features.send.v2.api.subcomponents.feeSelector.entity
|
package com.tangem.features.send.v2.api.subcomponents.feeSelector.entity
|
||||||
|
|
||||||
data class FeeSelectorData(
|
data class FeeSelectorData(
|
||||||
val removeSuggestedFee: Boolean = false,
|
val isRemoveSuggestedFee: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
<?xml version="1.0" ?>
|
|
||||||
<SmellBaseline>
|
|
||||||
<ManuallySuppressedIssues/>
|
|
||||||
<CurrentIssues>
|
|
||||||
<ID>BooleanPropertyNaming:ConfirmUM.kt$ConfirmUM.Content$val showTapHelp: Boolean</ID>
|
|
||||||
<ID>BooleanPropertyNaming:FeeSelectorAlertFactory.kt$FeeSelectorAlertFactory$val showFeeTooHigh = checkAndShowFeeTooHigh(feeSelectorUM, onConfirmClick)</ID>
|
|
||||||
<ID>BooleanPropertyNaming:FeeSelectorAlertFactory.kt$FeeSelectorAlertFactory$val showFeeTooLow = checkAndShowFeeTooLow(feeSelectorUM, onConfirmClick)</ID>
|
|
||||||
<ID>BooleanPropertyNaming:FeeSelectorModalBottomSheet.kt$val lastItem = index == state.feeItems.size - 1</ID>
|
|
||||||
<ID>BooleanPropertyNaming:FeeSelectorModalBottomSheet.kt$val showDivider = index != customFeeFields.size - 1 || nonce is FeeNonce.Nonce</ID>
|
|
||||||
<ID>BooleanPropertyNaming:NFTSendAnalyticEvents.kt$NFTSendAnalyticEvents.TransactionScreenOpened$val nonceNotEmpty: Boolean</ID>
|
|
||||||
<ID>BooleanPropertyNaming:NFTSendSuccessContent.kt$var visible by remember { mutableStateOf(false) }</ID>
|
|
||||||
<ID>BooleanPropertyNaming:SendAnalyticEvents.kt$SendAnalyticEvents.TransactionScreenOpened$val nonceNotEmpty: Boolean</ID>
|
|
||||||
<ID>BooleanPropertyNaming:SendAnalyticHelper.kt$SendAnalyticHelper$val blockchainAddressForEns = (sendUM.destinationUM as? DestinationUM.Content)?.addressTextField?.isAddressEns</ID>
|
|
||||||
<ID>BooleanPropertyNaming:SendConfirmSuccessContent.kt$var visible by remember { mutableStateOf(false) }</ID>
|
|
||||||
<ID>BooleanPropertyNaming:SendEntryPointModel.kt$SendEntryPointModel$val showSendViaSwapNotification = shouldShowNotificationUseCase( NotificationId.SendViaSwapTokenSelectorNotification.key, )</ID>
|
|
||||||
<ID>BooleanPropertyNaming:SendRecipientHistoryListConverter.kt$SendRecipientHistoryListConverter$val notZero = !item.amount.isZero()</ID>
|
|
||||||
<ID>BooleanPropertyNaming:TapHelp.kt$var wrappedIsDisplay by remember { mutableStateOf(false) }</ID>
|
|
||||||
<ID>CanBeNonNullable:SendDestinationContent.kt$memoField: DestinationTextFieldUM.RecipientMemo?</ID>
|
|
||||||
<ID>CanBeNonNullable:SendDestinationModel.kt$SendDestinationModel$type: EnterAddressSource?</ID>
|
|
||||||
<ID>MaxChainedCallsOnSameLine:FeeSelectorBlockContent.kt$state.selectedFeeItem.fee.amount.value.format { crypto( symbol = state.selectedFeeItem.fee.amount.currencySymbol, decimals = state.selectedFeeItem.fee.amount.decimals, ).fee(canBeLower = state.feeExtraInfo.isFeeApproximate) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:DefaultSellRedirectDeepLinkHandler.kt$DefaultSellRedirectDeepLinkHandler${ Timber.e("Error on getting cryptoCurrency: $it") return@launch }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:FeeSelectorCustomFieldConverter.kt$FeeSelectorCustomFieldConverter${ when (it) { is Fee.Kaspa -> kaspaCustomFeeConverter.tryAutoFixValue( minimumFee = it, customValues = customValues, ) else -> customValues } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:FeeSelectorModel.kt$FeeSelectorModel${ feeSelectorAlertFactory.getFeeUpdatedAlert( newFee = it, feeSelectorUM = uiState.value, proceedAction = { modelScope.launch { feeSelectorCheckReloadTrigger.callbackCheckResult(true) } }, stopAction = { modelScope.launch { feeSelectorCheckReloadTrigger.callbackCheckResult(false) } }, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:KaspaCustomFeeConverter.kt$KaspaCustomFeeConverter${ 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>MultilineLambdaItParameter:NFTSendConfirmModel.kt$NFTSendConfirmModel${ it.copy( confirmUM = NFTSendConfirmInitialStateTransformer( isShowTapHelp = isShowTapHelp, walletName = stringReference(userWallet.name), ).transform(uiState.value.confirmUM), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:NFTSendConfirmModel.kt$NFTSendConfirmModel${ it.copy( confirmUM = NFTSendConfirmationNotificationsTransformerV2( feeSelectorUM = uiState.value.feeSelectorUM, analyticsEventHandler = analyticsEventHandler, cryptoCurrency = cryptoCurrencyStatus.currency, appCurrency = params.appCurrency, analyticsCategoryName = analyticsCategoryName, ).transform(uiState.value.confirmUM), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:NFTSendConfirmModel.kt$NFTSendConfirmModel${ val confirmUM = it.confirmUM as? ConfirmUM.Content it.copy(confirmUM = confirmUM?.copy(showTapHelp = showTapHelp) ?: it.confirmUM) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:NFTSendConfirmModel.kt$NFTSendConfirmModel${ val isFeeNotNull = it.feeSelectorUM is FeeSelectorUMRedesigned.Content it.copy( confirmUM = (it.confirmUM as? ConfirmUM.Content)?.copy( isPrimaryButtonEnabled = !hasError && isFeeNotNull, ) ?: it.confirmUM, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:RecentListUtils.kt${ add( DestinationRecipientListUM( id = "$tag$it", isLoading = false, isVisible = false, ), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:RecentListUtils.kt${ add( DestinationRecipientListUM( id = "$tag$it", isLoading = true, ), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:SendAmountModel.kt$SendAmountModel${ (it as? AmountState.Data)?.copy( isPrimaryButtonEnabled = false, ) ?: it }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:SendAmountModel.kt$SendAmountModel${ EnterAmountBoundary( amount = it, fiatRate = cryptoCurrencyStatus.value.fiatRate.orZero(), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:SendConfirmModel.kt$SendConfirmModel${ it.copy( confirmUM = SendConfirmInitialStateTransformer( isShowTapHelp = isShowTapHelp, walletName = stringReference(userWallet.name), ).transform(uiState.value.confirmUM), confirmData = confirmData, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:SendConfirmModel.kt$SendConfirmModel${ it.copy( confirmUM = SendConfirmationNotificationsTransformerV2( feeSelectorUM = uiState.value.feeSelectorUM, amountUM = uiState.value.amountUM, analyticsEventHandler = analyticsEventHandler, cryptoCurrency = cryptoCurrencyStatus.currency, appCurrency = appCurrency, analyticsCategoryName = params.analyticsCategoryName, ).transform(uiState.value.confirmUM), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:SendConfirmModel.kt$SendConfirmModel${ val confirmUM = it.confirmUM as? ConfirmUM.Content it.copy(confirmUM = confirmUM?.copy(showTapHelp = showTapHelp) ?: it.confirmUM) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:SendConfirmModel.kt$SendConfirmModel${ val feeUM = it.feeSelectorUM as? FeeSelectorUMRedesigned.Content it.copy( confirmUM = (it.confirmUM as? ConfirmUM.Content)?.copy( isPrimaryButtonEnabled = !hasError && feeUM != null, ) ?: it.confirmUM, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:SendDestinationContent.kt${ if (it) { content() } else { Box(modifier = Modifier.fillMaxWidth()) } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:SendDestinationModel.kt$SendDestinationModel${ analyticsEventHandler.send( SendDestinationAnalyticEvents.AddressEntered( categoryName = analyticsCategoryName, source = params.analyticsSendSource, method = it, isValid = addressValidationResult.isRight(), ), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:SendDestinationModel.kt$SendDestinationModel${ if (it.network.rawId == cryptoCurrencyNetwork.rawId) { getNetworkAddressesUseCase.invokeSync( userWalletId = wallet.walletId, networkRawId = it.network.id.rawId, ) } else { null } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:SendDestinationValidationResultTransformer.kt$SendDestinationValidationResultTransformer${ when (it) { is AddressValidation.Error.DataError, AddressValidation.Error.InvalidAddress, -> R.string.send_recipient_address_error AddressValidation.Error.AddressInWallet -> R.string.send_error_address_same_as_wallet } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:SendModel.kt$SendModel${ Timber.w(it.toString()) showAlertError() return@launch }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:SendModel.kt$SendModel${ it.copy( destinationUM = SendDestinationInitialStateTransformer( cryptoCurrency = cryptoCurrency, ).transform(DestinationUM.Empty()), feeSelectorUM = FeeSelectorUM.Loading, confirmUM = ConfirmUM.Empty, confirmData = null, navigationUM = NavigationUM.Empty, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:SendRecipientWalletListConverter.kt$SendRecipientWalletListConverter${ val isCoin = it.cryptoCurrency is CryptoCurrency.Coin val isNotSameAddress = it.address != senderAddress val isNotBlankAddress = it.address.isNotBlank() isNotBlankAddress && isCoin && (isNotSameAddress || isSelfSendAvailable) }</ID>
|
|
||||||
<ID>NamedArguments:EthereumCustomFeeConverter.kt$EthereumCustomFeeConverter$onValueChange(feeValue, customValues, index, value)</ID>
|
|
||||||
<ID>NamedArguments:FeeSelectorCustomValueChangedTransformer.kt$FeeSelectorCustomValueChangedTransformer$onValueChange(state, customFee.customValues, index, value)</ID>
|
|
||||||
<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>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>
|
|
||||||
<ID>ReusedModifierInstance:DefaultSendEntryPointComponent.kt$DefaultSendEntryPointComponent$Content(modifier.fillMaxSize())</ID>
|
|
||||||
<ID>UnnecessaryLet:EthereumCustomFeeConverter.kt$EthereumCustomFeeConverter$let(::add)</ID>
|
|
||||||
<ID>UnnecessaryLet:EthereumCustomFeeConverter.kt$EthereumCustomFeeConverter$let(::addAll)</ID>
|
|
||||||
<ID>UnnecessaryLet:SendAmountModel.kt$SendAmountModel$let { onCurrencyChangeClick(isEnterInFiat) }</ID>
|
|
||||||
<ID>UseEmptyCounterpart:NFTSendAnalyticEvents.kt$NFTSendAnalyticEvents$mapOf()</ID>
|
|
||||||
<ID>UseEmptyCounterpart:NotificationsAnalyticEvents.kt$NotificationsAnalyticEvents$mapOf()</ID>
|
|
||||||
<ID>UseEmptyCounterpart:SendAnalyticEvents.kt$SendAnalyticEvents$mapOf()</ID>
|
|
||||||
<ID>UseEmptyCounterpart:SendDestinationAnalyticEvents.kt$SendDestinationAnalyticEvents$mapOf()</ID>
|
|
||||||
<ID>VarCouldBeVal:SendDestinationModel.kt$SendDestinationModel$private var validationJobHolder = JobHolder()</ID>
|
|
||||||
<ID>VarCouldBeVal:SendModel.kt$SendModel$private var balanceHidingJobHolder = JobHolder()</ID>
|
|
||||||
</CurrentIssues>
|
|
||||||
</SmellBaseline>
|
|
||||||
|
|
@ -85,14 +85,14 @@ internal fun SendContent(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SendAppBar(navigationUM: NavigationUM) {
|
private fun SendAppBar(navigationUM: NavigationUM) {
|
||||||
val navigationUM = navigationUM as? NavigationUM.Content ?: return
|
val navigationUMContent = navigationUM as? NavigationUM.Content ?: return
|
||||||
AppBarWithBackButtonAndIcon(
|
AppBarWithBackButtonAndIcon(
|
||||||
text = navigationUM.title.resolveReference(),
|
text = navigationUMContent.title.resolveReference(),
|
||||||
subtitle = navigationUM.subtitle?.resolveReference(),
|
subtitle = navigationUMContent.subtitle?.resolveReference(),
|
||||||
onBackClick = navigationUM.backIconClick,
|
onBackClick = navigationUMContent.backIconClick,
|
||||||
onIconClick = navigationUM.additionalIconClick,
|
onIconClick = navigationUMContent.additionalIconClick,
|
||||||
backIconRes = navigationUM.backIconRes,
|
backIconRes = navigationUMContent.backIconRes,
|
||||||
iconRes = navigationUM.additionalIconRes,
|
iconRes = navigationUMContent.additionalIconRes,
|
||||||
backgroundColor = TangemTheme.colors.background.tertiary,
|
backgroundColor = TangemTheme.colors.background.tertiary,
|
||||||
modifier = Modifier.height(TangemTheme.dimens.size56),
|
modifier = Modifier.height(TangemTheme.dimens.size56),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -22,14 +22,14 @@ private const val TAP_HELP_ANIMATION_DELAY = 500L
|
||||||
|
|
||||||
internal fun LazyListScope.tapHelp(isDisplay: Boolean, modifier: Modifier = Modifier) {
|
internal fun LazyListScope.tapHelp(isDisplay: Boolean, modifier: Modifier = Modifier) {
|
||||||
item(key = TAP_HELP_KEY) {
|
item(key = TAP_HELP_KEY) {
|
||||||
var wrappedIsDisplay by remember { mutableStateOf(false) }
|
var isWrappedDisplay by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
LaunchedEffect(key1 = isDisplay) {
|
LaunchedEffect(key1 = isDisplay) {
|
||||||
delay(TAP_HELP_ANIMATION_DELAY)
|
delay(TAP_HELP_ANIMATION_DELAY)
|
||||||
wrappedIsDisplay = isDisplay
|
isWrappedDisplay = isDisplay
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wrappedIsDisplay) {
|
if (isWrappedDisplay) {
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ internal sealed class ConfirmUM {
|
||||||
override val isPrimaryButtonEnabled: Boolean = false,
|
override val isPrimaryButtonEnabled: Boolean = false,
|
||||||
val walletName: TextReference,
|
val walletName: TextReference,
|
||||||
val isSending: Boolean,
|
val isSending: Boolean,
|
||||||
val showTapHelp: Boolean,
|
val isShowTapHelp: Boolean,
|
||||||
val sendingFooter: TextReference,
|
val sendingFooter: TextReference,
|
||||||
val notifications: ImmutableList<NotificationUM>,
|
val notifications: ImmutableList<NotificationUM>,
|
||||||
) : ConfirmUM()
|
) : ConfirmUM()
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,8 @@ internal class DefaultSellRedirectDeepLinkHandler @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val cryptoCurrency = getCryptoCurrencyUseCase(userWallet, currencyId).getOrElse {
|
val cryptoCurrency = getCryptoCurrencyUseCase(userWallet, currencyId).getOrElse { error ->
|
||||||
Timber.e("Error on getting cryptoCurrency: $it")
|
Timber.e("Error on getting cryptoCurrency: $error")
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
// Convert using universal parser to account for regional separators
|
// Convert using universal parser to account for regional separators
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,7 @@ internal class DefaultSendEntryPointComponent @AssistedInject constructor(
|
||||||
|
|
||||||
Children(
|
Children(
|
||||||
stack = childStackValue,
|
stack = childStackValue,
|
||||||
|
modifier = modifier,
|
||||||
animation = stackAnimation { child ->
|
animation = stackAnimation { child ->
|
||||||
when (child.configuration) {
|
when (child.configuration) {
|
||||||
SendEntryRoute.Send,
|
SendEntryRoute.Send,
|
||||||
|
|
@ -115,7 +116,7 @@ internal class DefaultSendEntryPointComponent @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
) { child ->
|
) { child ->
|
||||||
child.instance.Content(modifier.fillMaxSize())
|
child.instance.Content(Modifier.fillMaxSize())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,7 +126,7 @@ internal class DefaultSendEntryPointComponent @AssistedInject constructor(
|
||||||
): ComposableContentComponent = when (configuration) {
|
): ComposableContentComponent = when (configuration) {
|
||||||
is SendEntryRoute.ChooseToken -> getManagedTokensComponent(
|
is SendEntryRoute.ChooseToken -> getManagedTokensComponent(
|
||||||
componentContext = factoryContext,
|
componentContext = factoryContext,
|
||||||
showSendViaSwapNotification = configuration.showSendViaSwapNotification,
|
showSendViaSwapNotification = configuration.isShowSendViaSwapNotification,
|
||||||
)
|
)
|
||||||
SendEntryRoute.Send -> sendComponent
|
SendEntryRoute.Send -> sendComponent
|
||||||
SendEntryRoute.SendWithSwap -> sendWithSwapComponent
|
SendEntryRoute.SendWithSwap -> sendWithSwapComponent
|
||||||
|
|
|
||||||
|
|
@ -42,12 +42,12 @@ internal class SendEntryPointModel @Inject constructor(
|
||||||
lastSavedAmount = lastAmount
|
lastSavedAmount = lastAmount
|
||||||
isEnterInFiat = isEnterInFiatSelected
|
isEnterInFiat = isEnterInFiatSelected
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
val showSendViaSwapNotification = shouldShowNotificationUseCase(
|
val isShowSendViaSwapNotification = shouldShowNotificationUseCase(
|
||||||
NotificationId.SendViaSwapTokenSelectorNotification.key,
|
NotificationId.SendViaSwapTokenSelectorNotification.key,
|
||||||
)
|
)
|
||||||
router.push(
|
router.push(
|
||||||
SendEntryRoute.ChooseToken(
|
SendEntryRoute.ChooseToken(
|
||||||
showSendViaSwapNotification = showSendViaSwapNotification,
|
isShowSendViaSwapNotification = isShowSendViaSwapNotification,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,10 @@ internal class FeeSelectorAlertFactory @Inject constructor(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun checkAndShowAlerts(feeSelectorUM: FeeSelectorUM.Content, onConfirmClick: () -> Unit) {
|
fun checkAndShowAlerts(feeSelectorUM: FeeSelectorUM.Content, onConfirmClick: () -> Unit) {
|
||||||
val showFeeTooLow = checkAndShowFeeTooLow(feeSelectorUM, onConfirmClick)
|
val isShowFeeTooLow = checkAndShowFeeTooLow(feeSelectorUM, onConfirmClick)
|
||||||
val showFeeTooHigh = checkAndShowFeeTooHigh(feeSelectorUM, onConfirmClick)
|
val isShowFeeTooHigh = checkAndShowFeeTooHigh(feeSelectorUM, onConfirmClick)
|
||||||
|
|
||||||
if (!showFeeTooLow && !showFeeTooHigh) {
|
if (!isShowFeeTooLow && !isShowFeeTooHigh) {
|
||||||
onConfirmClick()
|
onConfirmClick()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -78,20 +78,20 @@ internal class FeeSelectorAlertFactory @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFeeUpdatedAlert(
|
fun getFeeUpdatedAlert(
|
||||||
newFee: TransactionFee,
|
newTransactionFee: TransactionFee,
|
||||||
feeSelectorUM: FeeSelectorUM,
|
feeSelectorUM: FeeSelectorUM,
|
||||||
proceedAction: () -> Unit,
|
proceedAction: () -> Unit,
|
||||||
stopAction: () -> Unit,
|
stopAction: () -> Unit,
|
||||||
) {
|
) {
|
||||||
if (feeSelectorUM !is FeeSelectorUM.Content) return
|
if (feeSelectorUM !is FeeSelectorUM.Content) return
|
||||||
val newFee = when (newFee) {
|
val newFee = when (newTransactionFee) {
|
||||||
is TransactionFee.Single -> newFee.normal
|
is TransactionFee.Single -> newTransactionFee.normal
|
||||||
is TransactionFee.Choosable -> {
|
is TransactionFee.Choosable -> {
|
||||||
when (feeSelectorUM.selectedFeeItem) {
|
when (feeSelectorUM.selectedFeeItem) {
|
||||||
is FeeItem.Suggested -> feeSelectorUM.selectedFeeItem.fee
|
is FeeItem.Suggested -> feeSelectorUM.selectedFeeItem.fee
|
||||||
is FeeItem.Slow -> newFee.minimum
|
is FeeItem.Slow -> newTransactionFee.minimum
|
||||||
is FeeItem.Market -> newFee.normal
|
is FeeItem.Market -> newTransactionFee.normal
|
||||||
is FeeItem.Fast -> newFee.priority
|
is FeeItem.Fast -> newTransactionFee.priority
|
||||||
is FeeItem.Custom -> return
|
is FeeItem.Custom -> return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -197,7 +197,7 @@ internal class FeeSelectorModel @Inject constructor(
|
||||||
private fun subscribeOnFeeReloadTriggerUpdates() {
|
private fun subscribeOnFeeReloadTriggerUpdates() {
|
||||||
feeSelectorReloadListener.reloadTriggerFlow
|
feeSelectorReloadListener.reloadTriggerFlow
|
||||||
.onEach { data ->
|
.onEach { data ->
|
||||||
if (data.removeSuggestedFee) {
|
if (data.isRemoveSuggestedFee) {
|
||||||
uiState.update(FeeSelectorRemoveSuggestedTransformer)
|
uiState.update(FeeSelectorRemoveSuggestedTransformer)
|
||||||
}
|
}
|
||||||
loadFee()
|
loadFee()
|
||||||
|
|
@ -220,9 +220,9 @@ internal class FeeSelectorModel @Inject constructor(
|
||||||
private fun checkLoadFee() {
|
private fun checkLoadFee() {
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
params.onLoadFee().fold(
|
params.onLoadFee().fold(
|
||||||
ifRight = {
|
ifRight = { newFee ->
|
||||||
feeSelectorAlertFactory.getFeeUpdatedAlert(
|
feeSelectorAlertFactory.getFeeUpdatedAlert(
|
||||||
newFee = it,
|
newTransactionFee = newFee,
|
||||||
feeSelectorUM = uiState.value,
|
feeSelectorUM = uiState.value,
|
||||||
proceedAction = {
|
proceedAction = {
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
|
|
|
||||||
|
|
@ -107,10 +107,10 @@ internal class FeeSelectorCustomFieldConverter(
|
||||||
when (val fees = feeSelectorState.fees) {
|
when (val fees = feeSelectorState.fees) {
|
||||||
is TransactionFee.Choosable -> fees.minimum
|
is TransactionFee.Choosable -> fees.minimum
|
||||||
is TransactionFee.Single -> fees.normal
|
is TransactionFee.Single -> fees.normal
|
||||||
}.let {
|
}.let { fee ->
|
||||||
when (it) {
|
when (fee) {
|
||||||
is Fee.Kaspa -> kaspaCustomFeeConverter.tryAutoFixValue(
|
is Fee.Kaspa -> kaspaCustomFeeConverter.tryAutoFixValue(
|
||||||
minimumFee = it,
|
minimumFee = fee,
|
||||||
customValues = customValues,
|
customValues = customValues,
|
||||||
)
|
)
|
||||||
else -> customValues
|
else -> customValues
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,12 @@ internal class FeeSelectorCustomValueChangedTransformer(
|
||||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||||
normalFee = state.selectedFeeItem.fee,
|
normalFee = state.selectedFeeItem.fee,
|
||||||
)
|
)
|
||||||
val updatedCustomValues = customFeeConverter.onValueChange(state, customFee.customValues, index, value)
|
val updatedCustomValues = customFeeConverter.onValueChange(
|
||||||
|
feeSelectorState = state,
|
||||||
|
customValues = customFee.customValues,
|
||||||
|
index = index,
|
||||||
|
value = value,
|
||||||
|
)
|
||||||
val newCustomFee = customFee.copy(
|
val newCustomFee = customFee.copy(
|
||||||
fee = customFeeConverter.convertBack(updatedCustomValues),
|
fee = customFeeConverter.convertBack(updatedCustomValues),
|
||||||
customValues = updatedCustomValues,
|
customValues = updatedCustomValues,
|
||||||
|
|
|
||||||
|
|
@ -172,12 +172,13 @@ private fun FeeContent(state: FeeSelectorUM.Content, modifier: Modifier = Modifi
|
||||||
approximate = state.feeExtraInfo.isFeeApproximate,
|
approximate = state.feeExtraInfo.isFeeApproximate,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
state.selectedFeeItem.fee.amount.value.format {
|
state.selectedFeeItem.fee.amount.value
|
||||||
crypto(
|
.format {
|
||||||
symbol = state.selectedFeeItem.fee.amount.currencySymbol,
|
crypto(
|
||||||
decimals = state.selectedFeeItem.fee.amount.decimals,
|
symbol = state.selectedFeeItem.fee.amount.currencySymbol,
|
||||||
).fee(canBeLower = state.feeExtraInfo.isFeeApproximate)
|
decimals = state.selectedFeeItem.fee.amount.decimals,
|
||||||
}
|
).fee(canBeLower = state.feeExtraInfo.isFeeApproximate)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
style = TangemTheme.typography.body1,
|
style = TangemTheme.typography.body1,
|
||||||
color = TangemTheme.colors.text.tertiary,
|
color = TangemTheme.colors.text.tertiary,
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ private fun FeeSelectorItems(
|
||||||
val feeFiatRateUM = state.feeFiatRateUM
|
val feeFiatRateUM = state.feeFiatRateUM
|
||||||
state.feeItems.fastForEachIndexed { index, item ->
|
state.feeItems.fastForEachIndexed { index, item ->
|
||||||
val isSelected = item.isSameClass(state.selectedFeeItem)
|
val isSelected = item.isSameClass(state.selectedFeeItem)
|
||||||
val lastItem = index == state.feeItems.size - 1
|
val isLastItem = index == state.feeItems.size - 1
|
||||||
val iconTint by animateColorAsState(
|
val iconTint by animateColorAsState(
|
||||||
targetValue = if (isSelected) TangemTheme.colors.icon.accent else TangemTheme.colors.text.tertiary,
|
targetValue = if (isSelected) TangemTheme.colors.icon.accent else TangemTheme.colors.text.tertiary,
|
||||||
label = "Fee selector icon tint change",
|
label = "Fee selector icon tint change",
|
||||||
|
|
@ -180,7 +180,7 @@ private fun FeeSelectorItems(
|
||||||
null
|
null
|
||||||
},
|
},
|
||||||
ellipsizeOffset = item.fee.amount.currencySymbol.length,
|
ellipsizeOffset = item.fee.amount.currencySymbol.length,
|
||||||
showDivider = !isSelected && !lastItem,
|
showDivider = !isSelected && !isLastItem,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -243,7 +243,7 @@ private fun ExpandedCustomFeeItems(
|
||||||
) {
|
) {
|
||||||
Column(modifier = modifier) {
|
Column(modifier = modifier) {
|
||||||
customFeeFields.fastForEachIndexed { index, field ->
|
customFeeFields.fastForEachIndexed { index, field ->
|
||||||
val showDivider = index != customFeeFields.size - 1 || nonce is FeeNonce.Nonce
|
val isShowDivider = index != customFeeFields.size - 1 || nonce is FeeNonce.Nonce
|
||||||
if (field.label != null) {
|
if (field.label != null) {
|
||||||
InputRowEnterInfoAmountV2(
|
InputRowEnterInfoAmountV2(
|
||||||
text = field.value,
|
text = field.value,
|
||||||
|
|
@ -256,7 +256,7 @@ private fun ExpandedCustomFeeItems(
|
||||||
keyboardOptions = field.keyboardOptions,
|
keyboardOptions = field.keyboardOptions,
|
||||||
keyboardActions = field.keyboardActions,
|
keyboardActions = field.keyboardActions,
|
||||||
onValueChange = { onValueChange(index, it) },
|
onValueChange = { onValueChange(index, it) },
|
||||||
showDivider = showDivider,
|
showDivider = isShowDivider,
|
||||||
isReadOnly = field.isReadonly,
|
isReadOnly = field.isReadonly,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -270,7 +270,7 @@ private fun ExpandedCustomFeeItems(
|
||||||
onValueChange = { onValueChange(index, it) },
|
onValueChange = { onValueChange(index, it) },
|
||||||
keyboardOptions = field.keyboardOptions,
|
keyboardOptions = field.keyboardOptions,
|
||||||
keyboardActions = field.keyboardActions,
|
keyboardActions = field.keyboardActions,
|
||||||
showDivider = showDivider,
|
showDivider = isShowDivider,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents
|
||||||
*/
|
*/
|
||||||
internal sealed class SendAnalyticEvents(
|
internal sealed class SendAnalyticEvents(
|
||||||
event: String,
|
event: String,
|
||||||
params: Map<String, String> = mapOf(),
|
params: Map<String, String> = emptyMap(),
|
||||||
) : AnalyticsEvent(category = CommonSendAnalyticEvents.SEND_CATEGORY, event = event, params = params) {
|
) : AnalyticsEvent(category = CommonSendAnalyticEvents.SEND_CATEGORY, event = event, params = params) {
|
||||||
|
|
||||||
/** Transaction send screen opened */
|
/** Transaction send screen opened */
|
||||||
|
|
@ -23,7 +23,7 @@ internal sealed class SendAnalyticEvents(
|
||||||
val token: String,
|
val token: String,
|
||||||
val feeType: AnalyticsParam.FeeType,
|
val feeType: AnalyticsParam.FeeType,
|
||||||
val blockchain: String,
|
val blockchain: String,
|
||||||
val nonceNotEmpty: Boolean,
|
val isNonceNotEmpty: Boolean,
|
||||||
private val ensStatus: AnalyticsParam.EmptyFull,
|
private val ensStatus: AnalyticsParam.EmptyFull,
|
||||||
) : SendAnalyticEvents(
|
) : SendAnalyticEvents(
|
||||||
event = "Transaction Sent Screen Opened",
|
event = "Transaction Sent Screen Opened",
|
||||||
|
|
@ -31,7 +31,7 @@ internal sealed class SendAnalyticEvents(
|
||||||
TOKEN_PARAM to token,
|
TOKEN_PARAM to token,
|
||||||
FEE_TYPE to feeType.value,
|
FEE_TYPE to feeType.value,
|
||||||
BLOCKCHAIN to blockchain,
|
BLOCKCHAIN to blockchain,
|
||||||
NONCE to nonceNotEmpty.toString().capitalize(),
|
NONCE to isNonceNotEmpty.toString().capitalize(),
|
||||||
ENS_ADDRESS to when (ensStatus) {
|
ENS_ADDRESS to when (ensStatus) {
|
||||||
AnalyticsParam.EmptyFull.Empty -> false.toString().capitalize()
|
AnalyticsParam.EmptyFull.Empty -> false.toString().capitalize()
|
||||||
AnalyticsParam.EmptyFull.Full -> true.toString().capitalize()
|
AnalyticsParam.EmptyFull.Full -> true.toString().capitalize()
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ internal class SendAnalyticHelper @Inject constructor(
|
||||||
token = cryptoCurrency.symbol,
|
token = cryptoCurrency.symbol,
|
||||||
feeType = feeType,
|
feeType = feeType,
|
||||||
blockchain = cryptoCurrency.network.name,
|
blockchain = cryptoCurrency.network.name,
|
||||||
nonceNotEmpty = feeSelectorUM.feeNonce is FeeNonce.Nonce,
|
isNonceNotEmpty = feeSelectorUM.feeNonce is FeeNonce.Nonce,
|
||||||
ensStatus = getEnsStatus(sendUM),
|
ensStatus = getEnsStatus(sendUM),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -54,9 +54,9 @@ internal class SendAnalyticHelper @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getEnsStatus(sendUM: SendUM): AnalyticsParam.EmptyFull {
|
private fun getEnsStatus(sendUM: SendUM): AnalyticsParam.EmptyFull {
|
||||||
val blockchainAddressForEns =
|
val isBlockchainAddressForEns =
|
||||||
(sendUM.destinationUM as? DestinationUM.Content)?.addressTextField?.isAddressEns
|
(sendUM.destinationUM as? DestinationUM.Content)?.addressTextField?.isAddressEns
|
||||||
return if (blockchainAddressForEns == true) {
|
return if (isBlockchainAddressForEns == true) {
|
||||||
AnalyticsParam.EmptyFull.Full
|
AnalyticsParam.EmptyFull.Full
|
||||||
} else {
|
} else {
|
||||||
AnalyticsParam.EmptyFull.Empty
|
AnalyticsParam.EmptyFull.Empty
|
||||||
|
|
|
||||||
|
|
@ -306,8 +306,8 @@ internal class SendConfirmModel @Inject constructor(
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
val isShowTapHelp = isSendTapHelpEnabledUseCase.invokeSync().getOrElse { false }
|
val isShowTapHelp = isSendTapHelpEnabledUseCase.invokeSync().getOrElse { false }
|
||||||
if (confirmUM is ConfirmUM.Empty) {
|
if (confirmUM is ConfirmUM.Empty) {
|
||||||
_uiState.update {
|
_uiState.update { state ->
|
||||||
it.copy(
|
state.copy(
|
||||||
confirmUM = SendConfirmInitialStateTransformer(
|
confirmUM = SendConfirmInitialStateTransformer(
|
||||||
isShowTapHelp = isShowTapHelp,
|
isShowTapHelp = isShowTapHelp,
|
||||||
walletName = stringReference(userWallet.name),
|
walletName = stringReference(userWallet.name),
|
||||||
|
|
@ -323,9 +323,9 @@ internal class SendConfirmModel @Inject constructor(
|
||||||
private fun subscribeOnTapHelpUpdates() {
|
private fun subscribeOnTapHelpUpdates() {
|
||||||
isSendTapHelpEnabledUseCase().getOrNull()
|
isSendTapHelpEnabledUseCase().getOrNull()
|
||||||
?.onEach { showTapHelp ->
|
?.onEach { showTapHelp ->
|
||||||
_uiState.update {
|
_uiState.update { state ->
|
||||||
val confirmUM = it.confirmUM as? ConfirmUM.Content
|
val confirmUM = state.confirmUM as? ConfirmUM.Content
|
||||||
it.copy(confirmUM = confirmUM?.copy(showTapHelp = showTapHelp) ?: it.confirmUM)
|
state.copy(confirmUM = confirmUM?.copy(isShowTapHelp = showTapHelp) ?: state.confirmUM)
|
||||||
}
|
}
|
||||||
}?.launchIn(modelScope)
|
}?.launchIn(modelScope)
|
||||||
}
|
}
|
||||||
|
|
@ -333,12 +333,12 @@ internal class SendConfirmModel @Inject constructor(
|
||||||
private fun subscribeOnNotificationsUpdateTrigger() {
|
private fun subscribeOnNotificationsUpdateTrigger() {
|
||||||
notificationsUpdateListener.hasErrorFlow
|
notificationsUpdateListener.hasErrorFlow
|
||||||
.onEach { hasError ->
|
.onEach { hasError ->
|
||||||
_uiState.update {
|
_uiState.update { state ->
|
||||||
val feeUM = it.feeSelectorUM as? FeeSelectorUMRedesigned.Content
|
val feeUM = state.feeSelectorUM as? FeeSelectorUMRedesigned.Content
|
||||||
it.copy(
|
state.copy(
|
||||||
confirmUM = (it.confirmUM as? ConfirmUM.Content)?.copy(
|
confirmUM = (state.confirmUM as? ConfirmUM.Content)?.copy(
|
||||||
isPrimaryButtonEnabled = !hasError && feeUM != null,
|
isPrimaryButtonEnabled = !hasError && feeUM != null,
|
||||||
) ?: it.confirmUM,
|
) ?: state.confirmUM,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -495,8 +495,8 @@ internal class SendConfirmModel @Inject constructor(
|
||||||
feeError = confirmData.feeError,
|
feeError = confirmData.feeError,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
_uiState.update {
|
_uiState.update { state ->
|
||||||
it.copy(
|
state.copy(
|
||||||
confirmUM = SendConfirmationNotificationsTransformerV2(
|
confirmUM = SendConfirmationNotificationsTransformerV2(
|
||||||
feeSelectorUM = uiState.value.feeSelectorUM,
|
feeSelectorUM = uiState.value.feeSelectorUM,
|
||||||
amountUM = uiState.value.amountUM,
|
amountUM = uiState.value.amountUM,
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ internal class SendConfirmInitialStateTransformer(
|
||||||
return ConfirmUM.Content(
|
return ConfirmUM.Content(
|
||||||
walletName = walletName,
|
walletName = walletName,
|
||||||
isSending = false,
|
isSending = false,
|
||||||
showTapHelp = isShowTapHelp,
|
isShowTapHelp = isShowTapHelp,
|
||||||
sendingFooter = TextReference.EMPTY,
|
sendingFooter = TextReference.EMPTY,
|
||||||
notifications = persistentListOf(),
|
notifications = persistentListOf(),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ internal fun SendConfirmContent(
|
||||||
feeSelectorBlockComponent = feeSelectorBlockComponent,
|
feeSelectorBlockComponent = feeSelectorBlockComponent,
|
||||||
)
|
)
|
||||||
if (confirmUM != null) {
|
if (confirmUM != null) {
|
||||||
tapHelp(isDisplay = confirmUM.showTapHelp)
|
tapHelp(isDisplay = confirmUM.isShowTapHelp)
|
||||||
with(notificationsComponent) {
|
with(notificationsComponent) {
|
||||||
content(
|
content(
|
||||||
state = notificationsUM,
|
state = notificationsUM,
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,7 @@ internal class SendModel @Inject constructor(
|
||||||
var appCurrency: AppCurrency = AppCurrency.Default
|
var appCurrency: AppCurrency = AppCurrency.Default
|
||||||
var predefinedValues: PredefinedValues = PredefinedValues.Empty
|
var predefinedValues: PredefinedValues = PredefinedValues.Empty
|
||||||
|
|
||||||
private var balanceHidingJobHolder = JobHolder()
|
private val balanceHidingJobHolder = JobHolder()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
subscribeOnBalanceHidden()
|
subscribeOnBalanceHidden()
|
||||||
|
|
@ -226,8 +226,8 @@ internal class SendModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun resetSendNavigation() {
|
override fun resetSendNavigation() {
|
||||||
uiState.update {
|
uiState.update { state ->
|
||||||
it.copy(
|
state.copy(
|
||||||
destinationUM = SendDestinationInitialStateTransformer(
|
destinationUM = SendDestinationInitialStateTransformer(
|
||||||
cryptoCurrency = cryptoCurrency,
|
cryptoCurrency = cryptoCurrency,
|
||||||
).transform(DestinationUM.Empty()),
|
).transform(DestinationUM.Empty()),
|
||||||
|
|
@ -358,8 +358,8 @@ internal class SendModel @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ifLeft = {
|
ifLeft = { error ->
|
||||||
Timber.w(it.toString())
|
Timber.w(error.toString())
|
||||||
showAlertError()
|
showAlertError()
|
||||||
return@launch
|
return@launch
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -29,17 +29,17 @@ import kotlinx.coroutines.delay
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun SendConfirmSuccessContent(sendUM: SendUM, destinationBlockComponent: SendDestinationBlockComponent) {
|
internal fun SendConfirmSuccessContent(sendUM: SendUM, destinationBlockComponent: SendDestinationBlockComponent) {
|
||||||
var visible by remember { mutableStateOf(false) }
|
var isVisible by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
delay(ANIMATION_DELAY)
|
delay(ANIMATION_DELAY)
|
||||||
visible = true
|
isVisible = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val height = ANIMATION_OFFSET.toPx().toInt()
|
val height = ANIMATION_OFFSET.toPx().toInt()
|
||||||
|
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = visible,
|
visible = isVisible,
|
||||||
enter = slideInVertically(
|
enter = slideInVertically(
|
||||||
initialOffsetY = { height },
|
initialOffsetY = { height },
|
||||||
).plus(fadeIn()),
|
).plus(fadeIn()),
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents
|
||||||
*/
|
*/
|
||||||
internal sealed class NFTSendAnalyticEvents(
|
internal sealed class NFTSendAnalyticEvents(
|
||||||
event: String,
|
event: String,
|
||||||
params: Map<String, String> = mapOf(),
|
params: Map<String, String> = emptyMap(),
|
||||||
) : AnalyticsEvent(category = CommonSendAnalyticEvents.NFT_SEND_CATEGORY, event = event, params = params) {
|
) : AnalyticsEvent(category = CommonSendAnalyticEvents.NFT_SEND_CATEGORY, event = event, params = params) {
|
||||||
|
|
||||||
/** Transaction send screen opened */
|
/** Transaction send screen opened */
|
||||||
|
|
@ -22,14 +22,14 @@ internal sealed class NFTSendAnalyticEvents(
|
||||||
val token: String,
|
val token: String,
|
||||||
val feeType: AnalyticsParam.FeeType,
|
val feeType: AnalyticsParam.FeeType,
|
||||||
val blockchain: String,
|
val blockchain: String,
|
||||||
val nonceNotEmpty: Boolean,
|
val isNonceNotEmpty: Boolean,
|
||||||
) : NFTSendAnalyticEvents(
|
) : NFTSendAnalyticEvents(
|
||||||
event = "NFT Sent Screen Opened",
|
event = "NFT Sent Screen Opened",
|
||||||
params = mapOf(
|
params = mapOf(
|
||||||
TOKEN_PARAM to token,
|
TOKEN_PARAM to token,
|
||||||
FEE_TYPE to feeType.value,
|
FEE_TYPE to feeType.value,
|
||||||
BLOCKCHAIN to blockchain,
|
BLOCKCHAIN to blockchain,
|
||||||
NONCE to nonceNotEmpty.toString().capitalize(),
|
NONCE to isNonceNotEmpty.toString().capitalize(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -27,7 +27,7 @@ internal class NFTSendAnalyticHelper @Inject constructor(
|
||||||
token = cryptoCurrency.symbol,
|
token = cryptoCurrency.symbol,
|
||||||
feeType = feeType,
|
feeType = feeType,
|
||||||
blockchain = cryptoCurrency.network.name,
|
blockchain = cryptoCurrency.network.name,
|
||||||
nonceNotEmpty = feeSelectorUM.feeNonce is FeeNonce.Nonce,
|
isNonceNotEmpty = feeSelectorUM.feeNonce is FeeNonce.Nonce,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
analyticsEventHandler.send(
|
analyticsEventHandler.send(
|
||||||
|
|
|
||||||
|
|
@ -195,7 +195,7 @@ internal class NFTSendConfirmModel @Inject constructor(
|
||||||
derivationPath = cryptoCurrency.network.derivationPath.value,
|
derivationPath = cryptoCurrency.network.derivationPath.value,
|
||||||
destinationAddress = confirmData.enteredDestination.orEmpty(),
|
destinationAddress = confirmData.enteredDestination.orEmpty(),
|
||||||
tokenSymbol = null,
|
tokenSymbol = null,
|
||||||
amount = params.nftAsset.amount.toString(),
|
amount = params.nftAsset.amount?.toString().orEmpty(),
|
||||||
fee = confirmData.fee?.amount?.value?.stripZeroPlainString(),
|
fee = confirmData.fee?.amount?.value?.stripZeroPlainString(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -213,8 +213,8 @@ internal class NFTSendConfirmModel @Inject constructor(
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
val isShowTapHelp = isSendTapHelpEnabledUseCase.invokeSync().getOrElse { false }
|
val isShowTapHelp = isSendTapHelpEnabledUseCase.invokeSync().getOrElse { false }
|
||||||
if (confirmUM is ConfirmUM.Empty || isEmptyFee) {
|
if (confirmUM is ConfirmUM.Empty || isEmptyFee) {
|
||||||
_uiState.update {
|
_uiState.update { state ->
|
||||||
it.copy(
|
state.copy(
|
||||||
confirmUM = NFTSendConfirmInitialStateTransformer(
|
confirmUM = NFTSendConfirmInitialStateTransformer(
|
||||||
isShowTapHelp = isShowTapHelp,
|
isShowTapHelp = isShowTapHelp,
|
||||||
walletName = stringReference(userWallet.name),
|
walletName = stringReference(userWallet.name),
|
||||||
|
|
@ -229,9 +229,9 @@ internal class NFTSendConfirmModel @Inject constructor(
|
||||||
private fun subscribeOnTapHelpUpdates() {
|
private fun subscribeOnTapHelpUpdates() {
|
||||||
isSendTapHelpEnabledUseCase().getOrNull()
|
isSendTapHelpEnabledUseCase().getOrNull()
|
||||||
?.onEach { showTapHelp ->
|
?.onEach { showTapHelp ->
|
||||||
_uiState.update {
|
_uiState.update { state ->
|
||||||
val confirmUM = it.confirmUM as? ConfirmUM.Content
|
val confirmUM = state.confirmUM as? ConfirmUM.Content
|
||||||
it.copy(confirmUM = confirmUM?.copy(showTapHelp = showTapHelp) ?: it.confirmUM)
|
state.copy(confirmUM = confirmUM?.copy(isShowTapHelp = showTapHelp) ?: state.confirmUM)
|
||||||
}
|
}
|
||||||
}?.launchIn(modelScope)
|
}?.launchIn(modelScope)
|
||||||
}
|
}
|
||||||
|
|
@ -239,13 +239,13 @@ internal class NFTSendConfirmModel @Inject constructor(
|
||||||
private fun subscribeOnNotificationsUpdateTrigger() {
|
private fun subscribeOnNotificationsUpdateTrigger() {
|
||||||
notificationsUpdateListener.hasErrorFlow
|
notificationsUpdateListener.hasErrorFlow
|
||||||
.onEach { hasError ->
|
.onEach { hasError ->
|
||||||
_uiState.update {
|
_uiState.update { state ->
|
||||||
val isFeeNotNull = it.feeSelectorUM is FeeSelectorUMRedesigned.Content
|
val isFeeNotNull = state.feeSelectorUM is FeeSelectorUMRedesigned.Content
|
||||||
|
|
||||||
it.copy(
|
state.copy(
|
||||||
confirmUM = (it.confirmUM as? ConfirmUM.Content)?.copy(
|
confirmUM = (state.confirmUM as? ConfirmUM.Content)?.copy(
|
||||||
isPrimaryButtonEnabled = !hasError && isFeeNotNull,
|
isPrimaryButtonEnabled = !hasError && isFeeNotNull,
|
||||||
) ?: it.confirmUM,
|
) ?: state.confirmUM,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -353,8 +353,8 @@ internal class NFTSendConfirmModel @Inject constructor(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_uiState.update {
|
_uiState.update { state ->
|
||||||
it.copy(
|
state.copy(
|
||||||
confirmUM = NFTSendConfirmationNotificationsTransformerV2(
|
confirmUM = NFTSendConfirmationNotificationsTransformerV2(
|
||||||
feeSelectorUM = uiState.value.feeSelectorUM,
|
feeSelectorUM = uiState.value.feeSelectorUM,
|
||||||
analyticsEventHandler = analyticsEventHandler,
|
analyticsEventHandler = analyticsEventHandler,
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ internal class NFTSendConfirmInitialStateTransformer(
|
||||||
override fun transform(prevState: ConfirmUM): ConfirmUM {
|
override fun transform(prevState: ConfirmUM): ConfirmUM {
|
||||||
return ConfirmUM.Content(
|
return ConfirmUM.Content(
|
||||||
isSending = false,
|
isSending = false,
|
||||||
showTapHelp = isShowTapHelp,
|
isShowTapHelp = isShowTapHelp,
|
||||||
sendingFooter = TextReference.EMPTY,
|
sendingFooter = TextReference.EMPTY,
|
||||||
walletName = walletName,
|
walletName = walletName,
|
||||||
notifications = persistentListOf(),
|
notifications = persistentListOf(),
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ internal fun NFTSendConfirmContent(
|
||||||
feeSelectorBlockComponent = feeSelectorBlockComponent,
|
feeSelectorBlockComponent = feeSelectorBlockComponent,
|
||||||
)
|
)
|
||||||
if (confirmUM != null) {
|
if (confirmUM != null) {
|
||||||
tapHelp(isDisplay = confirmUM.showTapHelp)
|
tapHelp(isDisplay = confirmUM.isShowTapHelp)
|
||||||
with(notificationsComponent) {
|
with(notificationsComponent) {
|
||||||
content(
|
content(
|
||||||
state = notificationsUM,
|
state = notificationsUM,
|
||||||
|
|
|
||||||
|
|
@ -34,17 +34,17 @@ internal fun NFTSendSuccessContent(
|
||||||
nftDetailsBlockComponent: NFTDetailsBlockComponent,
|
nftDetailsBlockComponent: NFTDetailsBlockComponent,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
var visible by remember { mutableStateOf(false) }
|
var isVisible by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
delay(ANIMATION_DELAY)
|
delay(ANIMATION_DELAY)
|
||||||
visible = true
|
isVisible = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val height = ANIMATION_OFFSET.toPx().toInt()
|
val height = ANIMATION_OFFSET.toPx().toInt()
|
||||||
|
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = visible,
|
visible = isVisible,
|
||||||
enter = slideInVertically(
|
enter = slideInVertically(
|
||||||
initialOffsetY = { height },
|
initialOffsetY = { height },
|
||||||
).plus(fadeIn()),
|
).plus(fadeIn()),
|
||||||
|
|
|
||||||
|
|
@ -155,9 +155,9 @@ internal class SendAmountModel @Inject constructor(
|
||||||
minAmountBoundary = getMinimumTransactionAmountSyncUseCase(
|
minAmountBoundary = getMinimumTransactionAmountSyncUseCase(
|
||||||
userWalletId = params.userWalletId,
|
userWalletId = params.userWalletId,
|
||||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||||
).getOrNull()?.let {
|
).getOrNull()?.let { amount ->
|
||||||
EnterAmountBoundary(
|
EnterAmountBoundary(
|
||||||
amount = it,
|
amount = amount,
|
||||||
fiatRate = cryptoCurrencyStatus.value.fiatRate.orZero(),
|
fiatRate = cryptoCurrencyStatus.value.fiatRate.orZero(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -183,6 +183,7 @@ internal class SendAmountModel @Inject constructor(
|
||||||
account: Account.CryptoPortfolio?,
|
account: Account.CryptoPortfolio?,
|
||||||
isAccountsMode: Boolean,
|
isAccountsMode: Boolean,
|
||||||
) {
|
) {
|
||||||
|
val userWallet = userWallet
|
||||||
if (uiState.value is AmountState.Empty && userWallet != null) {
|
if (uiState.value is AmountState.Empty && userWallet != null) {
|
||||||
val isOnlyOneWallet = getWalletsUseCase.invokeSync().size == 1
|
val isOnlyOneWallet = getWalletsUseCase.invokeSync().size == 1
|
||||||
val walletTitle = if (isOnlyOneWallet) {
|
val walletTitle = if (isOnlyOneWallet) {
|
||||||
|
|
@ -190,7 +191,7 @@ internal class SendAmountModel @Inject constructor(
|
||||||
} else {
|
} else {
|
||||||
resourceReference(
|
resourceReference(
|
||||||
R.string.send_from_wallet_name,
|
R.string.send_from_wallet_name,
|
||||||
WrappedList(listOf(userWallet?.name.orEmpty())), // TODO [REDACTED_TASK_KEY]
|
WrappedList(listOf(userWallet.name)), // TODO [REDACTED_TASK_KEY]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
|
|
@ -297,10 +298,10 @@ internal class SendAmountModel @Inject constructor(
|
||||||
private fun confirmConvertToToken() {
|
private fun confirmConvertToToken() {
|
||||||
val amountParams = params as? SendAmountComponentParams.AmountParams ?: return
|
val amountParams = params as? SendAmountComponentParams.AmountParams ?: return
|
||||||
val amountFieldData = uiState.value as? AmountState.Data
|
val amountFieldData = uiState.value as? AmountState.Data
|
||||||
_uiState.update {
|
_uiState.update { state ->
|
||||||
(it as? AmountState.Data)?.copy(
|
(state as? AmountState.Data)?.copy(
|
||||||
isPrimaryButtonEnabled = false,
|
isPrimaryButtonEnabled = false,
|
||||||
) ?: it
|
) ?: state
|
||||||
}
|
}
|
||||||
|
|
||||||
val isEnterInFiatSelected = amountFieldData?.amountTextField?.isFiatValue == true
|
val isEnterInFiatSelected = amountFieldData?.amountTextField?.isFiatValue == true
|
||||||
|
|
@ -352,7 +353,9 @@ internal class SendAmountModel @Inject constructor(
|
||||||
private fun subscribeOnAmountUpdateTriggerUpdates() {
|
private fun subscribeOnAmountUpdateTriggerUpdates() {
|
||||||
sendAmountUpdateListener.updateAmountTriggerFlow
|
sendAmountUpdateListener.updateAmountTriggerFlow
|
||||||
.onEach { (amount, isEnterInFiat) ->
|
.onEach { (amount, isEnterInFiat) ->
|
||||||
isEnterInFiat?.let { onCurrencyChangeClick(isEnterInFiat) }
|
if (isEnterInFiat != null) {
|
||||||
|
onCurrencyChangeClick(isEnterInFiat)
|
||||||
|
}
|
||||||
onAmountValueChange(amount)
|
onAmountValueChange(amount)
|
||||||
saveResult()
|
saveResult()
|
||||||
}.launchIn(modelScope)
|
}.launchIn(modelScope)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents
|
||||||
internal sealed class SendDestinationAnalyticEvents(
|
internal sealed class SendDestinationAnalyticEvents(
|
||||||
category: String,
|
category: String,
|
||||||
event: String,
|
event: String,
|
||||||
params: Map<String, String> = mapOf(),
|
params: Map<String, String> = emptyMap(),
|
||||||
) : AnalyticsEvent(category = category, event = event, params = params) {
|
) : AnalyticsEvent(category = category, event = event, params = params) {
|
||||||
|
|
||||||
abstract val categoryName: String
|
abstract val categoryName: String
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ internal class SendDestinationModel @Inject constructor(
|
||||||
|
|
||||||
private val senderAddresses = MutableStateFlow<List<CryptoCurrencyAddress>>(emptyList())
|
private val senderAddresses = MutableStateFlow<List<CryptoCurrencyAddress>>(emptyList())
|
||||||
|
|
||||||
private var validationJobHolder = JobHolder()
|
private val validationJobHolder = JobHolder()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
configDestinationNavigation()
|
configDestinationNavigation()
|
||||||
|
|
@ -234,11 +234,11 @@ internal class SendDestinationModel @Inject constructor(
|
||||||
.map { wallet ->
|
.map { wallet ->
|
||||||
async {
|
async {
|
||||||
val addresses = if (!wallet.isMultiCurrency) {
|
val addresses = if (!wallet.isMultiCurrency) {
|
||||||
getCryptoCurrencyUseCase(wallet.walletId).getOrNull()?.let {
|
getCryptoCurrencyUseCase(wallet.walletId).getOrNull()?.let { cryptoCurrency ->
|
||||||
if (it.network.rawId == cryptoCurrencyNetwork.rawId) {
|
if (cryptoCurrency.network.rawId == cryptoCurrencyNetwork.rawId) {
|
||||||
getNetworkAddressesUseCase.invokeSync(
|
getNetworkAddressesUseCase.invokeSync(
|
||||||
userWalletId = wallet.walletId,
|
userWalletId = wallet.walletId,
|
||||||
networkRawId = it.network.id.rawId,
|
networkRawId = cryptoCurrency.network.id.rawId,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
|
|
@ -323,12 +323,12 @@ internal class SendDestinationModel @Inject constructor(
|
||||||
network = cryptoCurrency.network,
|
network = cryptoCurrency.network,
|
||||||
)
|
)
|
||||||
|
|
||||||
type?.let {
|
if (type != null) {
|
||||||
analyticsEventHandler.send(
|
analyticsEventHandler.send(
|
||||||
SendDestinationAnalyticEvents.AddressEntered(
|
SendDestinationAnalyticEvents.AddressEntered(
|
||||||
categoryName = analyticsCategoryName,
|
categoryName = analyticsCategoryName,
|
||||||
source = params.analyticsSendSource,
|
source = params.analyticsSendSource,
|
||||||
method = it,
|
method = type,
|
||||||
isValid = addressValidationResult.isRight(),
|
isValid = addressValidationResult.isRight(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -339,12 +339,14 @@ internal class SendDestinationModel @Inject constructor(
|
||||||
memoValidationResult,
|
memoValidationResult,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
autoNextFromRecipient(type, addressValidationResult.isRight(), memoValidationResult.isRight())
|
if (type != null) {
|
||||||
|
autoNextFromRecipient(type, addressValidationResult.isRight(), memoValidationResult.isRight())
|
||||||
|
}
|
||||||
}.saveIn(validationJobHolder)
|
}.saveIn(validationJobHolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun autoNextFromRecipient(type: EnterAddressSource?, isValidAddress: Boolean, isValidMemo: Boolean) {
|
private fun autoNextFromRecipient(type: EnterAddressSource, isValidAddress: Boolean, isValidMemo: Boolean) {
|
||||||
if (type?.isAutoNext == true && isValidAddress && isValidMemo) {
|
if (type.isAutoNext && isValidAddress && isValidMemo) {
|
||||||
saveResult()
|
saveResult()
|
||||||
(params as? SendDestinationComponentParams.DestinationParams)?.callback?.onNextClick()
|
(params as? SendDestinationComponentParams.DestinationParams)?.callback?.onNextClick()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,8 @@ internal class SendRecipientHistoryListConverter(
|
||||||
} else {
|
} else {
|
||||||
item.sourceType is TxInfo.SourceType.Single
|
item.sourceType is TxInfo.SourceType.Single
|
||||||
}
|
}
|
||||||
val notZero = !item.amount.isZero()
|
val isNotZero = !item.amount.isZero()
|
||||||
isTransfer && isSingleAddress && isNotContract && item.isOutgoing && notZero
|
isTransfer && isSingleAddress && isNotContract && item.isOutgoing && isNotZero
|
||||||
}
|
}
|
||||||
.take(RECENT_LIST_SIZE)
|
.take(RECENT_LIST_SIZE)
|
||||||
.mapIndexed { index, tx ->
|
.mapIndexed { index, tx ->
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,10 @@ internal class SendRecipientWalletListConverter(
|
||||||
var walletsCounter = 0
|
var walletsCounter = 0
|
||||||
|
|
||||||
return this.filterNotNull()
|
return this.filterNotNull()
|
||||||
.filter {
|
.filter { destinationWallet ->
|
||||||
val isCoin = it.cryptoCurrency is CryptoCurrency.Coin
|
val isCoin = destinationWallet.cryptoCurrency is CryptoCurrency.Coin
|
||||||
val isNotSameAddress = it.address != senderAddress
|
val isNotSameAddress = destinationWallet.address != senderAddress
|
||||||
val isNotBlankAddress = it.address.isNotBlank()
|
val isNotBlankAddress = destinationWallet.address.isNotBlank()
|
||||||
|
|
||||||
isNotBlankAddress && isCoin && (isNotSameAddress || isSelfSendAvailable)
|
isNotBlankAddress && isCoin && (isNotSameAddress || isSelfSendAvailable)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,10 @@ internal const val WALLET_KEY_TAG = "wallet"
|
||||||
internal const val RECENT_KEY_TAG = "recent"
|
internal const val RECENT_KEY_TAG = "recent"
|
||||||
|
|
||||||
internal fun loadingListState(tag: String, count: Int) = buildList {
|
internal fun loadingListState(tag: String, count: Int) = buildList {
|
||||||
repeat(count) {
|
repeat(count) { i ->
|
||||||
add(
|
add(
|
||||||
DestinationRecipientListUM(
|
DestinationRecipientListUM(
|
||||||
id = "$tag$it",
|
id = "$tag$i",
|
||||||
isLoading = true,
|
isLoading = true,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -20,10 +20,10 @@ internal fun loadingListState(tag: String, count: Int) = buildList {
|
||||||
}.toPersistentList()
|
}.toPersistentList()
|
||||||
|
|
||||||
internal fun emptyListState(tag: String, count: Int) = buildList {
|
internal fun emptyListState(tag: String, count: Int) = buildList {
|
||||||
repeat(count) {
|
repeat(count) { i ->
|
||||||
add(
|
add(
|
||||||
DestinationRecipientListUM(
|
DestinationRecipientListUM(
|
||||||
id = "$tag$it",
|
id = "$tag$i",
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
isVisible = false,
|
isVisible = false,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ internal class SendDestinationValidationResultTransformer(
|
||||||
val isValidAddress = addressValidationResult.isRight()
|
val isValidAddress = addressValidationResult.isRight()
|
||||||
val isValidMemo = memoValidationResult.isRight()
|
val isValidMemo = memoValidationResult.isRight()
|
||||||
|
|
||||||
val addressErrorText = addressValidationResult.mapLeft {
|
val addressErrorText = addressValidationResult.mapLeft { error ->
|
||||||
when (it) {
|
when (error) {
|
||||||
is AddressValidation.Error.DataError,
|
is AddressValidation.Error.DataError,
|
||||||
AddressValidation.Error.InvalidAddress,
|
AddressValidation.Error.InvalidAddress,
|
||||||
-> R.string.send_recipient_address_error
|
-> R.string.send_recipient_address_error
|
||||||
|
|
|
||||||
|
|
@ -67,10 +67,13 @@ internal fun SendDestinationContent(
|
||||||
onAddressChange = clickIntents::onRecipientAddressValueChange,
|
onAddressChange = clickIntents::onRecipientAddressValueChange,
|
||||||
onQrCodeClick = clickIntents::onQrCodeScanClick,
|
onQrCodeClick = clickIntents::onQrCodeScanClick,
|
||||||
)
|
)
|
||||||
memoField(
|
val memoTextField = state.memoTextField
|
||||||
memoField = state.memoTextField,
|
if (memoTextField != null) {
|
||||||
onMemoChange = clickIntents::onRecipientMemoValueChange,
|
memoField(
|
||||||
)
|
memoField = memoTextField,
|
||||||
|
onMemoChange = clickIntents::onRecipientMemoValueChange,
|
||||||
|
)
|
||||||
|
}
|
||||||
listHeaderItem(
|
listHeaderItem(
|
||||||
titleRes = if (state.isAccountsMode == true) {
|
titleRes = if (state.isAccountsMode == true) {
|
||||||
R.string.common_accounts
|
R.string.common_accounts
|
||||||
|
|
@ -153,40 +156,38 @@ private fun LazyListScope.addressItem(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun LazyListScope.memoField(
|
private fun LazyListScope.memoField(
|
||||||
memoField: DestinationTextFieldUM.RecipientMemo?,
|
memoField: DestinationTextFieldUM.RecipientMemo,
|
||||||
onMemoChange: (String, Boolean) -> Unit,
|
onMemoChange: (String, Boolean) -> Unit,
|
||||||
) {
|
) {
|
||||||
if (memoField != null) {
|
item(key = MEMO_FIELD_KEY) {
|
||||||
item(key = MEMO_FIELD_KEY) {
|
val placeholder = if (memoField.isEnabled) memoField.placeholder else memoField.disabledText
|
||||||
val placeholder = if (memoField.isEnabled) memoField.placeholder else memoField.disabledText
|
TextFieldWithPaste(
|
||||||
TextFieldWithPaste(
|
value = memoField.value,
|
||||||
value = memoField.value,
|
label = memoField.label,
|
||||||
label = memoField.label,
|
placeholder = placeholder,
|
||||||
placeholder = placeholder,
|
footer = annotatedReference(
|
||||||
footer = annotatedReference(
|
buildAnnotatedString {
|
||||||
buildAnnotatedString {
|
append(stringResourceSafe(R.string.send_recipient_memo_footer_v2))
|
||||||
append(stringResourceSafe(R.string.send_recipient_memo_footer_v2))
|
append("\n")
|
||||||
append("\n")
|
withStyle(SpanStyle(fontWeight = FontWeight.Medium)) {
|
||||||
withStyle(SpanStyle(fontWeight = FontWeight.Medium)) {
|
appendColored(
|
||||||
appendColored(
|
text = stringResourceSafe(
|
||||||
text = stringResourceSafe(
|
R.string.send_recipient_memo_footer_v2_highlighted,
|
||||||
R.string.send_recipient_memo_footer_v2_highlighted,
|
),
|
||||||
),
|
color = TangemTheme.colors.text.secondary,
|
||||||
color = TangemTheme.colors.text.secondary,
|
)
|
||||||
)
|
}
|
||||||
}
|
},
|
||||||
},
|
),
|
||||||
),
|
onValueChange = { onMemoChange(it, false) },
|
||||||
onValueChange = { onMemoChange(it, false) },
|
onPasteClick = { onMemoChange(it, true) },
|
||||||
onPasteClick = { onMemoChange(it, true) },
|
modifier = Modifier.padding(top = 20.dp),
|
||||||
modifier = Modifier.padding(top = 20.dp),
|
labelStyle = TangemTheme.typography.subtitle2,
|
||||||
labelStyle = TangemTheme.typography.subtitle2,
|
isError = memoField.isError,
|
||||||
isError = memoField.isError,
|
error = memoField.error,
|
||||||
error = memoField.error,
|
isReadOnly = !memoField.isEnabled,
|
||||||
isReadOnly = !memoField.isEnabled,
|
isValuePasted = memoField.isValuePasted,
|
||||||
isValuePasted = memoField.isValuePasted,
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -297,8 +298,8 @@ private fun AnimateRecentAppearance(isVisible: Boolean, content: @Composable ()
|
||||||
(slideInHorizontally() + fadeIn())
|
(slideInHorizontally() + fadeIn())
|
||||||
.togetherWith(slideOutVertically() + fadeOut())
|
.togetherWith(slideOutVertically() + fadeOut())
|
||||||
},
|
},
|
||||||
) {
|
) { currentVisibility ->
|
||||||
if (it) {
|
if (currentVisibility) {
|
||||||
content()
|
content()
|
||||||
} else {
|
} else {
|
||||||
Box(modifier = Modifier.fillMaxWidth())
|
Box(modifier = Modifier.fillMaxWidth())
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ internal class BitcoinCustomFeeConverter(
|
||||||
amount = feeValue,
|
amount = feeValue,
|
||||||
decimals = value.amount.decimals,
|
decimals = value.amount.decimals,
|
||||||
txSize = value.txSize,
|
txSize = value.txSize,
|
||||||
).toString(),
|
)?.toString().orEmpty(),
|
||||||
decimals = SATOSHI_DECIMALS,
|
decimals = SATOSHI_DECIMALS,
|
||||||
symbol = "",
|
symbol = "",
|
||||||
title = resourceReference(R.string.send_satoshi_per_byte_title),
|
title = resourceReference(R.string.send_satoshi_per_byte_title),
|
||||||
|
|
|
||||||
|
|
@ -39,14 +39,16 @@ internal class EthereumCustomFeeConverter(
|
||||||
|
|
||||||
override fun convert(value: Fee.Ethereum): ImmutableList<CustomFeeFieldUM> {
|
override fun convert(value: Fee.Ethereum): ImmutableList<CustomFeeFieldUM> {
|
||||||
return buildList {
|
return buildList {
|
||||||
convertFeeValue(value).let(::add)
|
add(convertFeeValue(value))
|
||||||
|
|
||||||
when (value) {
|
addAll(
|
||||||
is Fee.Ethereum.EIP1559 -> eipFeeConverter.convert(value)
|
when (value) {
|
||||||
is Fee.Ethereum.Legacy -> legacyFeeConverter.convert(value)
|
is Fee.Ethereum.EIP1559 -> eipFeeConverter.convert(value)
|
||||||
}.let(::addAll)
|
is Fee.Ethereum.Legacy -> legacyFeeConverter.convert(value)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
convertGasLimitValue(value).let(::add)
|
add(convertGasLimitValue(value))
|
||||||
}
|
}
|
||||||
.toImmutableList()
|
.toImmutableList()
|
||||||
}
|
}
|
||||||
|
|
@ -72,8 +74,18 @@ internal class EthereumCustomFeeConverter(
|
||||||
value: String,
|
value: String,
|
||||||
): ImmutableList<CustomFeeFieldUM> {
|
): ImmutableList<CustomFeeFieldUM> {
|
||||||
return when (feeValue) {
|
return when (feeValue) {
|
||||||
is Fee.Ethereum.EIP1559 -> eipFeeConverter.onValueChange(feeValue, customValues, index, value)
|
is Fee.Ethereum.EIP1559 -> eipFeeConverter.onValueChange(
|
||||||
is Fee.Ethereum.Legacy -> legacyFeeConverter.onValueChange(feeValue, customValues, index, value)
|
feeValue = feeValue,
|
||||||
|
customValues = customValues,
|
||||||
|
index = index,
|
||||||
|
value = value,
|
||||||
|
)
|
||||||
|
is Fee.Ethereum.Legacy -> legacyFeeConverter.onValueChange(
|
||||||
|
feeValue = feeValue,
|
||||||
|
customValues = customValues,
|
||||||
|
index = index,
|
||||||
|
value = value,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -101,15 +101,16 @@ internal class KaspaCustomFeeConverter(
|
||||||
// check that there is reveal transaction info (= krc-20 token transfer)
|
// check that there is reveal transaction info (= krc-20 token transfer)
|
||||||
// return without changes otherwise
|
// return without changes otherwise
|
||||||
if (minimumFee.revealTransactionFee != null && minimumFeeAmountValue != null) {
|
if (minimumFee.revealTransactionFee != null && minimumFeeAmountValue != null) {
|
||||||
getOrNull(FEE_AMOUNT_INDEX)?.let {
|
val customFeeFieldUM = getOrNull(FEE_AMOUNT_INDEX)
|
||||||
val valueDecimal = it.value.parseToBigDecimal(it.decimals)
|
if (customFeeFieldUM != null) {
|
||||||
|
val valueDecimal = customFeeFieldUM.value.parseToBigDecimal(customFeeFieldUM.decimals)
|
||||||
// krc-20 transaction will be failed if custom fee value is less than minimum,
|
// krc-20 transaction will be failed if custom fee value is less than minimum,
|
||||||
// so we set value to minimum in this case
|
// so we set value to minimum in this case
|
||||||
if (valueDecimal < minimumFee.amount.value) {
|
if (valueDecimal < minimumFee.amount.value) {
|
||||||
val fixedValue = minimumFeeAmountValue.parseBigDecimal(it.decimals)
|
val fixedValue = minimumFeeAmountValue.parseBigDecimal(customFeeFieldUM.decimals)
|
||||||
set(
|
set(
|
||||||
FEE_AMOUNT_INDEX,
|
FEE_AMOUNT_INDEX,
|
||||||
it.copy(
|
customFeeFieldUM.copy(
|
||||||
value = fixedValue,
|
value = fixedValue,
|
||||||
label = getFiatReference(
|
label = getFiatReference(
|
||||||
rate = currencyStatus.fiatRate,
|
rate = currencyStatus.fiatRate,
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM
|
||||||
internal sealed class NotificationsAnalyticEvents(
|
internal sealed class NotificationsAnalyticEvents(
|
||||||
category: String,
|
category: String,
|
||||||
event: String,
|
event: String,
|
||||||
params: Map<String, String> = mapOf(),
|
params: Map<String, String> = emptyMap(),
|
||||||
) : AnalyticsEvent(category = category, event = event, params = params) {
|
) : AnalyticsEvent(category = category, event = event, params = params) {
|
||||||
|
|
||||||
abstract val categoryName: String
|
abstract val categoryName: String
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,7 @@ class NFTSendConfirmationNotificationsTransformerV2Test {
|
||||||
isPrimaryButtonEnabled = true,
|
isPrimaryButtonEnabled = true,
|
||||||
walletName = mockk(relaxed = true),
|
walletName = mockk(relaxed = true),
|
||||||
isSending = false,
|
isSending = false,
|
||||||
showTapHelp = false,
|
isShowTapHelp = false,
|
||||||
sendingFooter = mockk(relaxed = true),
|
sendingFooter = mockk(relaxed = true),
|
||||||
notifications = persistentListOf(),
|
notifications = persistentListOf(),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ class SendConfirmationNotificationsTransformerV2Test {
|
||||||
isPrimaryButtonEnabled = true,
|
isPrimaryButtonEnabled = true,
|
||||||
walletName = mockk(relaxed = true),
|
walletName = mockk(relaxed = true),
|
||||||
isSending = false,
|
isSending = false,
|
||||||
showTapHelp = false,
|
isShowTapHelp = false,
|
||||||
sendingFooter = mockk(relaxed = true),
|
sendingFooter = mockk(relaxed = true),
|
||||||
notifications = persistentListOf(),
|
notifications = persistentListOf(),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,7 @@ internal class WcSendTransactionModel @Inject constructor(
|
||||||
feeReloadState.value = false
|
feeReloadState.value = false
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
feeSelectorReloadTrigger.triggerUpdate(
|
feeSelectorReloadTrigger.triggerUpdate(
|
||||||
FeeSelectorData(removeSuggestedFee = feeStateConfiguration !is FeeStateConfiguration.Suggestion),
|
FeeSelectorData(isRemoveSuggestedFee = feeStateConfiguration !is FeeStateConfiguration.Suggestion),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue