Updated on 2026-08-14
This commit is contained in:
parent
d5d9c7bd2d
commit
f69235c0d7
85 changed files with 564 additions and 762 deletions
|
|
@ -115,7 +115,7 @@ object MarketsDomainModule {
|
||||||
return FilterAvailableNetworksForWalletUseCase(
|
return FilterAvailableNetworksForWalletUseCase(
|
||||||
userWalletsListManager = userWalletsListManager,
|
userWalletsListManager = userWalletsListManager,
|
||||||
userWalletsListRepository = userWalletsListRepository,
|
userWalletsListRepository = userWalletsListRepository,
|
||||||
useNewRepository = hotWalletFeatureToggles.isHotWalletEnabled,
|
shouldUseNewRepository = hotWalletFeatureToggles.isHotWalletEnabled,
|
||||||
excludedBlockchains = excludedBlockchains,
|
excludedBlockchains = excludedBlockchains,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,20 +9,11 @@
|
||||||
<ID>BooleanPropertyNaming:NotificationUM.kt$NotificationUM.Error.ExceedsBalance$val mergeFeeNetworkName: Boolean = false</ID>
|
<ID>BooleanPropertyNaming:NotificationUM.kt$NotificationUM.Error.ExceedsBalance$val mergeFeeNetworkName: Boolean = false</ID>
|
||||||
<ID>BooleanPropertyNaming:NotificationsFactory.kt$NotificationsFactory$val showNotification = sendingAmount + feeAmount > balance - minimumRequirement.orZero()</ID>
|
<ID>BooleanPropertyNaming:NotificationsFactory.kt$NotificationsFactory$val showNotification = sendingAmount + feeAmount > balance - minimumRequirement.orZero()</ID>
|
||||||
<ID>BooleanPropertyNaming:TokenReceiveBottomSheetConfig.kt$TokenReceiveBottomSheetConfig$val showMemoDisclaimer: Boolean</ID>
|
<ID>BooleanPropertyNaming:TokenReceiveBottomSheetConfig.kt$TokenReceiveBottomSheetConfig$val showMemoDisclaimer: Boolean</ID>
|
||||||
<ID>CanBeNonNullable:AmountBlockV2.kt$onClick: (() -> Unit)? = null</ID>
|
|
||||||
<ID>CanBeNonNullable:NavigationButtonsBlock.kt$footerText: TextReference?</ID>
|
|
||||||
<ID>CanBeNonNullable:NavigationButtonsBlock.kt$pairButtons: Pair<NavigationButton, NavigationButton>?</ID>
|
|
||||||
<ID>CanBeNonNullable:NavigationButtonsBlock.kt$prevButton: NavigationButton?</ID>
|
|
||||||
<ID>CanBeNonNullable:NotificationsFactory.kt$NotificationsFactory$dustValue: BigDecimal?</ID>
|
|
||||||
<ID>CanBeNonNullable:NotificationsFactory.kt$NotificationsFactory$minAdaValue: BigDecimal?</ID>
|
|
||||||
<ID>CanBeNonNullable:NotificationsFactory.kt$NotificationsFactory$minimumSendAmount: BigDecimal?</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.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>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>
|
||||||
<ID>NullableToStringCall:TokenItemStateConverter.kt$TokenItemStateConverter.Companion$${id.rawCurrencyId}</ID>
|
|
||||||
<ID>ReusedModifierInstance:AddTokenContent.kt$AddButton( modifier = modifier.fillMaxWidth(), state = state.button, )</ID>
|
<ID>ReusedModifierInstance:AddTokenContent.kt$AddButton( modifier = modifier.fillMaxWidth(), state = state.button, )</ID>
|
||||||
<ID>UnnecessaryEventHandlerParameter:SendDoneButtons.kt$onShareClick: (String) -> Unit</ID>
|
<ID>UnnecessaryEventHandlerParameter:SendDoneButtons.kt$onShareClick: (String) -> Unit</ID>
|
||||||
<ID>UnnecessaryLet:TokenItemStateConverter.kt$TokenItemStateConverter.Companion$let(::add)</ID>
|
<ID>UnnecessaryLet:TokenItemStateConverter.kt$TokenItemStateConverter.Companion$let(::add)</ID>
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,5 @@
|
||||||
<ID>NestedScopeFunctions:RetrofitApiBuilder.kt$RetrofitApiBuilder$let { withWriteTimeout(timeout = it.duration, unit = it.unit) }</ID>
|
<ID>NestedScopeFunctions:RetrofitApiBuilder.kt$RetrofitApiBuilder$let { withWriteTimeout(timeout = it.duration, unit = it.unit) }</ID>
|
||||||
<ID>UnreachableCode:MockApiConfigsManager.kt$MockApiConfigsManager$apiConfigs + (apiConfig to environment)</ID>
|
<ID>UnreachableCode:MockApiConfigsManager.kt$MockApiConfigsManager$apiConfigs + (apiConfig to environment)</ID>
|
||||||
<ID>UnreachableCode:MockApiConfigsManager.kt$MockApiConfigsManager$val apiConfig = apiConfigs.keys.firstOrNull { it.id.name == id } ?: error("Api config with id [$id] not found. Check that ApiConfig with id [$id] was provided into DI")</ID>
|
<ID>UnreachableCode:MockApiConfigsManager.kt$MockApiConfigsManager$val apiConfig = apiConfigs.keys.firstOrNull { it.id.name == id } ?: error("Api config with id [$id] not found. Check that ApiConfig with id [$id] was provided into DI")</ID>
|
||||||
<ID>UnusedImports:NetworkModule.kt$import com.tangem.datasource.api.common.config.MoonPay</ID>
|
|
||||||
</CurrentIssues>
|
</CurrentIssues>
|
||||||
</SmellBaseline>
|
</SmellBaseline>
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,6 @@
|
||||||
<ID>BooleanPropertyNaming:BigDecimalPercentFormat.kt$BigDecimalPercentFormat$val withPercentSign: Boolean = true</ID>
|
<ID>BooleanPropertyNaming:BigDecimalPercentFormat.kt$BigDecimalPercentFormat$val withPercentSign: Boolean = true</ID>
|
||||||
<ID>BooleanPropertyNaming:FullScreen.kt$FullScreenLayout$private val focusable: Boolean</ID>
|
<ID>BooleanPropertyNaming:FullScreen.kt$FullScreenLayout$private val focusable: Boolean</ID>
|
||||||
<ID>BooleanPropertyNaming:TokenItemState.kt$TokenItemState.TitleState.Content$val earnApyIsActive: Boolean = false</ID>
|
<ID>BooleanPropertyNaming:TokenItemState.kt$TokenItemState.TitleState.Content$val earnApyIsActive: Boolean = false</ID>
|
||||||
<ID>CanBeNonNullable:FooterContainer.kt$footer: TextReference? = null</ID>
|
|
||||||
<ID>CanBeNonNullable:InputRowImageBase.kt$iconRes: Int?</ID>
|
|
||||||
<ID>CanBeNonNullable:InputRowImageInfo.kt$subtitleEndIconRes: Int?</ID>
|
|
||||||
<ID>CanBeNonNullable:SearchBar.kt$keyboardController: SoftwareKeyboardController?</ID>
|
|
||||||
<ID>CanBeNonNullable:TextFields.kt$caption: String? = null</ID>
|
|
||||||
<ID>CanBeNonNullable:TransactionList.kt$txHistoryItems: LazyPagingItems<TxHistoryState.TxHistoryItemState>?</ID>
|
|
||||||
<ID>CastNullableToNonNullableType:DialogFullScreen.kt$as</ID>
|
<ID>CastNullableToNonNullableType:DialogFullScreen.kt$as</ID>
|
||||||
<ID>CastNullableToNonNullableType:FullScreen.kt$FullScreenLayout$as</ID>
|
<ID>CastNullableToNonNullableType:FullScreen.kt$FullScreenLayout$as</ID>
|
||||||
<ID>MultilineLambdaItParameter:Actions.kt${ ActionButtonContent( config = config, text = { textColor -> Text(text = config.text, textColor = textColor) }, modifier = it.padding( start = TangemTheme.dimens.spacing16, end = TangemTheme.dimens.spacing24, ), ) }</ID>
|
<ID>MultilineLambdaItParameter:Actions.kt${ ActionButtonContent( config = config, text = { textColor -> Text(text = config.text, textColor = textColor) }, modifier = it.padding( start = TangemTheme.dimens.spacing16, end = TangemTheme.dimens.spacing24, ), ) }</ID>
|
||||||
|
|
@ -27,6 +21,5 @@
|
||||||
<ID>ReusedModifierInstance:TangemRadioButton.kt$AnimatedVisibility( visible = isSelected, label = "Radio button animation", modifier = modifier .size(TangemTheme.dimens.size24), ) { Icon( painter = painterResource(id = R.drawable.ic_check_circle_24), contentDescription = null, tint = TangemTheme.colors.control.checked, ) }</ID>
|
<ID>ReusedModifierInstance:TangemRadioButton.kt$AnimatedVisibility( visible = isSelected, label = "Radio button animation", modifier = modifier .size(TangemTheme.dimens.size24), ) { Icon( painter = painterResource(id = R.drawable.ic_check_circle_24), contentDescription = null, tint = TangemTheme.colors.control.checked, ) }</ID>
|
||||||
<ID>ReusedModifierInstance:TokenPrice.kt$Icon( modifier = modifier, painter = painterResource( id = when (animatedType) { PriceChangeType.UP -> R.drawable.ic_arrow_up_8 PriceChangeType.DOWN -> R.drawable.ic_arrow_down_8 PriceChangeType.NEUTRAL -> R.drawable.ic_elipse_8 }, ), tint = when (animatedType) { PriceChangeType.UP -> TangemTheme.colors.icon.accent PriceChangeType.DOWN -> TangemTheme.colors.icon.warning PriceChangeType.NEUTRAL -> TangemTheme.colors.icon.inactive }, contentDescription = null, )</ID>
|
<ID>ReusedModifierInstance:TokenPrice.kt$Icon( modifier = modifier, painter = painterResource( id = when (animatedType) { PriceChangeType.UP -> R.drawable.ic_arrow_up_8 PriceChangeType.DOWN -> R.drawable.ic_arrow_down_8 PriceChangeType.NEUTRAL -> R.drawable.ic_elipse_8 }, ), tint = when (animatedType) { PriceChangeType.UP -> TangemTheme.colors.icon.accent PriceChangeType.DOWN -> TangemTheme.colors.icon.warning PriceChangeType.NEUTRAL -> TangemTheme.colors.icon.inactive }, contentDescription = null, )</ID>
|
||||||
<ID>UnnecessaryEventHandlerParameter:PinTextField.kt$onValueChange: (String) -> Unit</ID>
|
<ID>UnnecessaryEventHandlerParameter:PinTextField.kt$onValueChange: (String) -> Unit</ID>
|
||||||
<ID>UnnecessaryEventHandlerParameter:ResizableText.kt$onFontSizeChange: (Float) -> Unit</ID>
|
|
||||||
</CurrentIssues>
|
</CurrentIssues>
|
||||||
</SmellBaseline>
|
</SmellBaseline>
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
<?xml version="1.0" ?>
|
|
||||||
<SmellBaseline>
|
|
||||||
<ManuallySuppressedIssues/>
|
|
||||||
<CurrentIssues>
|
|
||||||
<ID>BooleanPropertyNaming:DefaultMarketsTokenRepository.kt$DefaultMarketsTokenRepository.<no name provided>$val last = res.tokens.size < request.limit</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:DefaultMarketsTokenRepository.kt$DefaultMarketsTokenRepository${ val error = it as QuotesFetcher.Error.ApiOperationError val errorEvent = createDetailsErrorEvent( error = error.apiError, request = MarketsDataAnalyticsEvent.Details.Error.Request.Info, tokenSymbol = tokenSymbol, ) analyticsEventHandler.send(errorEvent.toEvent()) throw error.apiError }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsBatchUpdateFetcher.kt$MarketsBatchUpdateFetcher${ it.copy( tokenCharts = TokenMarketChartsConverter.convert( chartsToCopy = it.tokenCharts, tokenId = it.id, interval = updateRequest.interval, value = update, ), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsBatchUpdateFetcher.kt$MarketsBatchUpdateFetcher${ val exception = if (it is QuotesFetcher.Error.ApiOperationError) { onApiResponseError(it.apiError) it.apiError } else { error("Cause: $it") } throw exception }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:TokenMarketInfoConverter.kt$TokenMarketInfoConverter${ TokenMarketInfo.Link( title = it.title, id = it.id, link = it.link, ) }</ID>
|
|
||||||
<ID>NullableBooleanCheck:TokenMarketListConverter.kt$TokenMarketListConverter$token.isUnderMarketCapLimit ?: false</ID>
|
|
||||||
<ID>SuspendFunWithFlowReturnType:DefaultMarketsTokenRepository.kt$DefaultMarketsTokenRepository$suspend</ID>
|
|
||||||
<ID>UnsafeCallOnNullableType:DefaultMarketsTokenRepository.kt$DefaultMarketsTokenRepository$network.contractAddress!!</ID>
|
|
||||||
<ID>UseEmptyCounterpart:MarketsDataAnalyticsEvent.kt$MarketsDataAnalyticsEvent.Details$mapOf()</ID>
|
|
||||||
<ID>UseEmptyCounterpart:MarketsDataAnalyticsEvent.kt$MarketsDataAnalyticsEvent.List$mapOf()</ID>
|
|
||||||
</CurrentIssues>
|
|
||||||
</SmellBaseline>
|
|
||||||
|
|
@ -90,13 +90,13 @@ internal class DefaultMarketsTokenRepository(
|
||||||
requestTimeStamp.set(res.timestamp ?: 0)
|
requestTimeStamp.set(res.timestamp ?: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
val last = res.tokens.size < request.limit
|
val isLast = res.tokens.size < request.limit
|
||||||
|
|
||||||
val tokenMarketListWithMaxApy = TokenMarketListConverter.convert(res)
|
val tokenMarketListWithMaxApy = TokenMarketListConverter.convert(res)
|
||||||
|
|
||||||
return BatchFetchResult.Success(
|
return BatchFetchResult.Success(
|
||||||
data = tokenMarketListWithMaxApy.tokens,
|
data = tokenMarketListWithMaxApy.tokens,
|
||||||
last = last,
|
last = isLast,
|
||||||
empty = res.tokens.isEmpty(),
|
empty = res.tokens.isEmpty(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -227,8 +227,8 @@ internal class DefaultMarketsTokenRepository(
|
||||||
currencyId = tokenId.value,
|
currencyId = tokenId.value,
|
||||||
field = QuotesFetcher.Field.ALL_PRICES,
|
field = QuotesFetcher.Field.ALL_PRICES,
|
||||||
)
|
)
|
||||||
.getOrElse {
|
.getOrElse { fetchError ->
|
||||||
val error = it as QuotesFetcher.Error.ApiOperationError
|
val error = fetchError as QuotesFetcher.Error.ApiOperationError
|
||||||
|
|
||||||
val errorEvent = createDetailsErrorEvent(
|
val errorEvent = createDetailsErrorEvent(
|
||||||
error = error.apiError,
|
error = error.apiError,
|
||||||
|
|
@ -274,7 +274,7 @@ internal class DefaultMarketsTokenRepository(
|
||||||
name = token.name,
|
name = token.name,
|
||||||
symbol = token.symbol,
|
symbol = token.symbol,
|
||||||
decimals = network.decimalCount ?: error("Unknown decimal"),
|
decimals = network.decimalCount ?: error("Unknown decimal"),
|
||||||
contractAddress = network.contractAddress!!,
|
contractAddress = requireNotNull(network.contractAddress) { "Contract address is required for token" },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,13 +79,13 @@ internal class MarketsBatchUpdateFetcher(
|
||||||
currenciesIds = currenciesIds,
|
currenciesIds = currenciesIds,
|
||||||
fields = setOf(QuotesFetcher.Field.ALL_PRICES),
|
fields = setOf(QuotesFetcher.Field.ALL_PRICES),
|
||||||
)
|
)
|
||||||
.getOrElse {
|
.getOrElse { error ->
|
||||||
val exception = if (it is QuotesFetcher.Error.ApiOperationError) {
|
val exception = if (error is QuotesFetcher.Error.ApiOperationError) {
|
||||||
onApiResponseError(it.apiError)
|
onApiResponseError(error.apiError)
|
||||||
|
|
||||||
it.apiError
|
error.apiError
|
||||||
} else {
|
} else {
|
||||||
error("Cause: $it")
|
error("Cause: $error")
|
||||||
}
|
}
|
||||||
|
|
||||||
throw exception
|
throw exception
|
||||||
|
|
@ -116,11 +116,11 @@ internal class MarketsBatchUpdateFetcher(
|
||||||
|
|
||||||
Batch(
|
Batch(
|
||||||
key = batchToUpdate.key,
|
key = batchToUpdate.key,
|
||||||
data = batchToUpdate.data.map {
|
data = batchToUpdate.data.map { tokenMarket ->
|
||||||
it.copy(
|
tokenMarket.copy(
|
||||||
tokenCharts = TokenMarketChartsConverter.convert(
|
tokenCharts = TokenMarketChartsConverter.convert(
|
||||||
chartsToCopy = it.tokenCharts,
|
chartsToCopy = tokenMarket.tokenCharts,
|
||||||
tokenId = it.id,
|
tokenId = tokenMarket.id,
|
||||||
interval = updateRequest.interval,
|
interval = updateRequest.interval,
|
||||||
value = update,
|
value = update,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ sealed interface MarketsDataAnalyticsEvent {
|
||||||
|
|
||||||
sealed class List(
|
sealed class List(
|
||||||
event: String,
|
event: String,
|
||||||
params: Map<String, String> = mapOf(),
|
params: Map<String, String> = emptyMap(),
|
||||||
) : AnalyticsEvent(category = "Markets", event = event, params = params), MarketsDataAnalyticsEvent {
|
) : AnalyticsEvent(category = "Markets", event = event, params = params), MarketsDataAnalyticsEvent {
|
||||||
|
|
||||||
data class Error(
|
data class Error(
|
||||||
|
|
@ -26,7 +26,7 @@ sealed interface MarketsDataAnalyticsEvent {
|
||||||
|
|
||||||
sealed class Details(
|
sealed class Details(
|
||||||
event: String,
|
event: String,
|
||||||
params: Map<String, String> = mapOf(),
|
params: Map<String, String> = emptyMap(),
|
||||||
) : AnalyticsEvent(category = "Markets / Chart", event = event, params = params), MarketsDataAnalyticsEvent {
|
) : AnalyticsEvent(category = "Markets / Chart", event = event, params = params), MarketsDataAnalyticsEvent {
|
||||||
|
|
||||||
data class Error(
|
data class Error(
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ internal class TokenMarketInfoConverter(
|
||||||
network.contractAddress.isNullOrEmpty() -> {
|
network.contractAddress.isNullOrEmpty() -> {
|
||||||
TokenMarketInfo.Network(
|
TokenMarketInfo.Network(
|
||||||
networkId = network.networkId,
|
networkId = network.networkId,
|
||||||
exchangeable = network.exchangeable,
|
isExchangeable = network.exchangeable,
|
||||||
contractAddress = network.contractAddress,
|
contractAddress = network.contractAddress,
|
||||||
decimalCount = network.decimalCount,
|
decimalCount = network.decimalCount,
|
||||||
)
|
)
|
||||||
|
|
@ -71,7 +71,7 @@ internal class TokenMarketInfoConverter(
|
||||||
blockchain.canHandleTokens() -> {
|
blockchain.canHandleTokens() -> {
|
||||||
TokenMarketInfo.Network(
|
TokenMarketInfo.Network(
|
||||||
networkId = network.networkId,
|
networkId = network.networkId,
|
||||||
exchangeable = network.exchangeable,
|
isExchangeable = network.exchangeable,
|
||||||
contractAddress = blockchain.reformatContractAddress(network.contractAddress),
|
contractAddress = blockchain.reformatContractAddress(network.contractAddress),
|
||||||
decimalCount = network.decimalCount,
|
decimalCount = network.decimalCount,
|
||||||
)
|
)
|
||||||
|
|
@ -147,11 +147,11 @@ internal class TokenMarketInfoConverter(
|
||||||
|
|
||||||
@JvmName("convertLink")
|
@JvmName("convertLink")
|
||||||
private fun List<TokenMarketInfoResponse.Link>.convert(): List<TokenMarketInfo.Link> {
|
private fun List<TokenMarketInfoResponse.Link>.convert(): List<TokenMarketInfo.Link> {
|
||||||
return map {
|
return map { link ->
|
||||||
TokenMarketInfo.Link(
|
TokenMarketInfo.Link(
|
||||||
title = it.title,
|
title = link.title,
|
||||||
id = it.id,
|
id = link.id,
|
||||||
link = it.link,
|
link = link.link,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ internal object TokenMarketListConverter : Converter<TokenMarketListResponse, To
|
||||||
symbol = token.symbol,
|
symbol = token.symbol,
|
||||||
marketRating = token.marketRating,
|
marketRating = token.marketRating,
|
||||||
marketCap = token.marketCap,
|
marketCap = token.marketCap,
|
||||||
isUnderMarketCapLimit = token.isUnderMarketCapLimit ?: false,
|
isUnderMarketCapLimit = token.isUnderMarketCapLimit == true,
|
||||||
imageHost = imageHost,
|
imageHost = imageHost,
|
||||||
tokenQuotesShort = TokenQuotesShort(
|
tokenQuotesShort = TokenQuotesShort(
|
||||||
currentPrice = token.currentPrice,
|
currentPrice = token.currentPrice,
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@
|
||||||
<ID>MultilineLambdaItParameter:WcEthMessageSignUseCase.kt$LegacySdkHelper${ val char = it.toInt().toChar() if (char.isAscii()) char else return null }</ID>
|
<ID>MultilineLambdaItParameter:WcEthMessageSignUseCase.kt$LegacySdkHelper${ val char = it.toInt().toChar() if (char.isAscii()) char else return null }</ID>
|
||||||
<ID>MultilineLambdaItParameter:WcEthNetwork.kt$WcEthNetwork${ if (this == WcEthMethodName.AddEthereumChain) { WcEthMethod.AddEthereumChain(rawChain = it).right() } else { WcEthMethod.SwitchEthereumChain(rawChain = it).right() } }</ID>
|
<ID>MultilineLambdaItParameter:WcEthNetwork.kt$WcEthNetwork${ if (this == WcEthMethodName.AddEthereumChain) { WcEthMethod.AddEthereumChain(rawChain = it).right() } else { WcEthMethod.SwitchEthereumChain(rawChain = it).right() } }</ID>
|
||||||
<ID>MultilineLambdaItParameter:WcEthNetwork.kt$WcEthNetwork${ if (this == WcEthMethodName.SignTransaction) { WcEthMethod.SignTransaction(transaction = it).right() } else { WcEthMethod.SendTransaction(transaction = it).right() } }</ID>
|
<ID>MultilineLambdaItParameter:WcEthNetwork.kt$WcEthNetwork${ if (this == WcEthMethodName.SignTransaction) { WcEthMethod.SignTransaction(transaction = it).right() } else { WcEthMethod.SendTransaction(transaction = it).right() } }</ID>
|
||||||
<ID>MultilineLambdaItParameter:WcNetworksConverter.kt$WcNetworksConverter${ val walletAddress = walletManagersFacade.getDefaultAddress(wallet.walletId, it) walletAddress?.lowercase() == caip10.accountAddress.lowercase() }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:WcPairSdkDelegate.kt$WcPairSdkDelegate${ proposalCallback.cancel() return@coroutineScope it.left() }</ID>
|
<ID>MultilineLambdaItParameter:WcPairSdkDelegate.kt$WcPairSdkDelegate${ proposalCallback.cancel() return@coroutineScope it.left() }</ID>
|
||||||
<ID>MultilineLambdaItParameter:WcPairSdkDelegate.kt$WcPairSdkDelegate${ store.removePendingApproval(forSave) it.left() }</ID>
|
<ID>MultilineLambdaItParameter:WcPairSdkDelegate.kt$WcPairSdkDelegate${ store.removePendingApproval(forSave) it.left() }</ID>
|
||||||
<ID>MultilineLambdaItParameter:WcSdkSessionConverter.kt$WcSdkSessionConverter${ WcAppMetaDataConverter.convert( value = WcAppMetaDataConverter.Input( originUrl = value.originUrl, peerMetaData = it, ), ) }</ID>
|
<ID>MultilineLambdaItParameter:WcSdkSessionConverter.kt$WcSdkSessionConverter${ WcAppMetaDataConverter.convert( value = WcAppMetaDataConverter.Input( originUrl = value.originUrl, peerMetaData = it, ), ) }</ID>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
==========================================
|
==========================================
|
||||||
Detekt Baseline Updater & Issue Counter
|
Detekt Baseline Updater & Issue Counter
|
||||||
==========================================
|
==========================================
|
||||||
Date: 2025-12-13 00:34:09
|
Date: 2026-01-04 19:04:51
|
||||||
|
|
||||||
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: 1271
|
Total Issues: 1065
|
||||||
Modules with Issues: 69
|
Modules with Issues: 65
|
||||||
Average Issues per Module: 18
|
Average Issues per Module: 16
|
||||||
|
|
||||||
Progress:
|
Progress:
|
||||||
Fixed: 662 out of 1933 (34%)
|
Fixed: 868 out of 1933 (44%)
|
||||||
Remaining: 1271
|
Remaining: 1065
|
||||||
|
|
||||||
==========================================
|
==========================================
|
||||||
All Modules with Issues (sorted by count)
|
All Modules with Issues (sorted by count)
|
||||||
|
|
@ -31,54 +31,51 @@ All Modules with Issues (sorted by count)
|
||||||
|
|
||||||
Module Issues
|
Module Issues
|
||||||
────────────────────────────────────────────────────────────────
|
────────────────────────────────────────────────────────────────
|
||||||
features/markets/impl 147
|
features/wallet/impl 138
|
||||||
features/wallet/impl 144
|
|
||||||
features/onboarding-v2/impl 128
|
features/onboarding-v2/impl 128
|
||||||
data/wallet-connect 54
|
data/wallet-connect 53
|
||||||
features/hot-wallet/impl 51
|
features/hot-wallet/impl 50
|
||||||
features/walletconnect/impl 47
|
features/walletconnect/impl 47
|
||||||
features/staking/impl 46
|
|
||||||
features/tokendetails/impl 45
|
features/tokendetails/impl 45
|
||||||
features/manage-tokens/impl 44
|
features/manage-tokens/impl 44
|
||||||
domain/wallets 37
|
domain/wallets 36
|
||||||
features/nft/impl 34
|
features/staking/impl 35
|
||||||
features/tester/impl 28
|
features/nft/impl 33
|
||||||
core/ui 26
|
features/tester/impl 27
|
||||||
domain/tokens 25
|
domain/tokens 23
|
||||||
common/ui 24
|
|
||||||
features/yield-supply/impl 21
|
features/yield-supply/impl 21
|
||||||
features/tangempay/details/impl 21
|
features/tangempay/details/impl 21
|
||||||
domain/models 21
|
domain/models 21
|
||||||
data/visa 21
|
data/visa 21
|
||||||
data/nft 19
|
data/nft 19
|
||||||
|
core/ui 19
|
||||||
core/pagination 16
|
core/pagination 16
|
||||||
|
common/ui 15
|
||||||
domain/staking/models 13
|
domain/staking/models 13
|
||||||
data/wallets 13
|
data/wallets 13
|
||||||
features/token-recieve/impl 11
|
features/token-recieve/impl 11
|
||||||
features/qr-scanning/impl 11
|
features/qr-scanning/impl 11
|
||||||
data/onramp 11
|
data/onramp 11
|
||||||
core/datasource 11
|
|
||||||
core/analytics/models 11
|
core/analytics/models 11
|
||||||
data/markets 10
|
core/datasource 10
|
||||||
features/details/impl 9
|
features/details/impl 9
|
||||||
domain/account/status 9
|
domain/account/status 9
|
||||||
data/yield-supply 9
|
data/yield-supply 9
|
||||||
data/networks 9
|
data/networks 9
|
||||||
domain/visa/models 8
|
domain/visa/models 8
|
||||||
domain/transaction 8
|
domain/transaction 8
|
||||||
domain/staking 8
|
|
||||||
core/utils 8
|
core/utils 8
|
||||||
libs/tangem-sdk-api 7
|
libs/tangem-sdk-api 7
|
||||||
features/referral/impl 7
|
features/referral/impl 7
|
||||||
domain/tokens/models 7
|
domain/tokens/models 7
|
||||||
|
domain/staking 7
|
||||||
data/txhistory 7
|
data/txhistory 7
|
||||||
features/welcome/impl 6
|
|
||||||
domain/wallet-connect/models 6
|
domain/wallet-connect/models 6
|
||||||
domain/onramp 6
|
domain/onramp 6
|
||||||
domain/account 6
|
domain/account 6
|
||||||
libs/visa 5
|
libs/visa 5
|
||||||
|
features/welcome/impl 5
|
||||||
features/home/impl 5
|
features/home/impl 5
|
||||||
domain/markets 5
|
|
||||||
domain/core 5
|
domain/core 5
|
||||||
data/account 5
|
data/account 5
|
||||||
domain/onramp/models 4
|
domain/onramp/models 4
|
||||||
|
|
@ -97,7 +94,6 @@ domain/transaction/models 1
|
||||||
domain/quotes 1
|
domain/quotes 1
|
||||||
domain/promo 1
|
domain/promo 1
|
||||||
domain/onboarding 1
|
domain/onboarding 1
|
||||||
domain/markets/models 1
|
|
||||||
domain/feedback/models 1
|
domain/feedback/models 1
|
||||||
domain/express/models 1
|
domain/express/models 1
|
||||||
────────────────────────────────────────────────────────────────
|
────────────────────────────────────────────────────────────────
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
<?xml version="1.0" ?>
|
|
||||||
<SmellBaseline>
|
|
||||||
<ManuallySuppressedIssues/>
|
|
||||||
<CurrentIssues>
|
|
||||||
<ID>BooleanPropertyNaming:FilterAvailableNetworksForWalletUseCase.kt$FilterAvailableNetworksForWalletUseCase$private val useNewRepository: Boolean</ID>
|
|
||||||
<ID>BooleanPropertyNaming:GetStakingNotificationMaxApyUseCase.kt$GetStakingNotificationMaxApyUseCase$val showStakingNotification = if (!hideClicked && walletFirstUsageDate != 0L) { currentDate - walletFirstUsageDate > TWO_WEEKS_IN_MILLIS } else { false }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:FilterAvailableNetworksForWalletUseCase.kt$FilterAvailableNetworksForWalletUseCase${ val blockchain = Blockchain.fromNetworkId(it.networkId) supportedBlockchains.contains(blockchain) }</ID>
|
|
||||||
<ID>SuspendFunWithFlowReturnType:GetStakingNotificationMaxApyUseCase.kt$GetStakingNotificationMaxApyUseCase$suspend</ID>
|
|
||||||
<ID>SuspendFunWithFlowReturnType:MarketsTokenRepository.kt$MarketsTokenRepository$suspend</ID>
|
|
||||||
</CurrentIssues>
|
|
||||||
</SmellBaseline>
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
<?xml version="1.0" ?>
|
|
||||||
<SmellBaseline>
|
|
||||||
<ManuallySuppressedIssues/>
|
|
||||||
<CurrentIssues>
|
|
||||||
<ID>BooleanPropertyNaming:TokenMarketInfo.kt$TokenMarketInfo.Network$val exchangeable: Boolean</ID>
|
|
||||||
</CurrentIssues>
|
|
||||||
</SmellBaseline>
|
|
||||||
|
|
@ -23,7 +23,7 @@ data class TokenMarketInfo(
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Network(
|
data class Network(
|
||||||
val networkId: String,
|
val networkId: String,
|
||||||
val exchangeable: Boolean,
|
val isExchangeable: Boolean,
|
||||||
val contractAddress: String?,
|
val contractAddress: String?,
|
||||||
val decimalCount: Int?,
|
val decimalCount: Int?,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||||
class FilterAvailableNetworksForWalletUseCase(
|
class FilterAvailableNetworksForWalletUseCase(
|
||||||
private val userWalletsListManager: UserWalletsListManager,
|
private val userWalletsListManager: UserWalletsListManager,
|
||||||
private val userWalletsListRepository: UserWalletsListRepository,
|
private val userWalletsListRepository: UserWalletsListRepository,
|
||||||
private val useNewRepository: Boolean,
|
private val shouldUseNewRepository: Boolean,
|
||||||
private val excludedBlockchains: ExcludedBlockchains,
|
private val excludedBlockchains: ExcludedBlockchains,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
@ -32,13 +32,13 @@ class FilterAvailableNetworksForWalletUseCase(
|
||||||
excludedBlockchains = excludedBlockchains,
|
excludedBlockchains = excludedBlockchains,
|
||||||
)
|
)
|
||||||
|
|
||||||
return networks.filter {
|
return networks.filter { network ->
|
||||||
val blockchain = Blockchain.fromNetworkId(it.networkId)
|
val blockchain = Blockchain.fromNetworkId(network.networkId)
|
||||||
supportedBlockchains.contains(blockchain)
|
supportedBlockchains.contains(blockchain)
|
||||||
}.toSet()
|
}.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getWallets() = if (useNewRepository) {
|
private fun getWallets() = if (shouldUseNewRepository) {
|
||||||
userWalletsListRepository.requireUserWalletsSync()
|
userWalletsListRepository.requireUserWalletsSync()
|
||||||
} else {
|
} else {
|
||||||
userWalletsListManager.userWalletsSync
|
userWalletsListManager.userWalletsSync
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
<ID>MultilineLambdaItParameter:FetchStakingYieldBalanceUseCase.kt$FetchStakingYieldBalanceUseCase${ when (it) { is StakingIdFactory.Error.UnableToGetAddress -> raise(StakingError.DomainError("$it")) StakingIdFactory.Error.UnsupportedCurrency -> Unit.right() } return@either }</ID>
|
<ID>MultilineLambdaItParameter:FetchStakingYieldBalanceUseCase.kt$FetchStakingYieldBalanceUseCase${ when (it) { is StakingIdFactory.Error.UnableToGetAddress -> raise(StakingError.DomainError("$it")) StakingIdFactory.Error.UnsupportedCurrency -> Unit.right() } return@either }</ID>
|
||||||
<ID>MultilineLambdaItParameter:InvalidatePendingTransactionsUseCase.kt$InvalidatePendingTransactionsUseCase${ !it.isPending && action.amount < it.amount && it.type == BalanceType.STAKED && it.validatorAddress == action.validatorAddress }</ID>
|
<ID>MultilineLambdaItParameter:InvalidatePendingTransactionsUseCase.kt$InvalidatePendingTransactionsUseCase${ !it.isPending && action.amount < it.amount && it.type == BalanceType.STAKED && it.validatorAddress == action.validatorAddress }</ID>
|
||||||
<ID>NamedArguments:GetConstructedStakingTransactionUseCase.kt$GetConstructedStakingTransactionUseCase$constructTransaction(networkId, fee, amount, transactionId)</ID>
|
<ID>NamedArguments:GetConstructedStakingTransactionUseCase.kt$GetConstructedStakingTransactionUseCase$constructTransaction(networkId, fee, amount, transactionId)</ID>
|
||||||
<ID>NullableToStringCall:StakingApyFlowUseCase.kt$StakingApyFlowUseCase$${yield.token.coinGeckoId}</ID>
|
|
||||||
<ID>UnnecessaryAbstractClass:MultiStakingBalanceSupplier.kt$MultiStakingBalanceSupplier$MultiStakingBalanceSupplier</ID>
|
<ID>UnnecessaryAbstractClass:MultiStakingBalanceSupplier.kt$MultiStakingBalanceSupplier$MultiStakingBalanceSupplier</ID>
|
||||||
<ID>UnnecessaryAbstractClass:SingleStakingBalanceSupplier.kt$SingleStakingBalanceSupplier$SingleStakingBalanceSupplier</ID>
|
<ID>UnnecessaryAbstractClass:SingleStakingBalanceSupplier.kt$SingleStakingBalanceSupplier$SingleStakingBalanceSupplier</ID>
|
||||||
<ID>UseEmptyCounterpart:StakingAnalyticsEvent.kt$StakingAnalyticsEvent$mapOf()</ID>
|
<ID>UseEmptyCounterpart:StakingAnalyticsEvent.kt$StakingAnalyticsEvent$mapOf()</ID>
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,12 @@
|
||||||
<ID>MultilineLambdaItParameter:FetchCurrencyStatusUseCase.kt$FetchCurrencyStatusUseCase${ when (it) { is StakingIdFactory.Error.UnableToGetAddress -> raise(IllegalStateException("$it")) StakingIdFactory.Error.UnsupportedCurrency -> Unit.right() } return@either }</ID>
|
<ID>MultilineLambdaItParameter:FetchCurrencyStatusUseCase.kt$FetchCurrencyStatusUseCase${ when (it) { is StakingIdFactory.Error.UnableToGetAddress -> raise(IllegalStateException("$it")) StakingIdFactory.Error.UnsupportedCurrency -> Unit.right() } return@either }</ID>
|
||||||
<ID>MultilineLambdaItParameter:GetBalanceNotEnoughForFeeWarningUseCase.kt$GetBalanceNotEnoughForFeeWarningUseCase${ it is CryptoCurrency.Token && it.contractAddress.equals(feePaidToken.contractAddress, ignoreCase = true) && it.network.derivationPath == tokenStatus.currency.network.derivationPath }</ID>
|
<ID>MultilineLambdaItParameter:GetBalanceNotEnoughForFeeWarningUseCase.kt$GetBalanceNotEnoughForFeeWarningUseCase${ it is CryptoCurrency.Token && it.contractAddress.equals(feePaidToken.contractAddress, ignoreCase = true) && it.network.derivationPath == tokenStatus.currency.network.derivationPath }</ID>
|
||||||
<ID>MultilineLambdaItParameter:GetCryptoCurrencyActionsUseCase.kt$GetCryptoCurrencyActionsUseCase${ TokenActionsState( walletId = userWallet.walletId, cryptoCurrencyStatus = cryptoCurrencyStatus, states = it.toList(), ) }</ID>
|
<ID>MultilineLambdaItParameter:GetCryptoCurrencyActionsUseCase.kt$GetCryptoCurrencyActionsUseCase${ TokenActionsState( walletId = userWallet.walletId, cryptoCurrencyStatus = cryptoCurrencyStatus, states = it.toList(), ) }</ID>
|
||||||
<ID>MultilineLambdaItParameter:GetCryptoCurrencyActionsUseCase.kt$GetCryptoCurrencyActionsUseCase${ outdatedDataActionsFactory.create( userWallet = userWallet, cryptoCurrencyStatus = cryptoCurrencyStatus, stakingAvailability = it, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:GetCurrencyWarningsUseCase.kt$GetCurrencyWarningsUseCase${ if (isNeedToCreateAccountWithoutReserve(networkId = currencyStatus.currency.network.rawId)) { CryptoCurrencyWarning.TopUpWithoutReserve } else { CryptoCurrencyWarning.SomeNetworksNoAccount( amountToCreateAccount = it.amountToCreateAccount, amountCurrency = currencyStatus.currency, ) } }</ID>
|
<ID>MultilineLambdaItParameter:GetCurrencyWarningsUseCase.kt$GetCurrencyWarningsUseCase${ if (isNeedToCreateAccountWithoutReserve(networkId = currencyStatus.currency.network.rawId)) { CryptoCurrencyWarning.TopUpWithoutReserve } else { CryptoCurrencyWarning.SomeNetworksNoAccount( amountToCreateAccount = it.amountToCreateAccount, amountCurrency = currencyStatus.currency, ) } }</ID>
|
||||||
<ID>MultilineLambdaItParameter:GetCurrencyWarningsUseCase.kt$GetCurrencyWarningsUseCase${ it is CryptoCurrency.Token && it.contractAddress.equals(feePaidToken.contractAddress, ignoreCase = true) && it.network.derivationPath == tokenStatus.currency.network.derivationPath }</ID>
|
<ID>MultilineLambdaItParameter:GetCurrencyWarningsUseCase.kt$GetCurrencyWarningsUseCase${ it is CryptoCurrency.Token && it.contractAddress.equals(feePaidToken.contractAddress, ignoreCase = true) && it.network.derivationPath == tokenStatus.currency.network.derivationPath }</ID>
|
||||||
<ID>MultilineLambdaItParameter:GetWalletTotalBalanceUseCase.kt$GetWalletTotalBalanceUseCase${ Timber.e("failed to load balances with error: $it") TotalFiatBalance.Failed }</ID>
|
<ID>MultilineLambdaItParameter:GetWalletTotalBalanceUseCase.kt$GetWalletTotalBalanceUseCase${ Timber.e("failed to load balances with error: $it") TotalFiatBalance.Failed }</ID>
|
||||||
<ID>MultilineLambdaItParameter:PriceChangeCalculator.kt$PriceChangeCalculator${ val weight = it.value.fiatAmount.orZero().divide(balance, 2, RoundingMode.HALF_UP) val priceChange = it.value.priceChange.orZero() weight * priceChange }</ID>
|
<ID>MultilineLambdaItParameter:PriceChangeCalculator.kt$PriceChangeCalculator${ val weight = it.value.fiatAmount.orZero().divide(balance, 2, RoundingMode.HALF_UP) val priceChange = it.value.priceChange.orZero() weight * priceChange }</ID>
|
||||||
<ID>MultilineLambdaItParameter:WalletBalanceFetcher.kt$WalletBalanceFetcher${ val stakingId = stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = it) if (stakingId.isLeft { it is StakingIdFactory.Error.UnableToGetAddress }) { Timber.e("Unable to get staking ID for user wallet $userWalletId and currency ${it.id}") } stakingId }</ID>
|
<ID>MultilineLambdaItParameter:WalletBalanceFetcher.kt$WalletBalanceFetcher${ val stakingId = stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = it) if (stakingId.isLeft { it is StakingIdFactory.Error.UnableToGetAddress }) { Timber.e("Unable to get staking ID for user wallet $userWalletId and currency ${it.id}") } stakingId }</ID>
|
||||||
<ID>NamedArguments:ApplyTokenListSortingUseCase.kt$ApplyTokenListSortingUseCase$saveTokens(userWalletId, currencies, isGrouped, isSortedByBalance)</ID>
|
<ID>NamedArguments:ApplyTokenListSortingUseCase.kt$ApplyTokenListSortingUseCase$saveTokens(userWalletId, currencies, isGrouped, isSortedByBalance)</ID>
|
||||||
<ID>NamedArguments:GetCurrencyWarningsUseCase.kt$GetCurrencyWarningsUseCase$combine( getCoinRelatedWarnings( userWalletId = userWalletId, networkId = currency.network.id, currencyId = currency.id, derivationPath = derivationPath, isSingleWalletWithTokens = isSingleWalletWithTokens, ), flowOf(currencyChecksRepository.getRentInfoWarning(userWalletId, currencyStatus)), flowOf(currencyChecksRepository.getExistentialDeposit(userWalletId, currency.network)), flowOf(currencyChecksRepository.getFeeResourceAmount(userWalletId, currency.network)), ) { coinRelatedWarnings, maybeRentWarning, maybeEdWarning, maybeFeeResource -> setOfNotNull( maybeRentWarning, maybeEdWarning?.let { getExistentialDepositWarning(currency, it) }, maybeFeeResource?.let { getFeeResourceWarning(it) }, * coinRelatedWarnings.toTypedArray(), getNetworkUnavailableWarning(currencyStatus), getNetworkNoAccountWarning(currencyStatus), getBeaconChainShutdownWarning(rawId = currency.network.id.rawId), getAssetRequirementsWarning(userWalletId = userWalletId, currency = currency), getMigrationFromMaticToPolWarning(currency), ) }</ID>
|
|
||||||
<ID>NoNameShadowing:WalletBalanceFetcher.kt$WalletBalanceFetcher${ it is StakingIdFactory.Error.UnableToGetAddress }</ID>
|
<ID>NoNameShadowing:WalletBalanceFetcher.kt$WalletBalanceFetcher${ it is StakingIdFactory.Error.UnableToGetAddress }</ID>
|
||||||
<ID>NullableBooleanCheck:GetCurrencyCheckUseCase.kt$GetCurrencyCheckUseCase$recipientAddress?.let { currencyChecksRepository.checkIfAccountFunded( userWalletId, network, recipientAddress, ) } ?: false</ID>
|
<ID>NullableBooleanCheck:GetCurrencyCheckUseCase.kt$GetCurrencyCheckUseCase$recipientAddress?.let { currencyChecksRepository.checkIfAccountFunded( userWalletId, network, recipientAddress, ) } ?: false</ID>
|
||||||
<ID>SuspendFunWithFlowReturnType:BaseCurrencyStatusOperations.kt$BaseCurrencyStatusOperations$suspend</ID>
|
<ID>SuspendFunWithFlowReturnType:BaseCurrencyStatusOperations.kt$BaseCurrencyStatusOperations$suspend</ID>
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@
|
||||||
<ID>MultilineLambdaItParameter:UpdateWalletUseCase.kt$UpdateWalletUseCase${ when (it) { is SaveWalletError.DataError -> DataError( IllegalStateException("Failed to update wallet: ${it.messageId}"), ) is SaveWalletError.WalletAlreadySaved -> UpdateWalletError.NameAlreadyExists } }</ID>
|
<ID>MultilineLambdaItParameter:UpdateWalletUseCase.kt$UpdateWalletUseCase${ when (it) { is SaveWalletError.DataError -> DataError( IllegalStateException("Failed to update wallet: ${it.messageId}"), ) is SaveWalletError.WalletAlreadySaved -> UpdateWalletError.NameAlreadyExists } }</ID>
|
||||||
<ID>NestedScopeFunctions:ColdUserWalletBuilder.kt$ColdUserWalletBuilder$let { UserWallet.Cold( walletId = it, name = generateWalletNameUseCase( card = card, productType = productType, isStartToCoin = cardTypesResolver.isStart2Coin(), ), cardsInWallet = backupCardsIds.plus(card.cardId), scanResponse = this, isMultiCurrency = cardTypesResolver.isMultiwalletAllowed(), hasBackupError = hasBackupError, ) }</ID>
|
<ID>NestedScopeFunctions:ColdUserWalletBuilder.kt$ColdUserWalletBuilder$let { UserWallet.Cold( walletId = it, name = generateWalletNameUseCase( card = card, productType = productType, isStartToCoin = cardTypesResolver.isStart2Coin(), ), cardsInWallet = backupCardsIds.plus(card.cardId), scanResponse = this, isMultiCurrency = cardTypesResolver.isMultiwalletAllowed(), hasBackupError = hasBackupError, ) }</ID>
|
||||||
<ID>NoNameShadowing:SaveWalletUseCase.kt$SaveWalletUseCase$userWallet</ID>
|
<ID>NoNameShadowing:SaveWalletUseCase.kt$SaveWalletUseCase$userWallet</ID>
|
||||||
<ID>NullableToStringCall:GenerateBuyTangemCardLinkUseCase.kt$GenerateBuyTangemCardLinkUseCase$$id</ID>
|
|
||||||
<ID>NullableToStringCall:UpdateWalletUseCase.kt$UpdateWalletUseCase$${it.messageId}</ID>
|
<ID>NullableToStringCall:UpdateWalletUseCase.kt$UpdateWalletUseCase$${it.messageId}</ID>
|
||||||
<ID>ObjectExtendsThrowable:UserWalletsListError.kt$UserWalletsListError$AllKeysInvalidated : UserWalletsListError</ID>
|
<ID>ObjectExtendsThrowable:UserWalletsListError.kt$UserWalletsListError$AllKeysInvalidated : UserWalletsListError</ID>
|
||||||
<ID>ObjectExtendsThrowable:UserWalletsListError.kt$UserWalletsListError$BiometricsAuthenticationDisabled : UserWalletsListError</ID>
|
<ID>ObjectExtendsThrowable:UserWalletsListError.kt$UserWalletsListError$BiometricsAuthenticationDisabled : UserWalletsListError</ID>
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@
|
||||||
<ID>BooleanPropertyNaming:WalletHardwareBackupUM.kt$WalletHardwareBackupUM$val showPurchaseBlock: Boolean = false</ID>
|
<ID>BooleanPropertyNaming:WalletHardwareBackupUM.kt$WalletHardwareBackupUM$val showPurchaseBlock: Boolean = false</ID>
|
||||||
<ID>MultilineLambdaItParameter:AddExistingWalletImportModel.kt$AddExistingWalletImportModel${ Timber.e(it) setImportProgress(false) }</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: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>
|
|
||||||
<ID>MultilineLambdaItParameter:ForgetWalletModel.kt$ForgetWalletModel${ Timber.e("Unable to delete wallet: $it") uiMessageSender.send( message = SnackbarMessage(resourceReference(R.string.common_unknown_error)), ) return@launch }</ID>
|
<ID>MultilineLambdaItParameter:ForgetWalletModel.kt$ForgetWalletModel${ Timber.e("Unable to delete wallet: $it") uiMessageSender.send( message = SnackbarMessage(resourceReference(R.string.common_unknown_error)), ) return@launch }</ID>
|
||||||
<ID>MultilineLambdaItParameter:ForgetWalletModel.kt$ForgetWalletModel${ val newValue = !it.firstCheckboxChecked it.copy( firstCheckboxChecked = newValue, isForgetButtonEnabled = newValue && it.secondCheckboxChecked, ) }</ID>
|
<ID>MultilineLambdaItParameter:ForgetWalletModel.kt$ForgetWalletModel${ val newValue = !it.firstCheckboxChecked it.copy( firstCheckboxChecked = newValue, isForgetButtonEnabled = newValue && it.secondCheckboxChecked, ) }</ID>
|
||||||
<ID>MultilineLambdaItParameter:ForgetWalletModel.kt$ForgetWalletModel${ val newValue = !it.secondCheckboxChecked it.copy( secondCheckboxChecked = newValue, isForgetButtonEnabled = it.firstCheckboxChecked && newValue, ) }</ID>
|
<ID>MultilineLambdaItParameter:ForgetWalletModel.kt$ForgetWalletModel${ val newValue = !it.secondCheckboxChecked it.copy( secondCheckboxChecked = newValue, isForgetButtonEnabled = it.firstCheckboxChecked && newValue, ) }</ID>
|
||||||
|
|
|
||||||
|
|
@ -1,153 +0,0 @@
|
||||||
<?xml version="1.0" ?>
|
|
||||||
<SmellBaseline>
|
|
||||||
<ManuallySuppressedIssues/>
|
|
||||||
<CurrentIssues>
|
|
||||||
<ID>BooleanPropertyNaming:AddToPortfolioBSContentUM.kt$AddToPortfolioBSContentUM$val continueButtonEnabled: Boolean</ID>
|
|
||||||
<ID>BooleanPropertyNaming:AvailableToAddData.kt$AvailableToAddData$val availableToAdd: Boolean get() = availableToAddWallets.isNotEmpty()</ID>
|
|
||||||
<ID>BooleanPropertyNaming:AvailableToAddData.kt$SelectedNetwork$val availableMoreNetwork: Boolean</ID>
|
|
||||||
<ID>BooleanPropertyNaming:AvailableToAddData.kt$SelectedPortfolio$val availableMorePortfolio: Boolean</ID>
|
|
||||||
<ID>BooleanPropertyNaming:DefaultAddToPortfolioComponent.kt$DefaultAddToPortfolioComponent$val scrollableContent = when (stack.active.configuration) { AddToPortfolioRoutes.PortfolioSelector -> false AddToPortfolioRoutes.AddToken, AddToPortfolioRoutes.Empty, is AddToPortfolioRoutes.NetworkSelector, AddToPortfolioRoutes.TokenActions, -> true }</ID>
|
|
||||||
<ID>BooleanPropertyNaming:MarketsList.kt$val mainScrolled by remember { derivedStateOf { mainLazyListState.firstVisibleItemScrollOffset > 0 } }</ID>
|
|
||||||
<ID>BooleanPropertyNaming:MarketsList.kt$val searchScrolledState by remember { derivedStateOf { searchLazyListState.firstVisibleItemScrollOffset > 0 } }</ID>
|
|
||||||
<ID>BooleanPropertyNaming:MarketsListAnalyticsEvent.kt$MarketsListAnalyticsEvent.TokenSearched$val tokenFound: Boolean</ID>
|
|
||||||
<ID>BooleanPropertyNaming:MarketsListUM.kt$ListUM.Content$val showUnder100kTokensNotification: Boolean</ID>
|
|
||||||
<ID>BooleanPropertyNaming:MarketsListUM.kt$ListUM.Content$val showUnder100kTokensNotificationWasHidden: Boolean</ID>
|
|
||||||
<ID>BooleanPropertyNaming:MarketsListUMStateManager.kt$MarketsListUMStateManager$private var sortByBottomSheetIsShown get() = state.value.sortByBottomSheet.isShown set(value) = state.update { it.copy(sortByBottomSheet = it.sortByBottomSheet.copy(isShown = value)) }</ID>
|
|
||||||
<ID>BooleanPropertyNaming:MarketsTokenDetailsContent.kt$val showPriceSubtitle by remember { derivedStateOf { lazyListState.firstVisibleItemIndex > 1 } }</ID>
|
|
||||||
<ID>BooleanPropertyNaming:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel$val allWalletsIsHot = getUserWalletsUseCase.invokeSync().all { it is UserWallet.Hot }</ID>
|
|
||||||
<ID>BooleanPropertyNaming:MarketsTokenDetailsUM.kt$MarketsTokenDetailsUM$val markerSet: Boolean</ID>
|
|
||||||
<ID>BooleanPropertyNaming:MetricsBlock.kt$var expanded by remember { mutableStateOf(false) }</ID>
|
|
||||||
<ID>BooleanPropertyNaming:PortfolioBSVisibilityModel.kt$PortfolioBSVisibilityModel$val addToPortfolioBSVisibility: Boolean = false</ID>
|
|
||||||
<ID>BooleanPropertyNaming:PortfolioBSVisibilityModel.kt$PortfolioBSVisibilityModel$val walletSelectorBSVisibility: Boolean = false</ID>
|
|
||||||
<ID>BooleanPropertyNaming:PortfolioItem.kt$var quickActionsShown by remember { mutableStateOf(value = false) }</ID>
|
|
||||||
<ID>BooleanPropertyNaming:PortfolioUIData.kt$PortfolioUIData$val needColdWalletInteraction: Boolean</ID>
|
|
||||||
<ID>BooleanPropertyNaming:QuickActionUM.kt$QuickActionUM$val longClickAvailable: Boolean = false</ID>
|
|
||||||
<ID>BooleanPropertyNaming:QuickActionUM.kt$QuickActionUM.Exchange$val showBadge: Boolean</ID>
|
|
||||||
<ID>BooleanPropertyNaming:TokenActionsHandler.kt$TokenActionsHandler$val demoCard = isDemoCardUseCase.invoke(userWallet.cardId)</ID>
|
|
||||||
<ID>BooleanPropertyNaming:TokenActionsHandler.kt$TokenActionsHandler$val needShowDemoWarning = demoCard && disabledActionsInDemoMode.contains(action)</ID>
|
|
||||||
<ID>CanBeNonNullable:TokenMarketDetailsBody.kt$portfolioBlock: @Composable ((Modifier) -> Unit)?</ID>
|
|
||||||
<ID>CastNullableToNonNullableType:PortfolioItem.kt$PortfolioTokenUMProvider$as</ID>
|
|
||||||
<ID>CastNullableToNonNullableType:PortfolioItem.kt$PortfolioTokenUMProvider.Companion$as</ID>
|
|
||||||
<ID>MaxChainedCallsOnSameLine:AddToPortfolioModel.kt$AddToPortfolioModel$selectedPortfolio.first().account.account.account.accountId</ID>
|
|
||||||
<ID>MaxChainedCallsOnSameLine:MarketsListBatchFlowManager.kt$MarketsListBatchFlowManager$batchFlow.state.value.data.map { it.data }.flatten().find { it.id == id }</ID>
|
|
||||||
<ID>MaxChainedCallsOnSameLine:MarketsListBatchFlowManager.kt$MarketsListBatchFlowManager$batchFlow.state.value.data.map { it.key }.toSet()</ID>
|
|
||||||
<ID>MaxChainedCallsOnSameLine:MarketsListModel.kt$MarketsListModel$marketsListUMStateManager.state.map { it.selectedSortBy }.distinctUntilChanged().drop(1).collectLatest { mainMarketsListManager.reload() }</ID>
|
|
||||||
<ID>MaxChainedCallsOnSameLine:MarketsPortfolioModel.kt$MarketsPortfolioModel$handledAction.cryptoCurrencyData.status.currency.network.name</ID>
|
|
||||||
<ID>MaxChainedCallsOnSameLine:TokenActionsModel.kt$TokenActionsModel$handledAction.cryptoCurrencyData.status.currency.network.name</ID>
|
|
||||||
<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${ 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>
|
|
||||||
<ID>MultilineLambdaItParameter:AvailableToAddDataConverter.kt$AvailableToAddDataConverter${ val (walletId, _) = it val availableToAddWallet = getAvailableToAddWallet(it) walletId to availableToAddWallet }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:ChooseNetworkModel.kt$ChooseNetworkModel${ Timber.e( it, """ Failed to check currency unsupported state |- User wallet ID: $userWalletId |- Network ID: $rawNetworkId |- Is main network: $isMainNetwork """.trimIndent(), ) val message = SnackbarMessage( message = it.localizedMessage?.let(::stringReference) ?: resourceReference(R.string.common_error), ) messageSender.send(message) null }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:DefaultMarketsTokenDetailsComponent.kt$DefaultMarketsTokenDetailsComponent${ when (it) { is TokenNetworksState.NetworksAvailable -> portfolioComponent?.setTokenNetworks(it.networks) TokenNetworksState.NoNetworksAvailable -> portfolioComponent?.setNoNetworksAvailable() else -> {} } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:DefaultMarketsTokenListComponent.kt$DefaultMarketsTokenListComponent${ MarketsListWithBack( modifier = Modifier .statusBarsPadding() .imePadding() .padding(it), state = state, bottomSheetState = BottomSheetState.EXPANDED, onBackClick = router::pop, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:EntryBottomSheetContent.kt${ when (it.configuration) { is MarketsEntryChildFactory.Child.TokenDetails -> { (it.instance as MarketsTokenDetailsComponent).BottomSheetContent( bottomSheetState = bottomSheetState, onHeaderSizeChange = onHeaderSizeChange, modifier = modifier, ) } is MarketsEntryChildFactory.Child.TokenList -> { (it.instance as MarketsTokenListComponent).BottomSheetContent( bottomSheetState = bottomSheetState, onHeaderSizeChange = onHeaderSizeChange, modifier = modifier, ) } } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:InsightsBlock.kt${ InfoPoint( modifier = Modifier.align(Alignment.CenterStart), infoPointUM = it, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:InsightsBlock.kt${ currentInterval = it state.onIntervalChanged(it) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:LinksBlock.kt${ Chip( text = stringReference(it.title), iconResId = it.iconRes, onClick = { onLinkClick(it) }, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketTokenDetailsChart.kt${ when (it) { MarketChartLook.Type.Growing -> growingColor MarketChartLook.Type.Falling -> fallingColor MarketChartLook.Type.Neutral -> neutralColor } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsList.kt${ if (it.size.height > 0) { with(density) { onHeaderSizeChange(it.size.height.toDp()) } } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsListBatchFlowManager.kt$MarketsListBatchFlowManager${ Batch( key = it.key, data = converter.convertList(it.data), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsListBatchFlowManager.kt$MarketsListBatchFlowManager${ currentSearchText().isNullOrEmpty().not() && it.status is PaginationStatus.EndOfPagination && it.data.isEmpty() }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsListBatchFlowManager.kt$MarketsListBatchFlowManager${ val first = it.data.firstOrNull() ?: return@filter false val chartByInterval = when (interval) { TrendInterval.H24 -> first.tokenCharts.h24 TrendInterval.D7 -> first.tokenCharts.week TrendInterval.M1 -> first.tokenCharts.month } chartByInterval != null }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsListBatchFlowManager.kt$MarketsListBatchFlowManager${ when (val status = it.status) { is PaginationStatus.Paginating -> { if (status.lastResult is BatchFetchResult.Success) { it.data.lastOrNull()?.key } else { null } } is PaginationStatus.EndOfPagination -> { it.data.lastOrNull()?.key } else -> null } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsListBatchFlowManager.kt$MarketsListBatchFlowManager${ when (val status = it.status) { is PaginationStatus.Paginating -> { if (status.lastResult is BatchFetchResult.Success) { it.data.size == 1 } else { null } } is PaginationStatus.EndOfPagination -> { it.data.size == 1 } else -> null } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsListItem.kt${ if (Random.nextBoolean()) { it.first.inc() to PriceChangeType.UP } else { it.first.dec() to PriceChangeType.DOWN } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsListLazyColumn.kt${ (it.key as? String)?.split(TOKEN_LAZY_LIST_ID_SEPARATOR)?.first() ?.let { rawId -> CryptoCurrency.RawID(rawId) } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsListModel.kt$MarketsListModel${ if (it) { analyticsEventHandler.send(MarketsListAnalyticsEvent.TokenSearched(tokenFound = false)) } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsListModel.kt$MarketsListModel${ if (it.isNotEmpty()) { activeListManager.getBatchKeysByItemIds(visibleItemIds.value) } else { null } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsListModel.kt$MarketsListModel${ if (it.list !is ListUM.Content) { visibleItemIds.value = emptyList() } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsListModel.kt$MarketsListModel${ mainMarketsListManager.loadCharts(setOf(it), marketsListUMStateManager.selectedInterval) modelScope.loadQuotesWithTimer(timeMillis = UPDATE_QUOTES_TIMER_MILLIS) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsListModel.kt$MarketsListModel${ searchMarketsListManager.loadCharts(setOf(it), marketsListUMStateManager.selectedInterval) modelScope.loadQuotesWithTimer(timeMillis = UPDATE_QUOTES_TIMER_MILLIS) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsListUMStateManager.kt$MarketsListUMStateManager${ it.copy( list = if (it.list is ListUM.Content) { it.list.copy( triggerScrollReset = consumedEvent(), ) } else { it.list }, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsListUMStateManager.kt$MarketsListUMStateManager${ it.copy( price = it.price.copy( changeType = if (visibleItemIds.contains(it.id)) { it.price.changeType } else { null }, ), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsListUMStateManager.kt$MarketsListUMStateManager${ it.copy( searchBar = it.searchBar.copy( query = value, isActive = value.isNotEmpty(), ), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsListUMStateManager.kt$MarketsListUMStateManager${ it.copy( selectedInterval = value, list = if (it.list is ListUM.Content && it.selectedSortBy != SortByTypeUM.Rating && it.selectedInterval != value ) { it.list.copy(triggerScrollReset = triggeredEvent(Unit) { consumeTriggerResetScrollEvent() }) } else { it.list }, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsListUMStateManager.kt$MarketsListUMStateManager${ it.copy( selectedSortBy = sortByTypeUM, sortByBottomSheet = it.sortByBottomSheet.copy( isShown = false, content = (it.sortByBottomSheet.content as SortByBottomSheetContentUM).copy( selectedOption = sortByTypeUM, ), ), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsListUMStateManager.kt$MarketsListUMStateManager${ it.copy( selectedSortBy = value, sortByBottomSheet = it.sortByBottomSheet.copy( content = (it.sortByBottomSheet.content as SortByBottomSheetContentUM).copy( selectedOption = value, ), ), list = if (it.list is ListUM.Content && it.selectedSortBy != value) { it.list.copy(triggerScrollReset = triggeredEvent(Unit) { consumeTriggerResetScrollEvent() }) } else { it.list }, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsListUMStateManager.kt$MarketsListUMStateManager${ when { isInErrorState -> { it.copy( list = ListUM.LoadingError(onRetryClicked = onRetryButtonClicked), ) } isSearchNotFound -> { it.copy(list = ListUM.SearchNothingFound) } uiItems.isEmpty() -> { it.copy(list = ListUM.Loading) } else -> { it.updateItems( newItems = uiItems, stakingNotificationMaxApy = stakingNotificationMaxApy, ) } } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsPortfolioModel.kt$MarketsPortfolioModel${ Timber.e( it, """ Failed to check currency unsupported state |- User wallet ID: $userWalletId |- Network ID: $rawNetworkId |- Is main network: $isMainNetwork """.trimIndent(), ) val message = SnackbarMessage( message = it.localizedMessage ?.let(::stringReference) ?: resourceReference(R.string.common_error), ) messageSender.send(message) null }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsPortfolioModel.kt$MarketsPortfolioModel${ onWalletSelect(it) // === Analytics === analyticsEventHandler.send( analyticsEventBuilder.addToPortfolioWalletChanged(), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsPortfolioModel.kt$MarketsPortfolioModel${ when (it) { is NewAddToPortfolioManager.State.AvailableToAdd -> AddButtonState.Available NewAddToPortfolioManager.State.Init -> AddButtonState.Loading NewAddToPortfolioManager.State.NothingToAdd -> AddButtonState.Unavailable } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsTokenDetailsContent.kt${ if (it.size.height > 0) { with(density) { onHeaderSizeChange(it.size.height.toDp()) } } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsTokenDetailsContent.kt${ val nextColor = when (it) { PriceChangeType.UP, -> growColor PriceChangeType.DOWN -> fallColor PriceChangeType.NEUTRAL -> return@EventEffect } color.animateTo(nextColor, snap()) color.animateTo(generalColor, tween(durationMillis = 500)) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel${ BlockchainUtils.isSupportedNetworkId( blockchainId = it.networkId, excludedBlockchains = excludedBlockchains, hotExcludedBlockchains = hotWalletExcludedBlockchains, hasOnlyHotWallets = allWalletsIsHot, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel${ ExchangesBottomSheetContent.Content( exchangeItems = ExchangeItemStateConverter.convertList(it).toImmutableList(), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel${ MarketsDateTimeFormatters.formatDateByIntervalWithMarker( interval = currentState.selectedInterval, markerTimestamp = it, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel${ analyticsEventHandler.send( analyticsEventBuilder.intervalChanged( intervalType = MarketDetailsAnalyticsEvent.IntervalType.Insights, interval = it, ), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel${ analyticsEventHandler.send( analyticsEventBuilder.intervalChanged( intervalType = MarketDetailsAnalyticsEvent.IntervalType.PricePerformance, interval = it, ), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel${ getChangePercentBetween( previousPrice = it, currentPrice = currentQuotes.value.currentPrice, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel${ if (it.chartState.status == MarketsTokenDetailsUM.ChartState.Status.DATA) { it.copy( body = MarketsTokenDetailsUM.Body.Error( onLoadRetryClick = ::onLoadRetryClicked, ), ) } else { it.copy( body = MarketsTokenDetailsUM.Body.Nothing, ) } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel${ it.copy( body = MarketsTokenDetailsUM.Body.Loading, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel${ it.copy( chartState = it.chartState.copy( status = MarketsTokenDetailsUM.ChartState.Status.DATA, ), body = if (it.body is MarketsTokenDetailsUM.Body.Nothing) { MarketsTokenDetailsUM.Body.Error(onLoadRetryClick = ::onLoadRetryClicked) } else { it.body }, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter: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>MultilineLambdaItParameter:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel${ it.copy( chartState = it.chartState.copy( status = MarketsTokenDetailsUM.ChartState.Status.LOADING, ), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel${ it.copy( priceChangePercentText = priceChangePercent, selectedInterval = interval, priceChangeType = quotes.getPercentByInterval(interval)?.percentChangeType() ?: PriceChangeType.NEUTRAL, dateTimeText = getDefaultDateTimeString(interval), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel${ it.copy( priceText = newInfo.quotes.currentPrice.format { fiat( fiatCurrencySymbol = currentAppCurrency.value.symbol, fiatCurrencyCode = currentAppCurrency.value.code, ).price() }, priceChangePercentText = newInfo.quotes.getFormattedPercentByInterval( interval = it.selectedInterval, ), priceChangeType = percent.percentChangeType(), body = MarketsTokenDetailsUM.Body.Content( description = descriptionConverter.convert(newInfo), infoBlocks = infoConverter.convert(newInfo), ), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel${ it.copy( selectedInterval = interval, chartState = it.chartState.copy( status = MarketsTokenDetailsUM.ChartState.Status.LOADING, ), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel${ it.copy( type = percent.percentChangeType().toChartType(), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel${ it.copy( xAxisFormatter = xAxisFormatter, type = state.value.priceChangeType.toChartType(), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel${ it.urlData?.fullUrl?.let { url -> urlOpener.openUrl(url) } // === Analytics === analyticsEventHandler.send(analyticsEventBuilder.securityScoreProviderClicked(it.name)) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel${ showBottomSheet(it) // === Analytics === analyticsEventHandler.send(analyticsEventBuilder.readMoreClicked()) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel${ showBottomSheet(it) // === Analytics === analyticsEventHandler.send(analyticsEventBuilder.securityScoreOpened()) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel${ val percentChangeType = params.token.tokenQuotes.h24Percent.percentChangeType() it.copy( type = percentChangeType.toChartType(), xAxisFormatter = MarketsDateTimeFormatters.getChartXFormatterByInterval(PriceChangeInterval.H24), yAxisFormatter = { value -> value.format { fiat( fiatCurrencyCode = currentAppCurrency.value.code, fiatCurrencySymbol = currentAppCurrency.value.symbol, ).price() } }, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:NewMarketsPortfolioDelegate.kt$NewMarketsPortfolioDelegate${ PortfoliosWithThisCurrency( currencyRawId = currencyRawId, portfolios = it.toList(), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:NewMarketsPortfolioDelegate.kt$NewMarketsPortfolioDelegate${ when (it) { AddButtonState.Loading -> MyPortfolioUM.Loading AddButtonState.Available -> MyPortfolioUM.AddFirstToken( onAddClick = onAddClick, addToPortfolioBSConfig = TangemBottomSheetConfig.Empty, ) AddButtonState.Unavailable -> MyPortfolioUM.Unavailable } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:PortfolioDataLoader.kt$PortfolioDataLoader${ PortfolioData.CryptoCurrencyData( userWallet = wallet, status = it, actions = emptyList(), ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:PortfolioDataLoader.kt$PortfolioDataLoader${ PortfolioData.CryptoCurrencyData( userWallet = wallet, status = status, actions = it.states, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:PortfolioItem.kt${ val onClick = state.tokenItemState.onItemClick if (onClick != null) { hapticManager.perform(TangemHapticEffect.View.ContextClick) onClick.invoke(it) } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:PortfolioTokenUMConverter.kt$PortfolioTokenUMConverter.Companion${ if (it == QuickActionUM.Receive) { tokenActionsHandler.handle( action = TokenActionsBSContentUM.Action.CopyAddress, cryptoCurrencyData = cryptoData, ) } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:PortfolioTokenUMConverter.kt$PortfolioTokenUMConverter.Companion${ when (it) { QuickActionUM.Buy -> tokenActionsHandler.handle( action = TokenActionsBSContentUM.Action.Buy, cryptoCurrencyData = cryptoData, ) is QuickActionUM.Exchange -> tokenActionsHandler.handle( action = TokenActionsBSContentUM.Action.Exchange, cryptoCurrencyData = cryptoData, ) QuickActionUM.Receive -> tokenActionsHandler.handle( action = TokenActionsBSContentUM.Action.Receive, cryptoCurrencyData = cryptoData, ) QuickActionUM.Stake -> tokenActionsHandler.handle( action = TokenActionsBSContentUM.Action.Stake, cryptoCurrencyData = cryptoData, ) } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:PricePerformanceBlock.kt${ currentInterval = it state.onIntervalChanged(it) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:SecurityScoreConverter.kt$SecurityScoreConverter${ SecurityScoreBottomSheetContent.SecurityScoreProviderUM( name = it.providerName, lastAuditDate = it.lastAuditDate?.let { date -> MarketsDateTimeFormatters.formatAsDate(date.millis) }, score = it.securityScore, urlData = it.urlData?.let { urlData -> SecurityScoreBottomSheetContent.SecurityScoreProviderUM.UrlData( fullUrl = urlData.fullUrl, rootHost = urlData.rootHost, ) }, iconUrl = it.iconUrl, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:TokenActionsContent.kt${ key(it.title) { ActionRow( state = it, onClick = { state.quickActions.onQuickActionClick(it) }, onLongClick = { state.quickActions.onQuickActionLongClick(it) }, ) } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:TokenMarketBlockModel.kt$TokenMarketBlockModel${ it.onSome { res -> quotesState = QuotesState( currentPrice = res.fiatRate, h24Percent = res.priceChange, ) state.value = state.value.copy( currentPrice = res.fiatRate.format { fiat( // TODO get currency from quotes use case AND-8022 fiatCurrencyCode = currentAppCurrency.value.code, // TODO get currency from quotes use case AND-8022 fiatCurrencySymbol = currentAppCurrency.value.symbol, ).price() }, h24Percent = res.priceChange.format { percent() }, priceChangeType = PriceChangeType.fromBigDecimal(res.priceChange), ) } }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:TokenMarketInfoConverter.kt$TokenMarketInfoConverter${ pricePerformanceConverter.convert( value = it, currentPrice = value.quotes.currentPrice, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:TokensPortfolioUMConverter.kt$TokensPortfolioUMConverter${ it.copy( isQuickActionsShown = if (it.matchWith(cryptoData)) { !it.isQuickActionsShown } else { false }, ) }</ID>
|
|
||||||
<ID>NamedArguments:AvailableToAddDataConverter.kt$AvailableToAddDataConverter$createCryptoCurrency(wallet, it, marketParams, this.account)</ID>
|
|
||||||
<ID>NamedArguments:MarketsListModel.kt$MarketsListModel$combine( mainMarketsListManager.uiItems, mainMarketsListManager.isInInitialLoadingErrorState, getStakingNotificationMaxApyUseCase(), getUserCountryUseCase.invoke(), ) { uiItems, isInInitialLoadingErrorState, stakingNotificationMaxApy, userCountry -> MarketsItemsData( items = uiItems, isInErrorState = isInInitialLoadingErrorState, isSearchNotFound = false, stakingNotificationMaxApy = stakingNotificationMaxApy, userCountry = userCountry, ) }</ID>
|
|
||||||
<ID>NamedArguments:MarketsListModel.kt$MarketsListModel$combine( searchMarketsListManager.uiItems, searchMarketsListManager.isInInitialLoadingErrorState, searchMarketsListManager.isSearchNotFoundState, getStakingNotificationMaxApyUseCase(), getUserCountryUseCase.invoke(), ) { uiItems, isInInitialLoadingErrorState, isSearchNotFoundState, stakingMaxApy, userCountry -> MarketsItemsData( items = uiItems, isInErrorState = isInInitialLoadingErrorState, isSearchNotFound = isSearchNotFoundState, stakingNotificationMaxApy = stakingMaxApy, userCountry = userCountry, ) }</ID>
|
|
||||||
<ID>NestedScopeFunctions:DescriptionConverter.kt$DescriptionConverter$let { fullDescription -> stringReference(fullDescription) }</ID>
|
|
||||||
<ID>NoNameShadowing:MarketsPortfolioModel.kt$MarketsPortfolioModel${ it.isMultiCurrency }</ID>
|
|
||||||
<ID>NoNameShadowing:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel${ if (it.chartState.status == MarketsTokenDetailsUM.ChartState.Status.DATA) { it.copy( body = MarketsTokenDetailsUM.Body.Error( onLoadRetryClick = ::onLoadRetryClicked, ), ) } else { it.copy( body = MarketsTokenDetailsUM.Body.Nothing, ) } }</ID>
|
|
||||||
<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>NullableToStringCall:MarketsListItemUM.kt$MarketsListItemUM$marketCap.toString()</ID>
|
|
||||||
<ID>PropertyUsedBeforeDeclaration:MarketsListModel.kt$MarketsListModel$activeListManager</ID>
|
|
||||||
<ID>PropertyUsedBeforeDeclaration:MarketsListUMStateManager.kt$MarketsListUMStateManager$state</ID>
|
|
||||||
<ID>PropertyUsedBeforeDeclaration:MarketsPortfolioModel.kt$MarketsPortfolioModel$_state</ID>
|
|
||||||
<ID>PropertyUsedBeforeDeclaration:MarketsPortfolioModel.kt$MarketsPortfolioModel$currentAppCurrency</ID>
|
|
||||||
<ID>PropertyUsedBeforeDeclaration:PreviewMyPortfolioUMProvider.kt$PreviewMyPortfolioUMProvider$accountHeader</ID>
|
|
||||||
<ID>PropertyUsedBeforeDeclaration:PreviewMyPortfolioUMProvider.kt$PreviewMyPortfolioUMProvider$accountToken</ID>
|
|
||||||
<ID>PropertyUsedBeforeDeclaration:PreviewMyPortfolioUMProvider.kt$PreviewMyPortfolioUMProvider$sampleToken</ID>
|
|
||||||
<ID>PropertyUsedBeforeDeclaration:PreviewMyPortfolioUMProvider.kt$PreviewMyPortfolioUMProvider$walletHeader</ID>
|
|
||||||
<ID>PropertyUsedBeforeDeclaration:PreviewMyPortfolioUMProvider.kt$PreviewMyPortfolioUMProvider$walletPortfolioHeader</ID>
|
|
||||||
<ID>ReusedModifierInstance:EntryBottomSheetContent.kt$BottomSheetContent( bottomSheetState = bottomSheetState, onHeaderSizeChange = onHeaderSizeChange, modifier = modifier, )</ID>
|
|
||||||
<ID>ReusedModifierInstance:PortfolioQuickActions.kt$Column(modifier = modifier) { actions.fastForEach { action -> LineSeparator() QuickActionItem( state = action, onClick = { onActionClick(action) }, onLongClick = { onActionLongClick(action) }.takeIf { action.longClickAvailable }, ) } }</ID>
|
|
||||||
<ID>ReusedModifierInstance:TokenActionsContent.kt$SecondaryButton( modifier = modifier.fillMaxWidth(), text = stringResourceSafe(R.string.common_later), onClick = state.onLaterClick, )</ID>
|
|
||||||
<ID>SuspendFunWithFlowReturnType:AddToPortfolioModel.kt$AddToPortfolioModel$suspend</ID>
|
|
||||||
<ID>UnnecessaryEventHandlerParameter:SecurityScoreBottomSheet.kt$onLinkClick: (SecurityScoreBottomSheetContent.SecurityScoreProviderUM) -> Unit</ID>
|
|
||||||
<ID>UnnecessaryLet:PortfolioAnalyticsEvent.kt$PortfolioAnalyticsEvent.EventBuilder$let { put("Source", source) }</ID>
|
|
||||||
<ID>UnnecessaryLet:SecurityScoreBottomSheet.kt$let { Text( text = providerUM.lastAuditDate, style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, ) }</ID>
|
|
||||||
<ID>UnsafeCallOnNullableType:DefaultMarketsPortfolioComponent.kt$DefaultMarketsPortfolioComponent$model.newAddToPortfolioManager!!</ID>
|
|
||||||
<ID>UnsafeCallOnNullableType:MarketsListBatchFlowManager.kt$MarketsListBatchFlowManager$previousList!!</ID>
|
|
||||||
<ID>UnsafeCallOnNullableType:PricePerformanceConverter.kt$PricePerformanceConverter$high!!</ID>
|
|
||||||
<ID>UnsafeCallOnNullableType:PricePerformanceConverter.kt$PricePerformanceConverter$low!!</ID>
|
|
||||||
<ID>UseAnyOrNoneInsteadOfFind:NewMarketsPortfolioDelegate.kt$NewMarketsPortfolioDelegate$find { account -> account.addedCurrency.isNotEmpty() }</ID>
|
|
||||||
<ID>UseEmptyCounterpart:MarketDetailsAnalyticsEvent.kt$MarketDetailsAnalyticsEvent$mapOf()</ID>
|
|
||||||
<ID>UseEmptyCounterpart:MarketsListAnalyticsEvent.kt$MarketsListAnalyticsEvent$mapOf()</ID>
|
|
||||||
<ID>UseEmptyCounterpart:NewMarketsPortfolioDelegate.kt$NewMarketsPortfolioDelegate$setOf()</ID>
|
|
||||||
<ID>UseEmptyCounterpart:PortfolioAnalyticsEvent.kt$PortfolioAnalyticsEvent$mapOf()</ID>
|
|
||||||
<ID>UseOrEmpty:DescriptionConverter.kt$DescriptionConverter$value.fullDescription ?: ""</ID>
|
|
||||||
<ID>UseOrEmpty:Formatters.kt$percent?.format { percent() } ?: ""</ID>
|
|
||||||
<ID>UseOrEmpty:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel$percent?.format { percent() } ?: ""</ID>
|
|
||||||
<ID>UseOrEmpty:NewMarketsPortfolioDelegate.kt$NewMarketsPortfolioDelegate$allActions[currencyStatus.currency]?.states ?: emptyList()</ID>
|
|
||||||
<ID>UseOrEmpty:NewMarketsPortfolioDelegate.kt$NewMarketsPortfolioDelegate$portfolio.portfolios .find { it.accountsWithAdded.find { account -> account.addedCurrency.isNotEmpty() } != null } ?.userWallet ?.let { setOf(it.walletId to currency.currency.id) } ?: setOf()</ID>
|
|
||||||
<ID>VarCouldBeVal:MarketsListModel.kt$MarketsListModel$private var updateQuotesJob = JobHolder()</ID>
|
|
||||||
<ID>VarCouldBeVal:MarketsTokenDetailsModel.kt$MarketsTokenDetailsModel$private var quotesJob = JobHolder()</ID>
|
|
||||||
</CurrentIssues>
|
|
||||||
</SmellBaseline>
|
|
||||||
|
|
@ -62,9 +62,11 @@ internal class DefaultMarketsTokenDetailsComponent @AssistedInject constructor(
|
||||||
|
|
||||||
init {
|
init {
|
||||||
componentScope.launch {
|
componentScope.launch {
|
||||||
model.networksState.collectLatest {
|
model.networksState.collectLatest { networksState ->
|
||||||
when (it) {
|
when (networksState) {
|
||||||
is TokenNetworksState.NetworksAvailable -> portfolioComponent?.setTokenNetworks(it.networks)
|
is TokenNetworksState.NetworksAvailable -> portfolioComponent?.setTokenNetworks(
|
||||||
|
networksState.networks,
|
||||||
|
)
|
||||||
TokenNetworksState.NoNetworksAvailable -> portfolioComponent?.setNoNetworksAvailable()
|
TokenNetworksState.NoNetworksAvailable -> portfolioComponent?.setNoNetworksAvailable()
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import com.tangem.domain.markets.TokenMarketParams
|
||||||
|
|
||||||
internal class MarketDetailsAnalyticsEvent(
|
internal class MarketDetailsAnalyticsEvent(
|
||||||
event: String,
|
event: String,
|
||||||
params: Map<String, String> = mapOf(),
|
params: Map<String, String> = emptyMap(),
|
||||||
) : AnalyticsEvent(category = "Markets / Chart", event = event, params = params) {
|
) : AnalyticsEvent(category = "Markets / Chart", event = event, params = params) {
|
||||||
|
|
||||||
data class EventBuilder(
|
data class EventBuilder(
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
private val getUserWalletsUseCase: GetWalletsUseCase,
|
private val getUserWalletsUseCase: GetWalletsUseCase,
|
||||||
) : Model() {
|
) : Model() {
|
||||||
|
|
||||||
private var quotesJob = JobHolder()
|
private val quotesJob = JobHolder()
|
||||||
private var userCountry: UserCountry? = null
|
private var userCountry: UserCountry? = null
|
||||||
private val params = paramsContainer.require<MarketsTokenDetailsComponent.Params>()
|
private val params = paramsContainer.require<MarketsTokenDetailsComponent.Params>()
|
||||||
private val analyticsEventBuilder = MarketDetailsAnalyticsEvent.EventBuilder(token = params.token)
|
private val analyticsEventBuilder = MarketDetailsAnalyticsEvent.EventBuilder(token = params.token)
|
||||||
|
|
@ -101,34 +101,34 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
// === Analytics ===
|
// === Analytics ===
|
||||||
analyticsEventHandler.send(analyticsEventBuilder.linkClicked(linkTitle = link.title))
|
analyticsEventHandler.send(analyticsEventBuilder.linkClicked(linkTitle = link.title))
|
||||||
},
|
},
|
||||||
onSecurityScoreInfoClick = {
|
onSecurityScoreInfoClick = { content ->
|
||||||
showBottomSheet(it)
|
showBottomSheet(content)
|
||||||
|
|
||||||
// === Analytics ===
|
// === Analytics ===
|
||||||
analyticsEventHandler.send(analyticsEventBuilder.securityScoreOpened())
|
analyticsEventHandler.send(analyticsEventBuilder.securityScoreOpened())
|
||||||
},
|
},
|
||||||
onSecurityScoreProviderLinkClick = {
|
onSecurityScoreProviderLinkClick = { provider ->
|
||||||
it.urlData?.fullUrl?.let { url ->
|
provider.urlData?.fullUrl?.let { url ->
|
||||||
urlOpener.openUrl(url)
|
urlOpener.openUrl(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Analytics ===
|
// === Analytics ===
|
||||||
analyticsEventHandler.send(analyticsEventBuilder.securityScoreProviderClicked(it.name))
|
analyticsEventHandler.send(analyticsEventBuilder.securityScoreProviderClicked(provider.name))
|
||||||
},
|
},
|
||||||
// === Analytics ===
|
// === Analytics ===
|
||||||
onPricePerformanceIntervalChanged = {
|
onPricePerformanceIntervalChanged = { interval ->
|
||||||
analyticsEventHandler.send(
|
analyticsEventHandler.send(
|
||||||
analyticsEventBuilder.intervalChanged(
|
analyticsEventBuilder.intervalChanged(
|
||||||
intervalType = MarketDetailsAnalyticsEvent.IntervalType.PricePerformance,
|
intervalType = MarketDetailsAnalyticsEvent.IntervalType.PricePerformance,
|
||||||
interval = it,
|
interval = interval,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onInsightsIntervalChanged = {
|
onInsightsIntervalChanged = { interval ->
|
||||||
analyticsEventHandler.send(
|
analyticsEventHandler.send(
|
||||||
analyticsEventBuilder.intervalChanged(
|
analyticsEventBuilder.intervalChanged(
|
||||||
intervalType = MarketDetailsAnalyticsEvent.IntervalType.Insights,
|
intervalType = MarketDetailsAnalyticsEvent.IntervalType.Insights,
|
||||||
interval = it,
|
interval = interval,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
@ -139,8 +139,8 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
)
|
)
|
||||||
|
|
||||||
private val descriptionConverter = DescriptionConverter(
|
private val descriptionConverter = DescriptionConverter(
|
||||||
onReadModeClicked = {
|
onReadModeClicked = { content ->
|
||||||
showBottomSheet(it)
|
showBottomSheet(content)
|
||||||
// === Analytics ===
|
// === Analytics ===
|
||||||
analyticsEventHandler.send(analyticsEventBuilder.readMoreClicked())
|
analyticsEventHandler.send(analyticsEventBuilder.readMoreClicked())
|
||||||
},
|
},
|
||||||
|
|
@ -162,10 +162,10 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
private val chartDataProducer = MarketChartDataProducer.build(dispatcher = dispatchers.default) {
|
private val chartDataProducer = MarketChartDataProducer.build(dispatcher = dispatchers.default) {
|
||||||
chartData = MarketChartData.NoData.Loading
|
chartData = MarketChartData.NoData.Loading
|
||||||
|
|
||||||
updateLook {
|
updateLook { currentLook ->
|
||||||
val percentChangeType = params.token.tokenQuotes.h24Percent.percentChangeType()
|
val percentChangeType = params.token.tokenQuotes.h24Percent.percentChangeType()
|
||||||
|
|
||||||
it.copy(
|
currentLook.copy(
|
||||||
type = percentChangeType.toChartType(),
|
type = percentChangeType.toChartType(),
|
||||||
xAxisFormatter = MarketsDateTimeFormatters.getChartXFormatterByInterval(PriceChangeInterval.H24),
|
xAxisFormatter = MarketsDateTimeFormatters.getChartXFormatterByInterval(PriceChangeInterval.H24),
|
||||||
yAxisFormatter = { value ->
|
yAxisFormatter = { value ->
|
||||||
|
|
@ -220,7 +220,7 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
),
|
),
|
||||||
selectedInterval = PriceChangeInterval.H24,
|
selectedInterval = PriceChangeInterval.H24,
|
||||||
onSelectedIntervalChange = ::onSelectedIntervalChange,
|
onSelectedIntervalChange = ::onSelectedIntervalChange,
|
||||||
markerSet = false,
|
isMarkerSet = false,
|
||||||
body = MarketsTokenDetailsUM.Body.Loading,
|
body = MarketsTokenDetailsUM.Body.Loading,
|
||||||
triggerPriceChange = consumedEvent(),
|
triggerPriceChange = consumedEvent(),
|
||||||
bottomSheetConfig = TangemBottomSheetConfig(
|
bottomSheetConfig = TangemBottomSheetConfig(
|
||||||
|
|
@ -237,11 +237,11 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
currentQuotes = currentQuotes,
|
currentQuotes = currentQuotes,
|
||||||
lastUpdatedTimestamp = lastUpdatedTimestamp,
|
lastUpdatedTimestamp = lastUpdatedTimestamp,
|
||||||
currentTokenInfo = currentTokenInfo,
|
currentTokenInfo = currentTokenInfo,
|
||||||
onPricePerformanceIntervalChanged = {
|
onPricePerformanceIntervalChanged = { interval ->
|
||||||
analyticsEventHandler.send(
|
analyticsEventHandler.send(
|
||||||
analyticsEventBuilder.intervalChanged(
|
analyticsEventBuilder.intervalChanged(
|
||||||
intervalType = MarketDetailsAnalyticsEvent.IntervalType.PricePerformance,
|
intervalType = MarketDetailsAnalyticsEvent.IntervalType.PricePerformance,
|
||||||
interval = it,
|
interval = interval,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
@ -256,7 +256,7 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
currentAppCurrency
|
currentAppCurrency
|
||||||
.filter { it != params.appCurrency }
|
.filter { it != params.appCurrency }
|
||||||
.collectLatest {
|
.collectLatest { _ ->
|
||||||
initialLoad()
|
initialLoad()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -286,9 +286,9 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
|
|
||||||
private fun loadChart(interval: PriceChangeInterval) {
|
private fun loadChart(interval: PriceChangeInterval) {
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
state.update {
|
state.update { currentState ->
|
||||||
it.copy(
|
currentState.copy(
|
||||||
chartState = it.chartState.copy(
|
chartState = currentState.chartState.copy(
|
||||||
status = MarketsTokenDetailsUM.ChartState.Status.LOADING,
|
status = MarketsTokenDetailsUM.ChartState.Status.LOADING,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -306,10 +306,10 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
preview = false,
|
preview = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
state.update {
|
state.update { currentState ->
|
||||||
it.copy(
|
currentState.copy(
|
||||||
selectedInterval = interval,
|
selectedInterval = interval,
|
||||||
chartState = it.chartState.copy(
|
chartState = currentState.chartState.copy(
|
||||||
status = MarketsTokenDetailsUM.ChartState.Status.LOADING,
|
status = MarketsTokenDetailsUM.ChartState.Status.LOADING,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -318,15 +318,15 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
chart
|
chart
|
||||||
.onRight { updateTokenChart(it) }
|
.onRight { updateTokenChart(it) }
|
||||||
.onLeft {
|
.onLeft {
|
||||||
state.update {
|
state.update { currentState ->
|
||||||
it.copy(
|
currentState.copy(
|
||||||
chartState = it.chartState.copy(
|
chartState = currentState.chartState.copy(
|
||||||
status = MarketsTokenDetailsUM.ChartState.Status.ERROR,
|
status = MarketsTokenDetailsUM.ChartState.Status.ERROR,
|
||||||
),
|
),
|
||||||
body = if (it.body is MarketsTokenDetailsUM.Body.Error) {
|
body = if (currentState.body is MarketsTokenDetailsUM.Body.Error) {
|
||||||
MarketsTokenDetailsUM.Body.Nothing
|
MarketsTokenDetailsUM.Body.Nothing
|
||||||
} else {
|
} else {
|
||||||
it.body
|
currentState.body
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -343,31 +343,31 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
x = tokenChart.timeStamps.map { it.toBigDecimal() }.toImmutableList(),
|
x = tokenChart.timeStamps.map { it.toBigDecimal() }.toImmutableList(),
|
||||||
).sorted()
|
).sorted()
|
||||||
|
|
||||||
updateLook {
|
updateLook { currentLook ->
|
||||||
it.copy(
|
currentLook.copy(
|
||||||
xAxisFormatter = xAxisFormatter,
|
xAxisFormatter = xAxisFormatter,
|
||||||
type = state.value.priceChangeType.toChartType(),
|
type = state.value.priceChangeType.toChartType(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.update {
|
state.update { currentState ->
|
||||||
it.copy(
|
currentState.copy(
|
||||||
chartState = it.chartState.copy(
|
chartState = currentState.chartState.copy(
|
||||||
status = MarketsTokenDetailsUM.ChartState.Status.DATA,
|
status = MarketsTokenDetailsUM.ChartState.Status.DATA,
|
||||||
),
|
),
|
||||||
body = if (it.body is MarketsTokenDetailsUM.Body.Nothing) {
|
body = if (currentState.body is MarketsTokenDetailsUM.Body.Nothing) {
|
||||||
MarketsTokenDetailsUM.Body.Error(onLoadRetryClick = ::onLoadRetryClicked)
|
MarketsTokenDetailsUM.Body.Error(onLoadRetryClick = ::onLoadRetryClicked)
|
||||||
} else {
|
} else {
|
||||||
it.body
|
currentState.body
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadInfo() {
|
private fun loadInfo() {
|
||||||
state.update {
|
state.update { currentState ->
|
||||||
it.copy(
|
currentState.copy(
|
||||||
body = MarketsTokenDetailsUM.Body.Loading,
|
body = MarketsTokenDetailsUM.Body.Loading,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -382,15 +382,15 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
tokenMarketInfo.fold(
|
tokenMarketInfo.fold(
|
||||||
ifRight = { result -> updateInfo(result) },
|
ifRight = { result -> updateInfo(result) },
|
||||||
ifLeft = {
|
ifLeft = {
|
||||||
state.update {
|
state.update { currentState ->
|
||||||
if (it.chartState.status == MarketsTokenDetailsUM.ChartState.Status.DATA) {
|
if (currentState.chartState.status == MarketsTokenDetailsUM.ChartState.Status.DATA) {
|
||||||
it.copy(
|
currentState.copy(
|
||||||
body = MarketsTokenDetailsUM.Body.Error(
|
body = MarketsTokenDetailsUM.Body.Error(
|
||||||
onLoadRetryClick = ::onLoadRetryClicked,
|
onLoadRetryClick = ::onLoadRetryClicked,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
it.copy(
|
currentState.copy(
|
||||||
body = MarketsTokenDetailsUM.Body.Nothing,
|
body = MarketsTokenDetailsUM.Body.Nothing,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -408,8 +408,8 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
|
|
||||||
val percent = newInfo.quotes.getPercentByInterval(interval = state.value.selectedInterval)
|
val percent = newInfo.quotes.getPercentByInterval(interval = state.value.selectedInterval)
|
||||||
|
|
||||||
state.update {
|
state.update { currentState ->
|
||||||
it.copy(
|
currentState.copy(
|
||||||
priceText = newInfo.quotes.currentPrice.format {
|
priceText = newInfo.quotes.currentPrice.format {
|
||||||
fiat(
|
fiat(
|
||||||
fiatCurrencySymbol = currentAppCurrency.value.symbol,
|
fiatCurrencySymbol = currentAppCurrency.value.symbol,
|
||||||
|
|
@ -417,7 +417,7 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
).price()
|
).price()
|
||||||
},
|
},
|
||||||
priceChangePercentText = newInfo.quotes.getFormattedPercentByInterval(
|
priceChangePercentText = newInfo.quotes.getFormattedPercentByInterval(
|
||||||
interval = it.selectedInterval,
|
interval = currentState.selectedInterval,
|
||||||
),
|
),
|
||||||
priceChangeType = percent.percentChangeType(),
|
priceChangeType = percent.percentChangeType(),
|
||||||
body = MarketsTokenDetailsUM.Body.Content(
|
body = MarketsTokenDetailsUM.Body.Content(
|
||||||
|
|
@ -427,14 +427,14 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val allWalletsIsHot = getUserWalletsUseCase.invokeSync().all { it is UserWallet.Hot }
|
val areAllWalletsHot = getUserWalletsUseCase.invokeSync().all { it is UserWallet.Hot }
|
||||||
|
|
||||||
val networks = newInfo.networks?.filter {
|
val networks = newInfo.networks?.filter { network ->
|
||||||
BlockchainUtils.isSupportedNetworkId(
|
BlockchainUtils.isSupportedNetworkId(
|
||||||
blockchainId = it.networkId,
|
blockchainId = network.networkId,
|
||||||
excludedBlockchains = excludedBlockchains,
|
excludedBlockchains = excludedBlockchains,
|
||||||
hotExcludedBlockchains = hotWalletExcludedBlockchains,
|
hotExcludedBlockchains = hotWalletExcludedBlockchains,
|
||||||
hasOnlyHotWallets = allWalletsIsHot,
|
hasOnlyHotWallets = areAllWalletsHot,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -445,8 +445,8 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
chartDataProducer.runTransaction {
|
chartDataProducer.runTransaction {
|
||||||
updateLook {
|
updateLook { currentLook ->
|
||||||
it.copy(type = percent.percentChangeType().toChartType())
|
currentLook.copy(type = percent.percentChangeType().toChartType())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -481,8 +481,8 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
val quotes = currentQuotes.value
|
val quotes = currentQuotes.value
|
||||||
val priceChangePercent = quotes.getFormattedPercentByInterval(interval)
|
val priceChangePercent = quotes.getFormattedPercentByInterval(interval)
|
||||||
|
|
||||||
state.update {
|
state.update { currentState ->
|
||||||
it.copy(
|
currentState.copy(
|
||||||
priceChangePercentText = priceChangePercent,
|
priceChangePercentText = priceChangePercent,
|
||||||
selectedInterval = interval,
|
selectedInterval = interval,
|
||||||
priceChangeType = quotes.getPercentByInterval(interval)?.percentChangeType()
|
priceChangeType = quotes.getPercentByInterval(interval)?.percentChangeType()
|
||||||
|
|
@ -502,10 +502,10 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
private fun onMarkerPointSelected(markerTimestamp: BigDecimal?, price: BigDecimal?) {
|
private fun onMarkerPointSelected(markerTimestamp: BigDecimal?, price: BigDecimal?) {
|
||||||
val currentState = state.value
|
val currentState = state.value
|
||||||
|
|
||||||
val dateTimeText = markerTimestamp?.let {
|
val dateTimeText = markerTimestamp?.let { timestamp ->
|
||||||
MarketsDateTimeFormatters.formatDateByIntervalWithMarker(
|
MarketsDateTimeFormatters.formatDateByIntervalWithMarker(
|
||||||
interval = currentState.selectedInterval,
|
interval = currentState.selectedInterval,
|
||||||
markerTimestamp = it,
|
markerTimestamp = timestamp,
|
||||||
)
|
)
|
||||||
} ?: getDefaultDateTimeString(currentState.selectedInterval)
|
} ?: getDefaultDateTimeString(currentState.selectedInterval)
|
||||||
|
|
||||||
|
|
@ -516,18 +516,18 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
).price()
|
).price()
|
||||||
}
|
}
|
||||||
|
|
||||||
val percent = price?.let {
|
val percent = price?.let { selectedPrice ->
|
||||||
getChangePercentBetween(
|
getChangePercentBetween(
|
||||||
previousPrice = it,
|
previousPrice = selectedPrice,
|
||||||
currentPrice = currentQuotes.value.currentPrice,
|
currentPrice = currentQuotes.value.currentPrice,
|
||||||
)
|
)
|
||||||
} ?: currentQuotes.value.getPercentByInterval(currentState.selectedInterval)
|
} ?: currentQuotes.value.getPercentByInterval(currentState.selectedInterval)
|
||||||
|
|
||||||
val percentText = percent?.format { percent() } ?: ""
|
val percentText = percent?.format { percent() }.orEmpty()
|
||||||
|
|
||||||
state.update { stateToUpdate ->
|
state.update { stateToUpdate ->
|
||||||
stateToUpdate.copy(
|
stateToUpdate.copy(
|
||||||
markerSet = markerTimestamp != null,
|
isMarkerSet = markerTimestamp != null,
|
||||||
dateTimeText = dateTimeText,
|
dateTimeText = dateTimeText,
|
||||||
priceText = priceText,
|
priceText = priceText,
|
||||||
priceChangePercentText = percentText,
|
priceChangePercentText = percentText,
|
||||||
|
|
@ -536,8 +536,8 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
chartDataProducer.runTransaction {
|
chartDataProducer.runTransaction {
|
||||||
updateLook {
|
updateLook { currentLook ->
|
||||||
it.copy(
|
currentLook.copy(
|
||||||
type = percent.percentChangeType().toChartType(),
|
type = percent.percentChangeType().toChartType(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -603,9 +603,9 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
ifLeft = {
|
ifLeft = {
|
||||||
ExchangesBottomSheetContent.Error(onRetryClick = { onListedOnClick(exchangesCount) })
|
ExchangesBottomSheetContent.Error(onRetryClick = { onListedOnClick(exchangesCount) })
|
||||||
},
|
},
|
||||||
ifRight = {
|
ifRight = { exchanges ->
|
||||||
ExchangesBottomSheetContent.Content(
|
ExchangesBottomSheetContent.Content(
|
||||||
exchangeItems = ExchangeItemStateConverter.convertList(it).toImmutableList(),
|
exchangeItems = ExchangeItemStateConverter.convertList(exchanges).toImmutableList(),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -20,29 +20,26 @@ internal class DescriptionConverter(
|
||||||
|
|
||||||
override fun convert(value: TokenMarketInfo): MarketsTokenDetailsUM.Description? {
|
override fun convert(value: TokenMarketInfo): MarketsTokenDetailsUM.Description? {
|
||||||
if (needApplyFCARestrictions()) return null
|
if (needApplyFCARestrictions()) return null
|
||||||
return value.shortDescription?.let { desc ->
|
val shortDesc = value.shortDescription ?: return null
|
||||||
MarketsTokenDetailsUM.Description(
|
return MarketsTokenDetailsUM.Description(
|
||||||
shortDescription = stringReference(desc),
|
shortDescription = stringReference(shortDesc),
|
||||||
fullDescription = value.fullDescription?.let { fullDescription ->
|
fullDescription = value.fullDescription?.let(::stringReference),
|
||||||
stringReference(fullDescription)
|
onReadMoreClick = {
|
||||||
},
|
onReadModeClicked(
|
||||||
onReadMoreClick = {
|
InfoBottomSheetContent(
|
||||||
onReadModeClicked(
|
title = resourceReference(
|
||||||
InfoBottomSheetContent(
|
R.string.markets_token_details_about_token_title,
|
||||||
title = resourceReference(
|
wrappedList(
|
||||||
R.string.markets_token_details_about_token_title,
|
value.name,
|
||||||
wrappedList(
|
|
||||||
value.name,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
body = stringReference(value.fullDescription ?: ""),
|
|
||||||
generatedAINotificationUM = InfoBottomSheetContent.GeneratedAINotificationUM(
|
|
||||||
onClick = onGeneratedAINotificationClick,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
body = stringReference(value.fullDescription.orEmpty()),
|
||||||
},
|
generatedAINotificationUM = InfoBottomSheetContent.GeneratedAINotificationUM(
|
||||||
)
|
onClick = onGeneratedAINotificationClick,
|
||||||
}
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -56,11 +56,13 @@ internal class PricePerformanceConverter(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TokenMarketInfo.Range.calculateFraction(currentPrice: BigDecimal): Float {
|
private fun TokenMarketInfo.Range.calculateFraction(currentPrice: BigDecimal): Float {
|
||||||
|
val lowValue = low
|
||||||
|
val highValue = high
|
||||||
return when {
|
return when {
|
||||||
low == null || high == null || high == BigDecimal.ZERO || currentPrice < low -> 0f
|
lowValue == null || highValue == null || highValue == BigDecimal.ZERO || currentPrice < lowValue -> 0f
|
||||||
currentPrice > high || low == high -> 1f
|
currentPrice > highValue || lowValue == highValue -> 1f
|
||||||
else -> {
|
else -> {
|
||||||
(currentPrice - low!!).divide(high!! - low!!, RoundingMode.HALF_UP)
|
(currentPrice - lowValue).divide(highValue - lowValue, RoundingMode.HALF_UP)
|
||||||
.setScale(2, RoundingMode.HALF_UP)
|
.setScale(2, RoundingMode.HALF_UP)
|
||||||
.toFloat().coerceAtMost(1f)
|
.toFloat().coerceAtMost(1f)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,21 +31,20 @@ internal class SecurityScoreConverter(
|
||||||
SecurityScoreBottomSheetContent(
|
SecurityScoreBottomSheetContent(
|
||||||
title = resourceReference(R.string.markets_token_details_security_score),
|
title = resourceReference(R.string.markets_token_details_security_score),
|
||||||
description = resourceReference(R.string.markets_token_details_security_score_description),
|
description = resourceReference(R.string.markets_token_details_security_score_description),
|
||||||
providers = value.securityScoreProviderData.map {
|
providers = value.securityScoreProviderData.map { provider ->
|
||||||
SecurityScoreBottomSheetContent.SecurityScoreProviderUM(
|
SecurityScoreBottomSheetContent.SecurityScoreProviderUM(
|
||||||
name = it.providerName,
|
name = provider.providerName,
|
||||||
lastAuditDate = it.lastAuditDate?.let { date ->
|
lastAuditDate = provider.lastAuditDate?.let { date ->
|
||||||
MarketsDateTimeFormatters.formatAsDate(date.millis)
|
MarketsDateTimeFormatters.formatAsDate(date.millis)
|
||||||
},
|
},
|
||||||
score = it.securityScore,
|
score = provider.securityScore,
|
||||||
urlData = it.urlData?.let {
|
urlData = provider.urlData?.let { urlData ->
|
||||||
urlData ->
|
|
||||||
SecurityScoreBottomSheetContent.SecurityScoreProviderUM.UrlData(
|
SecurityScoreBottomSheetContent.SecurityScoreProviderUM.UrlData(
|
||||||
fullUrl = urlData.fullUrl,
|
fullUrl = urlData.fullUrl,
|
||||||
rootHost = urlData.rootHost,
|
rootHost = urlData.rootHost,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
iconUrl = it.iconUrl,
|
iconUrl = provider.iconUrl,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onProviderLinkClick = onSecurityScoreProviderLinkClick,
|
onProviderLinkClick = onSecurityScoreProviderLinkClick,
|
||||||
|
|
|
||||||
|
|
@ -64,9 +64,9 @@ internal class TokenMarketInfoConverter(
|
||||||
insights = insights,
|
insights = insights,
|
||||||
securityScore = securityScore,
|
securityScore = securityScore,
|
||||||
metrics = value.metrics?.let { metricsConverter.convert(it) },
|
metrics = value.metrics?.let { metricsConverter.convert(it) },
|
||||||
pricePerformance = value.pricePerformance?.let {
|
pricePerformance = value.pricePerformance?.let { performance ->
|
||||||
pricePerformanceConverter.convert(
|
pricePerformanceConverter.convert(
|
||||||
value = it,
|
value = performance,
|
||||||
currentPrice = value.quotes.currentPrice,
|
currentPrice = value.quotes.currentPrice,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ internal fun TokenQuotes.getFormattedPercentByInterval(interval: PriceChangeInte
|
||||||
PriceChangeInterval.ALL_TIME -> allTimeChangePercent
|
PriceChangeInterval.ALL_TIME -> allTimeChangePercent
|
||||||
}
|
}
|
||||||
|
|
||||||
return percent?.format { percent() } ?: ""
|
return percent?.format { percent() }.orEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun TokenQuotes.getPercentByInterval(interval: PriceChangeInterval): BigDecimal? {
|
internal fun TokenQuotes.getPercentByInterval(interval: PriceChangeInterval): BigDecimal? {
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ internal class QuotesStateUpdater(
|
||||||
val priceChangeType = percent.percentChangeType()
|
val priceChangeType = percent.percentChangeType()
|
||||||
|
|
||||||
// wait until marker is removed
|
// wait until marker is removed
|
||||||
state.first { it.markerSet.not() }
|
state.first { it.isMarkerSet.not() }
|
||||||
|
|
||||||
currentQuotes.value = newQuotes
|
currentQuotes.value = newQuotes
|
||||||
lastUpdatedTimestamp.value = DateTime.now().millis
|
lastUpdatedTimestamp.value = DateTime.now().millis
|
||||||
|
|
|
||||||
|
|
@ -107,10 +107,10 @@ private fun Content(
|
||||||
.fillMaxSize(),
|
.fillMaxSize(),
|
||||||
) {
|
) {
|
||||||
TopBar(
|
TopBar(
|
||||||
modifier = Modifier.onGloballyPositioned {
|
modifier = Modifier.onGloballyPositioned { coordinates ->
|
||||||
if (it.size.height > 0) {
|
if (coordinates.size.height > 0) {
|
||||||
with(density) {
|
with(density) {
|
||||||
onHeaderSizeChange(it.size.height.toDp())
|
onHeaderSizeChange(coordinates.size.height.toDp())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -174,7 +174,7 @@ private fun TopBar(
|
||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val showPriceSubtitle by remember {
|
val shouldShowPriceSubtitle by remember {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
lazyListState.firstVisibleItemIndex > 1
|
lazyListState.firstVisibleItemIndex > 1
|
||||||
}
|
}
|
||||||
|
|
@ -183,7 +183,7 @@ private fun TopBar(
|
||||||
TangemTopAppBar(
|
TangemTopAppBar(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
title = tokenName,
|
title = tokenName,
|
||||||
subtitle = if (showPriceSubtitle) tokenPrice else null,
|
subtitle = if (shouldShowPriceSubtitle) tokenPrice else null,
|
||||||
startButton = TopAppBarButtonUM.Back(
|
startButton = TopAppBarButtonUM.Back(
|
||||||
onBackClicked = onBackClick,
|
onBackClicked = onBackClick,
|
||||||
enabled = isBackButtonEnabled,
|
enabled = isBackButtonEnabled,
|
||||||
|
|
@ -240,8 +240,8 @@ private fun TokenPriceText(
|
||||||
|
|
||||||
val color = remember(generalColor) { Animatable(generalColor) }
|
val color = remember(generalColor) { Animatable(generalColor) }
|
||||||
|
|
||||||
EventEffect(triggerPriceChange) {
|
EventEffect(triggerPriceChange) { changeType ->
|
||||||
val nextColor = when (it) {
|
val nextColor = when (changeType) {
|
||||||
PriceChangeType.UP,
|
PriceChangeType.UP,
|
||||||
-> growColor
|
-> growColor
|
||||||
PriceChangeType.DOWN -> fallColor
|
PriceChangeType.DOWN -> fallColor
|
||||||
|
|
|
||||||
|
|
@ -48,12 +48,12 @@ internal fun InsightsBlock(state: InsightsUM, modifier: Modifier = Modifier) {
|
||||||
PriceChangeInterval.MONTH,
|
PriceChangeInterval.MONTH,
|
||||||
),
|
),
|
||||||
initialSelectedItem = PriceChangeInterval.H24,
|
initialSelectedItem = PriceChangeInterval.H24,
|
||||||
onClick = {
|
onClick = { interval ->
|
||||||
currentInterval = it
|
currentInterval = interval
|
||||||
state.onIntervalChanged(it)
|
state.onIntervalChanged(interval)
|
||||||
},
|
},
|
||||||
modifier = Modifier.width(IntrinsicSize.Min),
|
modifier = Modifier.width(IntrinsicSize.Min),
|
||||||
) {
|
) { interval ->
|
||||||
Box(
|
Box(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
|
@ -65,7 +65,7 @@ internal fun InsightsBlock(state: InsightsUM, modifier: Modifier = Modifier) {
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center),
|
||||||
text = it.getText().resolveReference(),
|
text = interval.getText().resolveReference(),
|
||||||
style = TangemTheme.typography.caption1,
|
style = TangemTheme.typography.caption1,
|
||||||
color = TangemTheme.colors.text.primary1,
|
color = TangemTheme.colors.text.primary1,
|
||||||
)
|
)
|
||||||
|
|
@ -82,10 +82,10 @@ internal fun InsightsBlock(state: InsightsUM, modifier: Modifier = Modifier) {
|
||||||
|
|
||||||
GridItems(
|
GridItems(
|
||||||
items = infoPoints,
|
items = infoPoints,
|
||||||
itemContent = {
|
itemContent = { infoPoint ->
|
||||||
InfoPoint(
|
InfoPoint(
|
||||||
modifier = Modifier.align(Alignment.CenterStart),
|
modifier = Modifier.align(Alignment.CenterStart),
|
||||||
infoPointUM = it,
|
infoPointUM = infoPoint,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -96,11 +96,11 @@ private fun SubBlock(
|
||||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||||
) {
|
) {
|
||||||
links.fastForEach {
|
links.fastForEach { link ->
|
||||||
Chip(
|
Chip(
|
||||||
text = stringReference(it.title),
|
text = stringReference(link.title),
|
||||||
iconResId = it.iconRes,
|
iconResId = link.iconRes,
|
||||||
onClick = { onLinkClick(it) },
|
onClick = { onLinkClick(link) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,8 @@ internal fun MarketTokenDetailsChart(
|
||||||
|
|
||||||
val chartState = rememberMarketChartState(
|
val chartState = rememberMarketChartState(
|
||||||
dataProducer = state.dataProducer,
|
dataProducer = state.dataProducer,
|
||||||
colorMapper = {
|
colorMapper = { chartType ->
|
||||||
when (it) {
|
when (chartType) {
|
||||||
MarketChartLook.Type.Growing -> growingColor
|
MarketChartLook.Type.Growing -> growingColor
|
||||||
MarketChartLook.Type.Falling -> fallingColor
|
MarketChartLook.Type.Falling -> fallingColor
|
||||||
MarketChartLook.Type.Neutral -> neutralColor
|
MarketChartLook.Type.Neutral -> neutralColor
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ const val MAX_METRICS_COUNT = 6
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun MetricsBlock(state: MetricsUM, modifier: Modifier = Modifier) {
|
internal fun MetricsBlock(state: MetricsUM, modifier: Modifier = Modifier) {
|
||||||
var expanded by remember { mutableStateOf(false) }
|
var isExpanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
InformationBlock(
|
InformationBlock(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
|
@ -44,11 +44,11 @@ internal fun MetricsBlock(state: MetricsUM, modifier: Modifier = Modifier) {
|
||||||
},
|
},
|
||||||
action = {
|
action = {
|
||||||
if (state.metrics.size > MAX_METRICS_COUNT) {
|
if (state.metrics.size > MAX_METRICS_COUNT) {
|
||||||
ShowLessMoreButton(expanded = expanded, onClick = { expanded = !expanded })
|
ShowLessMoreButton(expanded = isExpanded, onClick = { isExpanded = !isExpanded })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
content = {
|
content = {
|
||||||
val metrics = if (expanded) {
|
val metrics = if (isExpanded) {
|
||||||
state.metrics
|
state.metrics
|
||||||
} else {
|
} else {
|
||||||
state.metrics.take(MAX_METRICS_COUNT).toImmutableList()
|
state.metrics.take(MAX_METRICS_COUNT).toImmutableList()
|
||||||
|
|
|
||||||
|
|
@ -53,12 +53,12 @@ internal fun PricePerformanceBlock(state: PricePerformanceUM, modifier: Modifier
|
||||||
PriceChangeInterval.ALL_TIME,
|
PriceChangeInterval.ALL_TIME,
|
||||||
),
|
),
|
||||||
initialSelectedItem = PriceChangeInterval.H24,
|
initialSelectedItem = PriceChangeInterval.H24,
|
||||||
onClick = {
|
onClick = { interval ->
|
||||||
currentInterval = it
|
currentInterval = interval
|
||||||
state.onIntervalChanged(it)
|
state.onIntervalChanged(interval)
|
||||||
},
|
},
|
||||||
modifier = Modifier.width(IntrinsicSize.Min),
|
modifier = Modifier.width(IntrinsicSize.Min),
|
||||||
) {
|
) { interval ->
|
||||||
Box(
|
Box(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
|
@ -70,7 +70,7 @@ internal fun PricePerformanceBlock(state: PricePerformanceUM, modifier: Modifier
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.align(Alignment.Center),
|
modifier = Modifier.align(Alignment.Center),
|
||||||
text = it.getText().resolveReference(),
|
text = interval.getText().resolveReference(),
|
||||||
style = TangemTheme.typography.caption1,
|
style = TangemTheme.typography.caption1,
|
||||||
color = TangemTheme.colors.text.primary1,
|
color = TangemTheme.colors.text.primary1,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ internal fun SecurityScoreBottomSheet(config: TangemBottomSheetConfig) {
|
||||||
) {
|
) {
|
||||||
SecurityScoreProviderRow(
|
SecurityScoreProviderRow(
|
||||||
providerUM = provider,
|
providerUM = provider,
|
||||||
onLinkClick = content.onProviderLinkClick,
|
onLinkClick = { content.onProviderLinkClick(provider) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -85,7 +85,7 @@ internal fun SecurityScoreBottomSheet(config: TangemBottomSheetConfig) {
|
||||||
@Composable
|
@Composable
|
||||||
private fun SecurityScoreProviderRow(
|
private fun SecurityScoreProviderRow(
|
||||||
providerUM: SecurityScoreBottomSheetContent.SecurityScoreProviderUM,
|
providerUM: SecurityScoreBottomSheetContent.SecurityScoreProviderUM,
|
||||||
onLinkClick: (SecurityScoreBottomSheetContent.SecurityScoreProviderUM) -> Unit,
|
onLinkClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -118,7 +118,7 @@ private fun SecurityScoreProviderRow(
|
||||||
style = TangemTheme.typography.subtitle2,
|
style = TangemTheme.typography.subtitle2,
|
||||||
color = TangemTheme.colors.text.primary1,
|
color = TangemTheme.colors.text.primary1,
|
||||||
)
|
)
|
||||||
providerUM.lastAuditDate?.let {
|
if (providerUM.lastAuditDate != null) {
|
||||||
Text(
|
Text(
|
||||||
text = providerUM.lastAuditDate,
|
text = providerUM.lastAuditDate,
|
||||||
style = TangemTheme.typography.caption2,
|
style = TangemTheme.typography.caption2,
|
||||||
|
|
@ -136,7 +136,7 @@ private fun SecurityScoreProviderRow(
|
||||||
enabled = providerUM.urlData != null,
|
enabled = providerUM.urlData != null,
|
||||||
indication = ripple(bounded = false),
|
indication = ripple(bounded = false),
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
onClick = { onLinkClick(providerUM) },
|
onClick = onLinkClick,
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
ScoreStarsBlock(
|
ScoreStarsBlock(
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ internal object MarketsTokenDetailsPreview {
|
||||||
onDismissRequest = {},
|
onDismissRequest = {},
|
||||||
content = TangemBottomSheetConfigContent.Empty,
|
content = TangemBottomSheetConfigContent.Empty,
|
||||||
),
|
),
|
||||||
markerSet = false,
|
isMarkerSet = false,
|
||||||
triggerPriceChange = consumedEvent(),
|
triggerPriceChange = consumedEvent(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -123,7 +123,7 @@ internal object MarketsTokenDetailsPreview {
|
||||||
onDismissRequest = {},
|
onDismissRequest = {},
|
||||||
content = TangemBottomSheetConfigContent.Empty,
|
content = TangemBottomSheetConfigContent.Empty,
|
||||||
),
|
),
|
||||||
markerSet = true,
|
isMarkerSet = true,
|
||||||
triggerPriceChange = consumedEvent(),
|
triggerPriceChange = consumedEvent(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -17,7 +17,7 @@ internal data class MarketsTokenDetailsUM(
|
||||||
val priceChangePercentText: String?,
|
val priceChangePercentText: String?,
|
||||||
val priceChangeType: PriceChangeType,
|
val priceChangeType: PriceChangeType,
|
||||||
val selectedInterval: PriceChangeInterval,
|
val selectedInterval: PriceChangeInterval,
|
||||||
val markerSet: Boolean,
|
val isMarkerSet: Boolean,
|
||||||
val chartState: ChartState,
|
val chartState: ChartState,
|
||||||
val onSelectedIntervalChange: (PriceChangeInterval) -> Unit,
|
val onSelectedIntervalChange: (PriceChangeInterval) -> Unit,
|
||||||
val bottomSheetConfig: TangemBottomSheetConfig,
|
val bottomSheetConfig: TangemBottomSheetConfig,
|
||||||
|
|
|
||||||
|
|
@ -37,20 +37,21 @@ internal fun EntryBottomSheetContent(
|
||||||
Children(
|
Children(
|
||||||
stack = stackState.value,
|
stack = stackState.value,
|
||||||
animation = stackAnimation(slide()),
|
animation = stackAnimation(slide()),
|
||||||
) {
|
modifier = modifier,
|
||||||
when (it.configuration) {
|
) { child ->
|
||||||
|
when (child.configuration) {
|
||||||
is MarketsEntryChildFactory.Child.TokenDetails -> {
|
is MarketsEntryChildFactory.Child.TokenDetails -> {
|
||||||
(it.instance as MarketsTokenDetailsComponent).BottomSheetContent(
|
(child.instance as MarketsTokenDetailsComponent).BottomSheetContent(
|
||||||
bottomSheetState = bottomSheetState,
|
bottomSheetState = bottomSheetState,
|
||||||
onHeaderSizeChange = onHeaderSizeChange,
|
onHeaderSizeChange = onHeaderSizeChange,
|
||||||
modifier = modifier,
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is MarketsEntryChildFactory.Child.TokenList -> {
|
is MarketsEntryChildFactory.Child.TokenList -> {
|
||||||
(it.instance as MarketsTokenListComponent).BottomSheetContent(
|
(child.instance as MarketsTokenListComponent).BottomSheetContent(
|
||||||
bottomSheetState = bottomSheetState,
|
bottomSheetState = bottomSheetState,
|
||||||
onHeaderSizeChange = onHeaderSizeChange,
|
onHeaderSizeChange = onHeaderSizeChange,
|
||||||
modifier = modifier,
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import kotlinx.serialization.Serializable
|
||||||
internal data class AvailableToAddData(
|
internal data class AvailableToAddData(
|
||||||
val availableToAddWallets: Map<UserWalletId, AvailableToAddWallet>,
|
val availableToAddWallets: Map<UserWalletId, AvailableToAddWallet>,
|
||||||
) {
|
) {
|
||||||
val availableToAdd: Boolean
|
val isAvailableToAdd: Boolean
|
||||||
get() = availableToAddWallets.isNotEmpty()
|
get() = availableToAddWallets.isNotEmpty()
|
||||||
val isSinglePortfolio: Boolean
|
val isSinglePortfolio: Boolean
|
||||||
get() = availableToAddWallets.size == 1 && availableToAddWallets.values.first().accounts.size == 1
|
get() = availableToAddWallets.size == 1 && availableToAddWallets.values.first().accounts.size == 1
|
||||||
|
|
@ -48,11 +48,11 @@ internal data class SelectedPortfolio(
|
||||||
val userWallet: UserWallet,
|
val userWallet: UserWallet,
|
||||||
val account: AvailableToAddAccount,
|
val account: AvailableToAddAccount,
|
||||||
val isAccountMode: Boolean,
|
val isAccountMode: Boolean,
|
||||||
val availableMorePortfolio: Boolean,
|
val hasMorePortfoliosAvailable: Boolean,
|
||||||
)
|
)
|
||||||
|
|
||||||
internal data class SelectedNetwork(
|
internal data class SelectedNetwork(
|
||||||
val selectedNetwork: TokenMarketInfo.Network,
|
val selectedNetwork: TokenMarketInfo.Network,
|
||||||
val cryptoCurrency: CryptoCurrency,
|
val cryptoCurrency: CryptoCurrency,
|
||||||
val availableMoreNetwork: Boolean,
|
val hasMoreNetworksAvailable: Boolean,
|
||||||
)
|
)
|
||||||
|
|
@ -131,7 +131,7 @@ internal class DefaultAddToPortfolioComponent @AssistedInject constructor(
|
||||||
end = 16.dp,
|
end = 16.dp,
|
||||||
bottom = 16.dp,
|
bottom = 16.dp,
|
||||||
)
|
)
|
||||||
val scrollableContent = when (stack.active.configuration) {
|
val isScrollableContent = when (stack.active.configuration) {
|
||||||
AddToPortfolioRoutes.PortfolioSelector -> false
|
AddToPortfolioRoutes.PortfolioSelector -> false
|
||||||
AddToPortfolioRoutes.AddToken,
|
AddToPortfolioRoutes.AddToken,
|
||||||
AddToPortfolioRoutes.Empty,
|
AddToPortfolioRoutes.Empty,
|
||||||
|
|
@ -139,7 +139,7 @@ internal class DefaultAddToPortfolioComponent @AssistedInject constructor(
|
||||||
AddToPortfolioRoutes.TokenActions,
|
AddToPortfolioRoutes.TokenActions,
|
||||||
-> true
|
-> true
|
||||||
}
|
}
|
||||||
if (scrollableContent) {
|
if (isScrollableContent) {
|
||||||
Column(
|
Column(
|
||||||
modifier = paddingModifier.verticalScroll(rememberScrollState()),
|
modifier = paddingModifier.verticalScroll(rememberScrollState()),
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,14 @@ internal class AvailableToAddDataConverter @Inject constructor(
|
||||||
): AvailableToAddData {
|
): AvailableToAddData {
|
||||||
suspend fun AccountStatus.getAvailableToAddAccount(wallet: UserWallet): AvailableToAddAccount? {
|
suspend fun AccountStatus.getAvailableToAddAccount(wallet: UserWallet): AvailableToAddAccount? {
|
||||||
val currencies = availableNetworks
|
val currencies = availableNetworks
|
||||||
.mapNotNull { createCryptoCurrency(wallet, it, marketParams, this.account) }
|
.mapNotNull { network ->
|
||||||
|
createCryptoCurrency(
|
||||||
|
userWallet = wallet,
|
||||||
|
network = network,
|
||||||
|
marketParams = marketParams,
|
||||||
|
account = this.account,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (currencies.isEmpty()) return null
|
if (currencies.isEmpty()) return null
|
||||||
|
|
||||||
|
|
@ -74,9 +81,9 @@ internal class AvailableToAddDataConverter @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
val availableToAddWallets: Map<UserWalletId, AvailableToAddWallet> = balances
|
val availableToAddWallets: Map<UserWalletId, AvailableToAddWallet> = balances
|
||||||
.map {
|
.map { entry ->
|
||||||
val (walletId, _) = it
|
val (walletId, _) = entry
|
||||||
val availableToAddWallet = getAvailableToAddWallet(it)
|
val availableToAddWallet = getAvailableToAddWallet(entry)
|
||||||
walletId to availableToAddWallet
|
walletId to availableToAddWallet
|
||||||
}
|
}
|
||||||
.filter { (_, wallet) -> wallet.availableToAddAccounts.isNotEmpty() }
|
.filter { (_, wallet) -> wallet.availableToAddAccounts.isNotEmpty() }
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ internal class AddToPortfolioModel @Inject constructor(
|
||||||
val data = featureDataFlow.value
|
val data = featureDataFlow.value
|
||||||
|
|
||||||
// you must control it via [AddToPortfolioManager.state]
|
// you must control it via [AddToPortfolioManager.state]
|
||||||
if (!data.availableToAdd) {
|
if (!data.isAvailableToAdd) {
|
||||||
finishFlow()
|
finishFlow()
|
||||||
return@channelFlow
|
return@channelFlow
|
||||||
}
|
}
|
||||||
|
|
@ -182,8 +182,8 @@ internal class AddToPortfolioModel @Inject constructor(
|
||||||
messageSender.send(ToastMessage(message = resourceReference(R.string.markets_token_added)))
|
messageSender.send(ToastMessage(message = resourceReference(R.string.markets_token_added)))
|
||||||
|
|
||||||
setupTokenActionsFlow(selectedPortfolio, addedToken)
|
setupTokenActionsFlow(selectedPortfolio, addedToken)
|
||||||
.onEach {
|
.onEach { cryptoCurrencyData ->
|
||||||
tokenActionsData.emit(it)
|
tokenActionsData.emit(cryptoCurrencyData)
|
||||||
navigation.replaceAll(AddToPortfolioRoutes.TokenActions)
|
navigation.replaceAll(AddToPortfolioRoutes.TokenActions)
|
||||||
}
|
}
|
||||||
.onEmpty { finishFlow() }
|
.onEmpty { finishFlow() }
|
||||||
|
|
@ -192,8 +192,8 @@ internal class AddToPortfolioModel @Inject constructor(
|
||||||
callbackDelegate.onLaterClick.receiveAsFlow().first()
|
callbackDelegate.onLaterClick.receiveAsFlow().first()
|
||||||
finishFlow()
|
finishFlow()
|
||||||
}
|
}
|
||||||
.catch {
|
.catch { error ->
|
||||||
Timber.e(it)
|
Timber.e(error)
|
||||||
params.callback.onDismiss()
|
params.callback.onDismiss()
|
||||||
}
|
}
|
||||||
.launchIn(modelScope)
|
.launchIn(modelScope)
|
||||||
|
|
@ -213,14 +213,15 @@ internal class AddToPortfolioModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun changePortfolioNavigationFlow(data: AvailableToAddData): Flow<Unit> {
|
private fun changePortfolioNavigationFlow(data: AvailableToAddData): Flow<Unit> = flow {
|
||||||
val selectedAccount = selectedPortfolio.first().account.account.account.accountId
|
val selectedPortfolioValue = selectedPortfolio.first()
|
||||||
|
val selectedAccount = selectedPortfolioValue.account.account.account.accountId
|
||||||
portfolioSelectorController.selectAccount(selectedAccount)
|
portfolioSelectorController.selectAccount(selectedAccount)
|
||||||
val changedPortfolio = setupPortfolioFlow(data)
|
val changedPortfolio = setupPortfolioFlow(data)
|
||||||
.drop(1)
|
.drop(1)
|
||||||
.onEach { portfolio -> navigation.pushNew(routeToNetworkSelector(portfolio)) }
|
.onEach { portfolio -> navigation.pushNew(routeToNetworkSelector(portfolio)) }
|
||||||
val changedNetwork = setupNetworkFlow(changedPortfolio)
|
val changedNetwork = setupNetworkFlow(changedPortfolio)
|
||||||
return combine(
|
combine(
|
||||||
flow = changedPortfolio,
|
flow = changedPortfolio,
|
||||||
flow2 = changedNetwork,
|
flow2 = changedNetwork,
|
||||||
transform = { newPortfolio, newNetwork ->
|
transform = { newPortfolio, newNetwork ->
|
||||||
|
|
@ -228,7 +229,7 @@ internal class AddToPortfolioModel @Inject constructor(
|
||||||
selectedNetwork.tryEmit(newNetwork)
|
selectedNetwork.tryEmit(newNetwork)
|
||||||
navigation.popToFirst()
|
navigation.popToFirst()
|
||||||
},
|
},
|
||||||
)
|
).collect { emit(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupTokenActionsFlow(
|
private fun setupTokenActionsFlow(
|
||||||
|
|
@ -276,7 +277,7 @@ internal class AddToPortfolioModel @Inject constructor(
|
||||||
isAccountMode = isAccountMode,
|
isAccountMode = isAccountMode,
|
||||||
userWallet = availableToAddWallets.userWallet,
|
userWallet = availableToAddWallets.userWallet,
|
||||||
account = availableToAddAccount,
|
account = availableToAddAccount,
|
||||||
availableMorePortfolio = !data.isSinglePortfolio,
|
hasMorePortfoliosAvailable = !data.isSinglePortfolio,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -293,7 +294,7 @@ internal class AddToPortfolioModel @Inject constructor(
|
||||||
account = selectedPortfolio.account,
|
account = selectedPortfolio.account,
|
||||||
) ?: return@transform null,
|
) ?: return@transform null,
|
||||||
selectedNetwork = selectedNetwork,
|
selectedNetwork = selectedNetwork,
|
||||||
availableMoreNetwork = !selectedPortfolio.account.isSingleNetwork,
|
hasMoreNetworksAvailable = !selectedPortfolio.account.isSingleNetwork,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -78,8 +78,8 @@ internal class AddTokenModel @Inject constructor(
|
||||||
val account = selectedPortfolio.account.account.account
|
val account = selectedPortfolio.account.account.account
|
||||||
val accountId = account.accountId
|
val accountId = account.accountId
|
||||||
manageCryptoCurrenciesUseCase(accountId = accountId, add = cryptoCurrency)
|
manageCryptoCurrenciesUseCase(accountId = accountId, add = cryptoCurrency)
|
||||||
.onLeft {
|
.onLeft { error ->
|
||||||
processError(error = it)
|
processError(error = error)
|
||||||
uiState.value = um.toggleProgress(false)
|
uiState.value = um.toggleProgress(false)
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ internal class AddTokenUiBuilder @Inject constructor(
|
||||||
return AddTokenUM.Network(
|
return AddTokenUM.Network(
|
||||||
icon = selectedNetwork.cryptoCurrency.network.iconResId,
|
icon = selectedNetwork.cryptoCurrency.network.iconResId,
|
||||||
name = stringReference(selectedNetwork.cryptoCurrency.network.name),
|
name = stringReference(selectedNetwork.cryptoCurrency.network.name),
|
||||||
editable = selectedNetwork.availableMoreNetwork,
|
editable = selectedNetwork.hasMoreNetworksAvailable,
|
||||||
onClick = { params.callbacks.onChangeNetworkClick() },
|
onClick = { params.callbacks.onChangeNetworkClick() },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -54,7 +54,7 @@ internal class AddTokenUiBuilder @Inject constructor(
|
||||||
icon = accountIcon,
|
icon = accountIcon,
|
||||||
name = portfolioName,
|
name = portfolioName,
|
||||||
isAccountMode = selectedPortfolio.isAccountMode,
|
isAccountMode = selectedPortfolio.isAccountMode,
|
||||||
isMultiChoice = selectedPortfolio.availableMorePortfolio,
|
isMultiChoice = selectedPortfolio.hasMorePortfoliosAvailable,
|
||||||
onClick = { params.callbacks.onChangePortfolioClick() },
|
onClick = { params.callbacks.onChangePortfolioClick() },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,9 +82,9 @@ internal class ChooseNetworkModel @Inject constructor(
|
||||||
userWalletId = userWalletId,
|
userWalletId = userWalletId,
|
||||||
networkId = rawNetworkId,
|
networkId = rawNetworkId,
|
||||||
isMainNetwork = isMainNetwork,
|
isMainNetwork = isMainNetwork,
|
||||||
).getOrElse {
|
).getOrElse { error ->
|
||||||
Timber.e(
|
Timber.e(
|
||||||
it,
|
error,
|
||||||
"""
|
"""
|
||||||
Failed to check currency unsupported state
|
Failed to check currency unsupported state
|
||||||
|- User wallet ID: $userWalletId
|
|- User wallet ID: $userWalletId
|
||||||
|
|
@ -94,7 +94,7 @@ internal class ChooseNetworkModel @Inject constructor(
|
||||||
)
|
)
|
||||||
|
|
||||||
val message = SnackbarMessage(
|
val message = SnackbarMessage(
|
||||||
message = it.localizedMessage?.let(::stringReference) ?: resourceReference(R.string.common_error),
|
message = error.localizedMessage?.let(::stringReference) ?: resourceReference(R.string.common_error),
|
||||||
)
|
)
|
||||||
messageSender.send(message)
|
messageSender.send(message)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ internal class DefaultAddToPortfolioManager @AssistedInject constructor(
|
||||||
availableNetworks = availableNetworks,
|
availableNetworks = availableNetworks,
|
||||||
marketParams = token,
|
marketParams = token,
|
||||||
)
|
)
|
||||||
if (data.availableToAdd) {
|
if (data.isAvailableToAdd) {
|
||||||
State.AvailableToAdd(data)
|
State.AvailableToAdd(data)
|
||||||
} else {
|
} else {
|
||||||
State.NothingToAdd
|
State.NothingToAdd
|
||||||
|
|
|
||||||
|
|
@ -63,12 +63,12 @@ internal fun TokenActionsContent(state: TokenActionsUM, modifier: Modifier = Mod
|
||||||
.clip(RoundedCornerShape(TangemTheme.dimens.radius14))
|
.clip(RoundedCornerShape(TangemTheme.dimens.radius14))
|
||||||
.background(TangemTheme.colors.background.action),
|
.background(TangemTheme.colors.background.action),
|
||||||
) {
|
) {
|
||||||
state.quickActions.actions.fastForEach {
|
state.quickActions.actions.fastForEach { action ->
|
||||||
key(it.title) {
|
key(action.title) {
|
||||||
ActionRow(
|
ActionRow(
|
||||||
state = it,
|
state = action,
|
||||||
onClick = { state.quickActions.onQuickActionClick(it) },
|
onClick = { state.quickActions.onQuickActionClick(action) },
|
||||||
onLongClick = { state.quickActions.onQuickActionLongClick(it) },
|
onLongClick = { state.quickActions.onQuickActionLongClick(action) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -77,7 +77,7 @@ internal fun TokenActionsContent(state: TokenActionsUM, modifier: Modifier = Mod
|
||||||
SpacerH16()
|
SpacerH16()
|
||||||
|
|
||||||
SecondaryButton(
|
SecondaryButton(
|
||||||
modifier = modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
text = stringResourceSafe(R.string.common_later),
|
text = stringResourceSafe(R.string.common_later),
|
||||||
onClick = state.onLaterClick,
|
onClick = state.onLaterClick,
|
||||||
)
|
)
|
||||||
|
|
@ -102,7 +102,7 @@ private fun ActionRow(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.combinedClickable(
|
.combinedClickable(
|
||||||
onLongClick = onLongClickInternal.takeIf { state.longClickAvailable },
|
onLongClick = onLongClickInternal.takeIf { state.isLongClickAvailable },
|
||||||
onClick = {
|
onClick = {
|
||||||
hapticManager.perform(TangemHapticEffect.View.SegmentTick)
|
hapticManager.perform(TangemHapticEffect.View.SegmentTick)
|
||||||
onClick()
|
onClick()
|
||||||
|
|
@ -123,7 +123,7 @@ private fun ActionRow(
|
||||||
.size(36.dp)
|
.size(36.dp)
|
||||||
.drawWithContent {
|
.drawWithContent {
|
||||||
drawContent()
|
drawContent()
|
||||||
if (state is QuickActionUM.Exchange && state.showBadge) {
|
if (state is QuickActionUM.Exchange && state.shouldShowBadge) {
|
||||||
drawBadge(containerColor = containerColor, offset = 4.dp)
|
drawBadge(containerColor = containerColor, offset = 4.dp)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -191,7 +191,7 @@ private class TokenActionsContentPreviewProvider : PreviewParameterProvider<Toke
|
||||||
quickActions = PortfolioTokenUM.QuickActions(
|
quickActions = PortfolioTokenUM.QuickActions(
|
||||||
actions = persistentListOf(
|
actions = persistentListOf(
|
||||||
QuickActionUM.Buy,
|
QuickActionUM.Buy,
|
||||||
QuickActionUM.Exchange(showBadge = true),
|
QuickActionUM.Exchange(shouldShowBadge = true),
|
||||||
QuickActionUM.Receive,
|
QuickActionUM.Receive,
|
||||||
),
|
),
|
||||||
onQuickActionClick = {},
|
onQuickActionClick = {},
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,9 @@ internal class DefaultMarketsPortfolioComponent @AssistedInject constructor(
|
||||||
MarketsPortfolioRoute.AddToPortfolio -> addToPortfolioComponentFactory.create(
|
MarketsPortfolioRoute.AddToPortfolio -> addToPortfolioComponentFactory.create(
|
||||||
context = childByContext(componentContext),
|
context = childByContext(componentContext),
|
||||||
params = AddToPortfolioComponent.Params(
|
params = AddToPortfolioComponent.Params(
|
||||||
addToPortfolioManager = model.newAddToPortfolioManager!!,
|
addToPortfolioManager = requireNotNull(model.newAddToPortfolioManager) {
|
||||||
|
"newAddToPortfolioManager must be initialized"
|
||||||
|
},
|
||||||
callback = model.addToPortfolioCallback,
|
callback = model.addToPortfolioCallback,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import com.tangem.features.markets.portfolio.impl.ui.state.TokenActionsBSContent
|
||||||
|
|
||||||
internal class PortfolioAnalyticsEvent(
|
internal class PortfolioAnalyticsEvent(
|
||||||
event: String,
|
event: String,
|
||||||
params: Map<String, String> = mapOf(),
|
params: Map<String, String> = emptyMap(),
|
||||||
) : AnalyticsEvent(category = "Markets / Chart", event = event, params = params) {
|
) : AnalyticsEvent(category = "Markets / Chart", event = event, params = params) {
|
||||||
|
|
||||||
data class EventBuilder(
|
data class EventBuilder(
|
||||||
|
|
@ -52,7 +52,7 @@ internal class PortfolioAnalyticsEvent(
|
||||||
},
|
},
|
||||||
params = buildMap {
|
params = buildMap {
|
||||||
put("Token", token.symbol)
|
put("Token", token.symbol)
|
||||||
source?.let { put("Source", source) }
|
if (source != null) put("Source", source)
|
||||||
put("blockchain", blockchainName)
|
put("blockchain", blockchainName)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -80,11 +80,11 @@ internal class PortfolioDataLoader @Inject constructor(
|
||||||
YieldSupplyAvailability.Unavailable
|
YieldSupplyAvailability.Unavailable
|
||||||
}
|
}
|
||||||
getCryptoCurrencyActionsUseCase(wallet, status, yieldSupplyAvailability)
|
getCryptoCurrencyActionsUseCase(wallet, status, yieldSupplyAvailability)
|
||||||
.map {
|
.map { actionStates ->
|
||||||
PortfolioData.CryptoCurrencyData(
|
PortfolioData.CryptoCurrencyData(
|
||||||
userWallet = wallet,
|
userWallet = wallet,
|
||||||
status = status,
|
status = status,
|
||||||
actions = it.states,
|
actions = actionStates.states,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -93,18 +93,18 @@ internal class PortfolioDataLoader @Inject constructor(
|
||||||
combine(actionsFlows) { actions ->
|
combine(actionsFlows) { actions ->
|
||||||
walletsWithStatuses.mapValues { entry ->
|
walletsWithStatuses.mapValues { entry ->
|
||||||
entry.value.mapNotNull { status ->
|
entry.value.mapNotNull { status ->
|
||||||
actions.firstOrNull {
|
actions.firstOrNull { data ->
|
||||||
it.userWallet == entry.key && it.status == status
|
data.userWallet == entry.key && data.status == status
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.onEmpty {
|
}.onEmpty {
|
||||||
emit(
|
emit(
|
||||||
walletsWithStatuses.mapValues { (wallet, statuses) ->
|
walletsWithStatuses.mapValues { (wallet, statuses) ->
|
||||||
statuses.map {
|
statuses.map { status ->
|
||||||
PortfolioData.CryptoCurrencyData(
|
PortfolioData.CryptoCurrencyData(
|
||||||
userWallet = wallet,
|
userWallet = wallet,
|
||||||
status = it,
|
status = status,
|
||||||
actions = emptyList(),
|
actions = emptyList(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ internal class AddToPortfolioBSContentUMFactory(
|
||||||
artworks: Map<UserWalletId, UserWalletItemUM.ImageState>,
|
artworks: Map<UserWalletId, UserWalletItemUM.ImageState>,
|
||||||
): TangemBottomSheetConfig {
|
): TangemBottomSheetConfig {
|
||||||
return (currentState ?: TangemBottomSheetConfig.Empty).copy(
|
return (currentState ?: TangemBottomSheetConfig.Empty).copy(
|
||||||
isShown = portfolioUIData.portfolioBSVisibilityModel.addToPortfolioBSVisibility,
|
isShown = portfolioUIData.portfolioBSVisibilityModel.isAddToPortfolioBSVisible,
|
||||||
onDismissRequest = { onAddToPortfolioVisibilityChange(false) },
|
onDismissRequest = { onAddToPortfolioVisibilityChange(false) },
|
||||||
content = if (selectedWallet != null && alreadyAddedNetworks != null) {
|
content = if (selectedWallet != null && alreadyAddedNetworks != null) {
|
||||||
AddToPortfolioBSContentUM(
|
AddToPortfolioBSContentUM(
|
||||||
|
|
@ -76,8 +76,8 @@ internal class AddToPortfolioBSContentUMFactory(
|
||||||
alreadyAddedNetworks = alreadyAddedNetworks,
|
alreadyAddedNetworks = alreadyAddedNetworks,
|
||||||
onNetworkSwitchClick = onNetworkSwitchClick,
|
onNetworkSwitchClick = onNetworkSwitchClick,
|
||||||
).convert(value = token),
|
).convert(value = token),
|
||||||
isScanCardNotificationVisible = portfolioUIData.needColdWalletInteraction,
|
isScanCardNotificationVisible = portfolioUIData.shouldRequireColdWalletInteraction,
|
||||||
continueButtonEnabled = portfolioUIData.addToPortfolioData.isUserAddedNetworks(
|
isContinueButtonEnabled = portfolioUIData.addToPortfolioData.isUserAddedNetworks(
|
||||||
userWalletId = selectedWallet.walletId,
|
userWalletId = selectedWallet.walletId,
|
||||||
),
|
),
|
||||||
onContinueButtonClick = {
|
onContinueButtonClick = {
|
||||||
|
|
@ -90,7 +90,7 @@ internal class AddToPortfolioBSContentUMFactory(
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
walletSelectorConfig = createWalletSelectorBSConfig(
|
walletSelectorConfig = createWalletSelectorBSConfig(
|
||||||
isShow = portfolioUIData.portfolioBSVisibilityModel.walletSelectorBSVisibility,
|
isShow = portfolioUIData.portfolioBSVisibilityModel.isWalletSelectorBSVisible,
|
||||||
portfolioData = portfolioData,
|
portfolioData = portfolioData,
|
||||||
selectedWalletId = selectedWallet.walletId,
|
selectedWalletId = selectedWallet.walletId,
|
||||||
artworks = artworks,
|
artworks = artworks,
|
||||||
|
|
@ -136,9 +136,9 @@ internal class AddToPortfolioBSContentUMFactory(
|
||||||
val balance = portfolioData.walletsWithBalance[userWallet.walletId]
|
val balance = portfolioData.walletsWithBalance[userWallet.walletId]
|
||||||
|
|
||||||
UserWalletItemUMConverter(
|
UserWalletItemUMConverter(
|
||||||
onClick = {
|
onClick = { walletId ->
|
||||||
if (it != selectedWalletId) {
|
if (walletId != selectedWalletId) {
|
||||||
onAnotherWalletSelect(it)
|
onAnotherWalletSelect(walletId)
|
||||||
onWalletSelectorVisibilityChange(false)
|
onWalletSelectorVisibilityChange(false)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -124,8 +124,8 @@ internal class AddToPortfolioManager @Inject constructor(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
update {
|
update { currentMap ->
|
||||||
it.toMutableMap().apply {
|
currentMap.toMutableMap().apply {
|
||||||
this[userWalletId] = if (isAddAction) {
|
this[userWalletId] = if (isAddAction) {
|
||||||
this[userWalletId].orEmpty() + network
|
this[userWalletId].orEmpty() + network
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -77,8 +77,8 @@ internal class MarketsPortfolioModel @Inject constructor(
|
||||||
private val newMarketsPortfolioDelegateFactory: NewMarketsPortfolioDelegate.Factory,
|
private val newMarketsPortfolioDelegateFactory: NewMarketsPortfolioDelegate.Factory,
|
||||||
) : Model() {
|
) : Model() {
|
||||||
|
|
||||||
val state: StateFlow<MyPortfolioUM> get() = _state
|
|
||||||
private val _state: MutableStateFlow<MyPortfolioUM> = MutableStateFlow(value = MyPortfolioUM.Loading)
|
private val _state: MutableStateFlow<MyPortfolioUM> = MutableStateFlow(value = MyPortfolioUM.Loading)
|
||||||
|
val state: StateFlow<MyPortfolioUM> get() = _state
|
||||||
|
|
||||||
private val params = paramsContainer.require<MarketsPortfolioComponent.Params>()
|
private val params = paramsContainer.require<MarketsPortfolioComponent.Params>()
|
||||||
private val analyticsEventBuilder = PortfolioAnalyticsEvent.EventBuilder(
|
private val analyticsEventBuilder = PortfolioAnalyticsEvent.EventBuilder(
|
||||||
|
|
@ -99,6 +99,16 @@ internal class MarketsPortfolioModel @Inject constructor(
|
||||||
override fun onDismiss() = bottomSheetNavigation.dismiss()
|
override fun onDismiss() = bottomSheetNavigation.dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val currentAppCurrency = getSelectedAppCurrencyUseCase()
|
||||||
|
.map { maybeAppCurrency ->
|
||||||
|
maybeAppCurrency.getOrElse { AppCurrency.Default }
|
||||||
|
}
|
||||||
|
.stateIn(
|
||||||
|
scope = modelScope,
|
||||||
|
started = SharingStarted.Eagerly,
|
||||||
|
initialValue = AppCurrency.Default,
|
||||||
|
)
|
||||||
|
|
||||||
private val tokenActionsHandler = tokenActionsIntentsFactory.create(
|
private val tokenActionsHandler = tokenActionsIntentsFactory.create(
|
||||||
currentAppCurrency = Provider { currentAppCurrency.value },
|
currentAppCurrency = Provider { currentAppCurrency.value },
|
||||||
updateTokenReceiveBSConfig = { updateBlock ->
|
updateTokenReceiveBSConfig = { updateBlock ->
|
||||||
|
|
@ -109,10 +119,11 @@ internal class MarketsPortfolioModel @Inject constructor(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onHandleQuickAction = { handledAction ->
|
onHandleQuickAction = { handledAction ->
|
||||||
|
val currencyNetwork = handledAction.cryptoCurrencyData.status.currency.network
|
||||||
analyticsEventHandler.send(
|
analyticsEventHandler.send(
|
||||||
analyticsEventBuilder.quickActionClick(
|
analyticsEventBuilder.quickActionClick(
|
||||||
actionUM = handledAction.action,
|
actionUM = handledAction.action,
|
||||||
blockchainName = handledAction.cryptoCurrencyData.status.currency.network.name,
|
blockchainName = currencyNetwork.name,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
if (tokenReceiveFeatureToggle.isNewTokenReceiveEnabled) {
|
if (tokenReceiveFeatureToggle.isNewTokenReceiveEnabled) {
|
||||||
|
|
@ -121,16 +132,6 @@ internal class MarketsPortfolioModel @Inject constructor(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
private val currentAppCurrency = getSelectedAppCurrencyUseCase()
|
|
||||||
.map { maybeAppCurrency ->
|
|
||||||
maybeAppCurrency.getOrElse { AppCurrency.Default }
|
|
||||||
}
|
|
||||||
.stateIn(
|
|
||||||
scope = modelScope,
|
|
||||||
started = SharingStarted.Eagerly,
|
|
||||||
initialValue = AppCurrency.Default,
|
|
||||||
)
|
|
||||||
|
|
||||||
private val factory = MyPortfolioUMFactory(
|
private val factory = MyPortfolioUMFactory(
|
||||||
onAddClick = {
|
onAddClick = {
|
||||||
onAddToPortfolioBSVisibilityChange(isShow = true)
|
onAddToPortfolioBSVisibilityChange(isShow = true)
|
||||||
|
|
@ -145,8 +146,8 @@ internal class MarketsPortfolioModel @Inject constructor(
|
||||||
onAddToPortfolioVisibilityChange = ::onAddToPortfolioBSVisibilityChange,
|
onAddToPortfolioVisibilityChange = ::onAddToPortfolioBSVisibilityChange,
|
||||||
onWalletSelectorVisibilityChange = ::onWalletSelectorVisibilityChange,
|
onWalletSelectorVisibilityChange = ::onWalletSelectorVisibilityChange,
|
||||||
onNetworkSwitchClick = ::onNetworkSwitchClick,
|
onNetworkSwitchClick = ::onNetworkSwitchClick,
|
||||||
onAnotherWalletSelect = {
|
onAnotherWalletSelect = { walletId ->
|
||||||
onWalletSelect(it)
|
onWalletSelect(walletId)
|
||||||
// === Analytics ===
|
// === Analytics ===
|
||||||
analyticsEventHandler.send(
|
analyticsEventHandler.send(
|
||||||
analyticsEventBuilder.addToPortfolioWalletChanged(),
|
analyticsEventBuilder.addToPortfolioWalletChanged(),
|
||||||
|
|
@ -186,8 +187,8 @@ internal class MarketsPortfolioModel @Inject constructor(
|
||||||
scope = modelScope,
|
scope = modelScope,
|
||||||
token = params.token,
|
token = params.token,
|
||||||
tokenActionsHandler = tokenActionsHandler,
|
tokenActionsHandler = tokenActionsHandler,
|
||||||
buttonState = newAddToPortfolioManager.state.map {
|
buttonState = newAddToPortfolioManager.state.map { managerState ->
|
||||||
when (it) {
|
when (managerState) {
|
||||||
is NewAddToPortfolioManager.State.AvailableToAdd -> AddButtonState.Available
|
is NewAddToPortfolioManager.State.AvailableToAdd -> AddButtonState.Available
|
||||||
NewAddToPortfolioManager.State.Init -> AddButtonState.Loading
|
NewAddToPortfolioManager.State.Init -> AddButtonState.Loading
|
||||||
NewAddToPortfolioManager.State.NothingToAdd -> AddButtonState.Unavailable
|
NewAddToPortfolioManager.State.NothingToAdd -> AddButtonState.Unavailable
|
||||||
|
|
@ -230,8 +231,8 @@ internal class MarketsPortfolioModel @Inject constructor(
|
||||||
Timber.e("Failed to load selected wallet: $e")
|
Timber.e("Failed to load selected wallet: $e")
|
||||||
error("Failed to load selected wallet")
|
error("Failed to load selected wallet")
|
||||||
}
|
}
|
||||||
.onEach {
|
.onEach { wallet ->
|
||||||
selectedMultiWalletIdFlow.value = it.takeIf { it.isMultiCurrency }?.walletId
|
selectedMultiWalletIdFlow.value = wallet.takeIf { it.isMultiCurrency }?.walletId
|
||||||
}
|
}
|
||||||
.launchIn(modelScope)
|
.launchIn(modelScope)
|
||||||
}
|
}
|
||||||
|
|
@ -277,7 +278,10 @@ internal class MarketsPortfolioModel @Inject constructor(
|
||||||
portfolioBSVisibilityModel = portfolioBSVisibilityModel,
|
portfolioBSVisibilityModel = portfolioBSVisibilityModel,
|
||||||
selectedWalletId = selectedWalletId,
|
selectedWalletId = selectedWalletId,
|
||||||
addToPortfolioData = addToPortfolioData,
|
addToPortfolioData = addToPortfolioData,
|
||||||
needColdWalletInteraction = needColdWalletInteraction(selectedWalletId, addToPortfolioData),
|
shouldRequireColdWalletInteraction = needColdWalletInteraction(
|
||||||
|
selectedWalletId,
|
||||||
|
addToPortfolioData,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -333,9 +337,9 @@ internal class MarketsPortfolioModel @Inject constructor(
|
||||||
userWalletId = userWalletId,
|
userWalletId = userWalletId,
|
||||||
networkId = rawNetworkId,
|
networkId = rawNetworkId,
|
||||||
isMainNetwork = isMainNetwork,
|
isMainNetwork = isMainNetwork,
|
||||||
).getOrElse {
|
).getOrElse { error ->
|
||||||
Timber.e(
|
Timber.e(
|
||||||
it,
|
error,
|
||||||
"""
|
"""
|
||||||
Failed to check currency unsupported state
|
Failed to check currency unsupported state
|
||||||
|- User wallet ID: $userWalletId
|
|- User wallet ID: $userWalletId
|
||||||
|
|
@ -345,7 +349,7 @@ internal class MarketsPortfolioModel @Inject constructor(
|
||||||
)
|
)
|
||||||
|
|
||||||
val message = SnackbarMessage(
|
val message = SnackbarMessage(
|
||||||
message = it.localizedMessage
|
message = error.localizedMessage
|
||||||
?.let(::stringReference)
|
?.let(::stringReference)
|
||||||
?: resourceReference(R.string.common_error),
|
?: resourceReference(R.string.common_error),
|
||||||
)
|
)
|
||||||
|
|
@ -401,13 +405,13 @@ internal class MarketsPortfolioModel @Inject constructor(
|
||||||
|
|
||||||
private fun onAddToPortfolioBSVisibilityChange(isShow: Boolean) {
|
private fun onAddToPortfolioBSVisibilityChange(isShow: Boolean) {
|
||||||
portfolioBSVisibilityModelFlow.update {
|
portfolioBSVisibilityModelFlow.update {
|
||||||
it.copy(addToPortfolioBSVisibility = isShow, walletSelectorBSVisibility = false)
|
it.copy(isAddToPortfolioBSVisible = isShow, isWalletSelectorBSVisible = false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onWalletSelectorVisibilityChange(isShow: Boolean) {
|
private fun onWalletSelectorVisibilityChange(isShow: Boolean) {
|
||||||
portfolioBSVisibilityModelFlow.update {
|
portfolioBSVisibilityModelFlow.update {
|
||||||
it.copy(addToPortfolioBSVisibility = true, walletSelectorBSVisibility = isShow)
|
it.copy(isAddToPortfolioBSVisible = true, isWalletSelectorBSVisible = isShow)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -143,8 +143,8 @@ internal class MyPortfolioUMFactory(
|
||||||
): List<PortfolioData.CryptoCurrencyData> {
|
): List<PortfolioData.CryptoCurrencyData> {
|
||||||
val networkIds = networks.map(TokenMarketInfo.Network::networkId)
|
val networkIds = networks.map(TokenMarketInfo.Network::networkId)
|
||||||
|
|
||||||
return mapNotNull {
|
return mapNotNull { currencyData ->
|
||||||
it.takeIf { networkIds.contains(it.status.currency.network.backendId) }
|
currencyData.takeIf { networkIds.contains(it.status.currency.network.backendId) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -98,8 +98,8 @@ internal class NewMarketsPortfolioDelegate @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addFirstTokenFlow(): Flow<MyPortfolioUM> = buttonState.map {
|
private fun addFirstTokenFlow(): Flow<MyPortfolioUM> = buttonState.map { state ->
|
||||||
when (it) {
|
when (state) {
|
||||||
AddButtonState.Loading -> MyPortfolioUM.Loading
|
AddButtonState.Loading -> MyPortfolioUM.Loading
|
||||||
AddButtonState.Available -> MyPortfolioUM.AddFirstToken(
|
AddButtonState.Available -> MyPortfolioUM.AddFirstToken(
|
||||||
onAddClick = onAddClick,
|
onAddClick = onAddClick,
|
||||||
|
|
@ -120,7 +120,7 @@ internal class NewMarketsPortfolioDelegate @AssistedInject constructor(
|
||||||
}.flatten()
|
}.flatten()
|
||||||
|
|
||||||
val allAddedTokenActions =
|
val allAddedTokenActions =
|
||||||
portfolio.portfolios.map { portfolio -> portfolio.actionsFoAccountCurrencies() }.flatten()
|
portfolio.portfolios.map { portfolioItem -> portfolioItem.actionsFoAccountCurrencies() }.flatten()
|
||||||
|
|
||||||
return combine(
|
return combine(
|
||||||
flow = combine(allAddedTokenActions) { it.toMap() }.distinctUntilChanged(),
|
flow = combine(allAddedTokenActions) { it.toMap() }.distinctUntilChanged(),
|
||||||
|
|
@ -153,12 +153,12 @@ internal class NewMarketsPortfolioDelegate @AssistedInject constructor(
|
||||||
val currency = allAddedCurrency.first()
|
val currency = allAddedCurrency.first()
|
||||||
// find userWallet than have this single added token
|
// find userWallet than have this single added token
|
||||||
portfolio.portfolios
|
portfolio.portfolios
|
||||||
.find { it.accountsWithAdded.find { account -> account.addedCurrency.isNotEmpty() } != null }
|
.find { it.accountsWithAdded.any { account -> account.addedCurrency.isNotEmpty() } }
|
||||||
?.userWallet
|
?.userWallet
|
||||||
?.let { setOf(it.walletId to currency.currency.id) }
|
?.let { setOf(it.walletId to currency.currency.id) }
|
||||||
?: setOf()
|
.orEmpty()
|
||||||
}
|
}
|
||||||
else -> setOf()
|
else -> emptySet()
|
||||||
}
|
}
|
||||||
return MutableStateFlow(initValue)
|
return MutableStateFlow(initValue)
|
||||||
.also { this.expandedHolder = it }
|
.also { this.expandedHolder = it }
|
||||||
|
|
@ -166,10 +166,10 @@ internal class NewMarketsPortfolioDelegate @AssistedInject constructor(
|
||||||
|
|
||||||
private fun portfolioWithThisCurrencyFLow(): Flow<PortfoliosWithThisCurrency> =
|
private fun portfolioWithThisCurrencyFLow(): Flow<PortfoliosWithThisCurrency> =
|
||||||
allAccountSupplier().map { list -> list.map { it.addedAccountsFlow() } }.flatMapLatest { flows ->
|
allAccountSupplier().map { list -> list.map { it.addedAccountsFlow() } }.flatMapLatest { flows ->
|
||||||
combine(flows) {
|
combine(flows) { portfolios ->
|
||||||
PortfoliosWithThisCurrency(
|
PortfoliosWithThisCurrency(
|
||||||
currencyRawId = currencyRawId,
|
currencyRawId = currencyRawId,
|
||||||
portfolios = it.toList(),
|
portfolios = portfolios.toList(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.distinctUntilChanged()
|
}.distinctUntilChanged()
|
||||||
|
|
@ -237,8 +237,7 @@ internal class NewMarketsPortfolioDelegate @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
accountWithAdded.addedCurrency.forEach { currencyStatus ->
|
accountWithAdded.addedCurrency.forEach { currencyStatus ->
|
||||||
val actions = allActions[currencyStatus.currency]?.states
|
val actions = allActions[currencyStatus.currency]?.states.orEmpty()
|
||||||
?: emptyList()
|
|
||||||
val value = PortfolioData.CryptoCurrencyData(
|
val value = PortfolioData.CryptoCurrencyData(
|
||||||
userWallet = userWallet,
|
userWallet = userWallet,
|
||||||
status = currencyStatus,
|
status = currencyStatus,
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,12 @@ package com.tangem.features.markets.portfolio.impl.model
|
||||||
/**
|
/**
|
||||||
* Model for portfolio bottom sheet visibility
|
* Model for portfolio bottom sheet visibility
|
||||||
*
|
*
|
||||||
* @property addToPortfolioBSVisibility visibility of add to portfolio bottom sheet
|
* @property isAddToPortfolioBSVisible visibility of add to portfolio bottom sheet
|
||||||
* @property walletSelectorBSVisibility visibility of wallet selector bottom sheet
|
* @property isWalletSelectorBSVisible visibility of wallet selector bottom sheet
|
||||||
*
|
*
|
||||||
[REDACTED_AUTHOR]
|
[REDACTED_AUTHOR]
|
||||||
*/
|
*/
|
||||||
internal data class PortfolioBSVisibilityModel(
|
internal data class PortfolioBSVisibilityModel(
|
||||||
val addToPortfolioBSVisibility: Boolean = false,
|
val isAddToPortfolioBSVisible: Boolean = false,
|
||||||
val walletSelectorBSVisibility: Boolean = false,
|
val isWalletSelectorBSVisible: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|
@ -95,8 +95,8 @@ internal class PortfolioTokenUMConverter(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onQuickActionLongClick = {
|
onQuickActionLongClick = { quickAction ->
|
||||||
if (it == QuickActionUM.Receive) {
|
if (quickAction == QuickActionUM.Receive) {
|
||||||
tokenActionsHandler.handle(
|
tokenActionsHandler.handle(
|
||||||
action = TokenActionsBSContentUM.Action.CopyAddress,
|
action = TokenActionsBSContentUM.Action.CopyAddress,
|
||||||
cryptoCurrencyData = cryptoData,
|
cryptoCurrencyData = cryptoData,
|
||||||
|
|
@ -111,7 +111,9 @@ internal class PortfolioTokenUMConverter(
|
||||||
if (action.unavailabilityReason == ScenarioUnavailabilityReason.None) {
|
if (action.unavailabilityReason == ScenarioUnavailabilityReason.None) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is TokenActionsState.ActionState.Buy -> QuickActionUM.Buy
|
is TokenActionsState.ActionState.Buy -> QuickActionUM.Buy
|
||||||
is TokenActionsState.ActionState.Swap -> QuickActionUM.Exchange(showBadge = action.showBadge)
|
is TokenActionsState.ActionState.Swap -> QuickActionUM.Exchange(
|
||||||
|
shouldShowBadge = action.showBadge,
|
||||||
|
)
|
||||||
is TokenActionsState.ActionState.Receive -> QuickActionUM.Receive
|
is TokenActionsState.ActionState.Receive -> QuickActionUM.Receive
|
||||||
is TokenActionsState.ActionState.Stake -> QuickActionUM.Stake
|
is TokenActionsState.ActionState.Stake -> QuickActionUM.Stake
|
||||||
is TokenActionsState.ActionState.YieldMode -> QuickActionUM.YieldMode(apy = action.apy)
|
is TokenActionsState.ActionState.YieldMode -> QuickActionUM.YieldMode(apy = action.apy)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import com.tangem.domain.models.wallet.UserWalletId
|
||||||
* @property portfolioBSVisibilityModel portfolio bottom sheet visibility model
|
* @property portfolioBSVisibilityModel portfolio bottom sheet visibility model
|
||||||
* @property selectedWalletId selected wallet id
|
* @property selectedWalletId selected wallet id
|
||||||
* @property addToPortfolioData add to portfolio data
|
* @property addToPortfolioData add to portfolio data
|
||||||
* @property needColdWalletInteraction flag that indicates if user has missed derivations and has a cold wallet
|
* @property shouldRequireColdWalletInteraction flag that indicates if user has missed derivations and has a cold wallet
|
||||||
*
|
*
|
||||||
[REDACTED_AUTHOR]
|
[REDACTED_AUTHOR]
|
||||||
*/
|
*/
|
||||||
|
|
@ -16,5 +16,5 @@ internal data class PortfolioUIData(
|
||||||
val portfolioBSVisibilityModel: PortfolioBSVisibilityModel,
|
val portfolioBSVisibilityModel: PortfolioBSVisibilityModel,
|
||||||
val selectedWalletId: UserWalletId?,
|
val selectedWalletId: UserWalletId?,
|
||||||
val addToPortfolioData: AddToPortfolioManager.AddToPortfolioData,
|
val addToPortfolioData: AddToPortfolioManager.AddToPortfolioData,
|
||||||
val needColdWalletInteraction: Boolean,
|
val shouldRequireColdWalletInteraction: Boolean,
|
||||||
)
|
)
|
||||||
|
|
@ -73,14 +73,14 @@ internal class TokenActionsHandler @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleDemoMode(action: TokenActionsBSContentUM.Action, userWallet: UserWallet.Cold): Boolean {
|
private fun handleDemoMode(action: TokenActionsBSContentUM.Action, userWallet: UserWallet.Cold): Boolean {
|
||||||
val demoCard = isDemoCardUseCase.invoke(userWallet.cardId)
|
val isDemoCard = isDemoCardUseCase.invoke(userWallet.cardId)
|
||||||
val needShowDemoWarning = demoCard && disabledActionsInDemoMode.contains(action)
|
val shouldShowDemoWarning = isDemoCard && disabledActionsInDemoMode.contains(action)
|
||||||
|
|
||||||
if (needShowDemoWarning) {
|
if (shouldShowDemoWarning) {
|
||||||
showDemoModeWarning()
|
showDemoModeWarning()
|
||||||
}
|
}
|
||||||
|
|
||||||
return needShowDemoWarning
|
return shouldShowDemoWarning
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showDemoModeWarning() {
|
private fun showDemoModeWarning() {
|
||||||
|
|
|
||||||
|
|
@ -93,10 +93,10 @@ internal class TokensPortfolioUMConverter(
|
||||||
|
|
||||||
private fun toggleQuickActions(cryptoData: PortfolioData.CryptoCurrencyData) {
|
private fun toggleQuickActions(cryptoData: PortfolioData.CryptoCurrencyData) {
|
||||||
updateTokens { tokenList ->
|
updateTokens { tokenList ->
|
||||||
tokenList.map {
|
tokenList.map { token ->
|
||||||
it.copy(
|
token.copy(
|
||||||
isQuickActionsShown = if (it.matchWith(cryptoData)) {
|
isQuickActionsShown = if (token.matchWith(cryptoData)) {
|
||||||
!it.isQuickActionsShown
|
!token.isQuickActionsShown
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -54,13 +54,13 @@ internal fun AddToPortfolioBottomSheet(config: TangemBottomSheetConfig) {
|
||||||
containerColor = TangemTheme.colors.background.tertiary,
|
containerColor = TangemTheme.colors.background.tertiary,
|
||||||
addBottomInsets = false,
|
addBottomInsets = false,
|
||||||
titleText = resourceReference(R.string.common_add_to_portfolio),
|
titleText = resourceReference(R.string.common_add_to_portfolio),
|
||||||
) {
|
) { contentState ->
|
||||||
Content(
|
Content(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
state = it,
|
state = contentState,
|
||||||
)
|
)
|
||||||
|
|
||||||
WalletSelectorBottomSheet(it.walletSelectorConfig)
|
WalletSelectorBottomSheet(contentState.walletSelectorConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,7 +130,7 @@ private fun Content(state: AddToPortfolioBSContentUM, modifier: Modifier = Modif
|
||||||
.onGloballyPositioned {
|
.onGloballyPositioned {
|
||||||
continueButtonAreaHeight = it.size.height
|
continueButtonAreaHeight = it.size.height
|
||||||
},
|
},
|
||||||
enabled = state.continueButtonEnabled,
|
enabled = state.isContinueButtonEnabled,
|
||||||
isTangemIconVisible = state.isScanCardNotificationVisible,
|
isTangemIconVisible = state.isScanCardNotificationVisible,
|
||||||
onClick = state.onContinueButtonClick,
|
onClick = state.onContinueButtonClick,
|
||||||
)
|
)
|
||||||
|
|
@ -355,11 +355,11 @@ private fun PreviewContentTestOnDevice(
|
||||||
isScanCardNotificationVisible = !contentState.isScanCardNotificationVisible,
|
isScanCardNotificationVisible = !contentState.isScanCardNotificationVisible,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
continueButtonEnabled = true,
|
isContinueButtonEnabled = true,
|
||||||
selectedWallet = content.selectedWallet.copy(
|
selectedWallet = content.selectedWallet.copy(
|
||||||
onClick = {
|
onClick = {
|
||||||
contentState = contentState.copy(
|
contentState = contentState.copy(
|
||||||
continueButtonEnabled = !contentState.continueButtonEnabled,
|
isContinueButtonEnabled = !contentState.isContinueButtonEnabled,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -31,11 +31,11 @@ internal fun PortfolioItem(state: PortfolioTokenUM, lastInList: Boolean, modifie
|
||||||
val tokenItemState = remember(state.tokenItemState) {
|
val tokenItemState = remember(state.tokenItemState) {
|
||||||
when (state.tokenItemState) {
|
when (state.tokenItemState) {
|
||||||
is TokenItemState.Content -> state.tokenItemState.copy(
|
is TokenItemState.Content -> state.tokenItemState.copy(
|
||||||
onItemClick = {
|
onItemClick = { cryptoCurrency ->
|
||||||
val onClick = state.tokenItemState.onItemClick
|
val onClick = state.tokenItemState.onItemClick
|
||||||
if (onClick != null) {
|
if (onClick != null) {
|
||||||
hapticManager.perform(TangemHapticEffect.View.ContextClick)
|
hapticManager.perform(TangemHapticEffect.View.ContextClick)
|
||||||
onClick.invoke(it)
|
onClick.invoke(cryptoCurrency)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -73,10 +73,10 @@ internal fun PortfolioItem(state: PortfolioTokenUM, lastInList: Boolean, modifie
|
||||||
@Composable
|
@Composable
|
||||||
private fun Preview(@PreviewParameter(PortfolioTokenUMProvider::class) tokenUM: PortfolioTokenUM) {
|
private fun Preview(@PreviewParameter(PortfolioTokenUMProvider::class) tokenUM: PortfolioTokenUM) {
|
||||||
TangemThemePreview {
|
TangemThemePreview {
|
||||||
var quickActionsShown by remember { mutableStateOf(value = false) }
|
var areQuickActionsShown by remember { mutableStateOf(value = false) }
|
||||||
|
|
||||||
val onItemClick = {
|
val onItemClick = {
|
||||||
quickActionsShown = quickActionsShown.not()
|
areQuickActionsShown = areQuickActionsShown.not()
|
||||||
}
|
}
|
||||||
|
|
||||||
PortfolioItem(
|
PortfolioItem(
|
||||||
|
|
@ -87,7 +87,7 @@ private fun Preview(@PreviewParameter(PortfolioTokenUMProvider::class) tokenUM:
|
||||||
is TokenItemState.Unreachable -> tokenUM.tokenItemState.copy(onItemClick = { onItemClick() })
|
is TokenItemState.Unreachable -> tokenUM.tokenItemState.copy(onItemClick = { onItemClick() })
|
||||||
else -> tokenUM.tokenItemState
|
else -> tokenUM.tokenItemState
|
||||||
},
|
},
|
||||||
isQuickActionsShown = quickActionsShown,
|
isQuickActionsShown = areQuickActionsShown,
|
||||||
),
|
),
|
||||||
lastInList = true,
|
lastInList = true,
|
||||||
)
|
)
|
||||||
|
|
@ -111,7 +111,8 @@ private class PortfolioTokenUMProvider : CollectionPreviewParameterProvider<Port
|
||||||
tokenUM.copy(
|
tokenUM.copy(
|
||||||
tokenItemState = tokenUM.tokenItemState.copy(
|
tokenItemState = tokenUM.tokenItemState.copy(
|
||||||
fiatAmountState = contentFiatAmount.copy(text = DASH_SIGN),
|
fiatAmountState = contentFiatAmount.copy(text = DASH_SIGN),
|
||||||
subtitle2State = (tokenUM.tokenItemState.subtitle2State as TokenItemState.Subtitle2State.TextContent)
|
subtitle2State = (tokenUM.tokenItemState.subtitle2State as? TokenItemState.Subtitle2State.TextContent
|
||||||
|
?: error("subtitle2State must be TextContent for preview"))
|
||||||
.copy(text = DASH_SIGN),
|
.copy(text = DASH_SIGN),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -148,6 +149,7 @@ private class PortfolioTokenUMProvider : CollectionPreviewParameterProvider<Port
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val tokenUM = PreviewMyPortfolioUMProvider().sampleToken
|
val tokenUM = PreviewMyPortfolioUMProvider().sampleToken
|
||||||
val contentFiatAmount = tokenUM.tokenItemState.fiatAmountState as TokenFiatAmountState.Content
|
val contentFiatAmount = tokenUM.tokenItemState.fiatAmountState as? TokenFiatAmountState.Content
|
||||||
|
?: error("fiatAmountState must be Content for preview")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -50,14 +50,15 @@ internal fun PortfolioQuickActions(
|
||||||
visible = isVisible,
|
visible = isVisible,
|
||||||
enter = expandVertically(expandFrom = Alignment.Top),
|
enter = expandVertically(expandFrom = Alignment.Top),
|
||||||
exit = shrinkVertically(shrinkTowards = Alignment.Top),
|
exit = shrinkVertically(shrinkTowards = Alignment.Top),
|
||||||
|
modifier = modifier,
|
||||||
) {
|
) {
|
||||||
Column(modifier = modifier) {
|
Column(modifier = Modifier) {
|
||||||
actions.fastForEach { action ->
|
actions.fastForEach { action ->
|
||||||
LineSeparator()
|
LineSeparator()
|
||||||
QuickActionItem(
|
QuickActionItem(
|
||||||
state = action,
|
state = action,
|
||||||
onClick = { onActionClick(action) },
|
onClick = { onActionClick(action) },
|
||||||
onLongClick = { onActionLongClick(action) }.takeIf { action.longClickAvailable },
|
onLongClick = { onActionLongClick(action) }.takeIf { action.isLongClickAvailable },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -174,7 +175,7 @@ private fun AnimatedVisibilityScope.QuickActionIcon(state: QuickActionUM) {
|
||||||
.size(TangemTheme.dimens.size32)
|
.size(TangemTheme.dimens.size32)
|
||||||
.drawWithContent {
|
.drawWithContent {
|
||||||
drawContent()
|
drawContent()
|
||||||
if (state is QuickActionUM.Exchange && state.showBadge) {
|
if (state is QuickActionUM.Exchange && state.shouldShowBadge) {
|
||||||
drawBadge(containerColor = containerColor, offset = 4.dp)
|
drawBadge(containerColor = containerColor, offset = 4.dp)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -215,7 +216,7 @@ private fun Preview() {
|
||||||
PortfolioQuickActions(
|
PortfolioQuickActions(
|
||||||
actions = persistentListOf(
|
actions = persistentListOf(
|
||||||
QuickActionUM.Buy,
|
QuickActionUM.Buy,
|
||||||
QuickActionUM.Exchange(showBadge = true),
|
QuickActionUM.Exchange(shouldShowBadge = true),
|
||||||
QuickActionUM.Receive,
|
QuickActionUM.Receive,
|
||||||
),
|
),
|
||||||
isVisible = isVisible,
|
isVisible = isVisible,
|
||||||
|
|
@ -236,7 +237,7 @@ private fun PreviewRtl() {
|
||||||
PortfolioQuickActions(
|
PortfolioQuickActions(
|
||||||
actions = persistentListOf(
|
actions = persistentListOf(
|
||||||
QuickActionUM.Buy,
|
QuickActionUM.Buy,
|
||||||
QuickActionUM.Exchange(showBadge = true),
|
QuickActionUM.Exchange(shouldShowBadge = true),
|
||||||
QuickActionUM.Receive,
|
QuickActionUM.Receive,
|
||||||
),
|
),
|
||||||
isVisible = true,
|
isVisible = true,
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ internal class PreviewAddToPortfolioBSContentProvider : PreviewParameterProvider
|
||||||
),
|
),
|
||||||
isScanCardNotificationVisible = true,
|
isScanCardNotificationVisible = true,
|
||||||
isWalletBlockVisible = true,
|
isWalletBlockVisible = true,
|
||||||
continueButtonEnabled = true,
|
isContinueButtonEnabled = true,
|
||||||
onContinueButtonClick = {},
|
onContinueButtonClick = {},
|
||||||
walletSelectorConfig = TangemBottomSheetConfig.Empty,
|
walletSelectorConfig = TangemBottomSheetConfig.Empty,
|
||||||
),
|
),
|
||||||
|
|
@ -78,7 +78,7 @@ internal class PreviewAddToPortfolioBSContentProvider : PreviewParameterProvider
|
||||||
),
|
),
|
||||||
isScanCardNotificationVisible = true,
|
isScanCardNotificationVisible = true,
|
||||||
isWalletBlockVisible = false,
|
isWalletBlockVisible = false,
|
||||||
continueButtonEnabled = false,
|
isContinueButtonEnabled = false,
|
||||||
onContinueButtonClick = {},
|
onContinueButtonClick = {},
|
||||||
walletSelectorConfig = TangemBottomSheetConfig.Empty,
|
walletSelectorConfig = TangemBottomSheetConfig.Empty,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,76 @@ import java.util.UUID
|
||||||
|
|
||||||
internal class PreviewMyPortfolioUMProvider : PreviewParameterProvider<MyPortfolioUM> {
|
internal class PreviewMyPortfolioUMProvider : PreviewParameterProvider<MyPortfolioUM> {
|
||||||
|
|
||||||
|
val sampleToken
|
||||||
|
get() = PortfolioTokenUM(
|
||||||
|
tokenItemState = TokenItemState.Content(
|
||||||
|
id = UUID.randomUUID().toString(),
|
||||||
|
iconState = CurrencyIconState.Locked,
|
||||||
|
titleState = TokenItemState.TitleState.Content(text = stringReference(value = "My wallet")),
|
||||||
|
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "486,65 \$"),
|
||||||
|
subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "733,71097 MATIC"),
|
||||||
|
subtitleState = TokenItemState.SubtitleState.TextContent(
|
||||||
|
value = stringReference(value = "XRP Ledger token"),
|
||||||
|
),
|
||||||
|
onItemClick = {},
|
||||||
|
onItemLongClick = {},
|
||||||
|
),
|
||||||
|
isQuickActionsShown = false,
|
||||||
|
quickActions = PortfolioTokenUM.QuickActions(
|
||||||
|
actions = persistentListOf(
|
||||||
|
QuickActionUM.Buy,
|
||||||
|
QuickActionUM.Exchange(shouldShowBadge = true),
|
||||||
|
QuickActionUM.Receive,
|
||||||
|
),
|
||||||
|
onQuickActionClick = {},
|
||||||
|
onQuickActionLongClick = {},
|
||||||
|
),
|
||||||
|
isBalanceHidden = false,
|
||||||
|
walletId = UserWalletId(""),
|
||||||
|
)
|
||||||
|
|
||||||
|
val walletHeader
|
||||||
|
get() = WalletHeader(
|
||||||
|
id = UUID.randomUUID().toString(),
|
||||||
|
name = stringReference("Wallet 1"),
|
||||||
|
)
|
||||||
|
|
||||||
|
val walletPortfolioHeader
|
||||||
|
get() = PortfolioHeader(
|
||||||
|
state = AccountTitleUM.Text(title = stringReference("Wallet 1")),
|
||||||
|
id = UUID.randomUUID().toString(),
|
||||||
|
)
|
||||||
|
|
||||||
|
val accountHeader
|
||||||
|
get() = PortfolioHeader(
|
||||||
|
state = AccountTitleUM.Account(
|
||||||
|
icon = AccountIconPreviewData.randomAccountIcon(),
|
||||||
|
name = stringReference("Main Account"),
|
||||||
|
prefixText = TextReference.EMPTY,
|
||||||
|
),
|
||||||
|
id = UUID.randomUUID().toString(),
|
||||||
|
)
|
||||||
|
val coinIconState
|
||||||
|
get() = CurrencyIconState.CoinIcon(
|
||||||
|
url = null,
|
||||||
|
fallbackResId = com.tangem.core.ui.R.drawable.img_polygon_22,
|
||||||
|
isGrayscale = false,
|
||||||
|
shouldShowCustomBadge = false,
|
||||||
|
)
|
||||||
|
val accountToken
|
||||||
|
get() = sampleToken.copy(
|
||||||
|
tokenItemState = TokenItemState.Content(
|
||||||
|
id = UUID.randomUUID().toString(),
|
||||||
|
iconState = coinIconState,
|
||||||
|
titleState = TokenItemState.TitleState.Content(text = stringReference(value = "Polygon")),
|
||||||
|
fiatAmountState = FiatAmountState.Content(text = "321 $"),
|
||||||
|
subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "5,412 MATIC"),
|
||||||
|
subtitleState = TokenItemState.SubtitleState.TextContent(stringReference(value = "Token")),
|
||||||
|
onItemClick = {},
|
||||||
|
onItemLongClick = {},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
override val values: Sequence<MyPortfolioUM>
|
override val values: Sequence<MyPortfolioUM>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
MyPortfolioUM.Tokens(
|
MyPortfolioUM.Tokens(
|
||||||
|
|
@ -77,74 +147,4 @@ internal class PreviewMyPortfolioUMProvider : PreviewParameterProvider<MyPortfol
|
||||||
MyPortfolioUM.Unavailable,
|
MyPortfolioUM.Unavailable,
|
||||||
MyPortfolioUM.UnavailableForWallet,
|
MyPortfolioUM.UnavailableForWallet,
|
||||||
)
|
)
|
||||||
|
|
||||||
val walletHeader
|
|
||||||
get() = WalletHeader(
|
|
||||||
id = UUID.randomUUID().toString(),
|
|
||||||
name = stringReference("Wallet 1"),
|
|
||||||
)
|
|
||||||
|
|
||||||
val walletPortfolioHeader
|
|
||||||
get() = PortfolioHeader(
|
|
||||||
state = AccountTitleUM.Text(title = stringReference("Wallet 1")),
|
|
||||||
id = UUID.randomUUID().toString(),
|
|
||||||
)
|
|
||||||
|
|
||||||
val accountHeader
|
|
||||||
get() = PortfolioHeader(
|
|
||||||
state = AccountTitleUM.Account(
|
|
||||||
icon = AccountIconPreviewData.randomAccountIcon(),
|
|
||||||
name = stringReference("Main Account"),
|
|
||||||
prefixText = TextReference.EMPTY,
|
|
||||||
),
|
|
||||||
id = UUID.randomUUID().toString(),
|
|
||||||
)
|
|
||||||
val coinIconState
|
|
||||||
get() = CurrencyIconState.CoinIcon(
|
|
||||||
url = null,
|
|
||||||
fallbackResId = com.tangem.core.ui.R.drawable.img_polygon_22,
|
|
||||||
isGrayscale = false,
|
|
||||||
shouldShowCustomBadge = false,
|
|
||||||
)
|
|
||||||
val accountToken
|
|
||||||
get() = sampleToken.copy(
|
|
||||||
tokenItemState = TokenItemState.Content(
|
|
||||||
id = UUID.randomUUID().toString(),
|
|
||||||
iconState = coinIconState,
|
|
||||||
titleState = TokenItemState.TitleState.Content(text = stringReference(value = "Polygon")),
|
|
||||||
fiatAmountState = FiatAmountState.Content(text = "321 $"),
|
|
||||||
subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "5,412 MATIC"),
|
|
||||||
subtitleState = TokenItemState.SubtitleState.TextContent(stringReference(value = "Token")),
|
|
||||||
onItemClick = {},
|
|
||||||
onItemLongClick = {},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
val sampleToken
|
|
||||||
get() = PortfolioTokenUM(
|
|
||||||
tokenItemState = TokenItemState.Content(
|
|
||||||
id = UUID.randomUUID().toString(),
|
|
||||||
iconState = CurrencyIconState.Locked,
|
|
||||||
titleState = TokenItemState.TitleState.Content(text = stringReference(value = "My wallet")),
|
|
||||||
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "486,65 \$"),
|
|
||||||
subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "733,71097 MATIC"),
|
|
||||||
subtitleState = TokenItemState.SubtitleState.TextContent(
|
|
||||||
value = stringReference(value = "XRP Ledger token"),
|
|
||||||
),
|
|
||||||
onItemClick = {},
|
|
||||||
onItemLongClick = {},
|
|
||||||
),
|
|
||||||
isQuickActionsShown = false,
|
|
||||||
quickActions = PortfolioTokenUM.QuickActions(
|
|
||||||
actions = persistentListOf(
|
|
||||||
QuickActionUM.Buy,
|
|
||||||
QuickActionUM.Exchange(showBadge = true),
|
|
||||||
QuickActionUM.Receive,
|
|
||||||
),
|
|
||||||
onQuickActionClick = {},
|
|
||||||
onQuickActionLongClick = {},
|
|
||||||
),
|
|
||||||
isBalanceHidden = false,
|
|
||||||
walletId = UserWalletId(""),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
@ -9,7 +9,7 @@ internal data class AddToPortfolioBSContentUM(
|
||||||
val selectNetworkUM: SelectNetworkUM,
|
val selectNetworkUM: SelectNetworkUM,
|
||||||
val isWalletBlockVisible: Boolean,
|
val isWalletBlockVisible: Boolean,
|
||||||
val isScanCardNotificationVisible: Boolean,
|
val isScanCardNotificationVisible: Boolean,
|
||||||
val continueButtonEnabled: Boolean,
|
val isContinueButtonEnabled: Boolean,
|
||||||
val onContinueButtonClick: () -> Unit,
|
val onContinueButtonClick: () -> Unit,
|
||||||
val walletSelectorConfig: TangemBottomSheetConfig,
|
val walletSelectorConfig: TangemBottomSheetConfig,
|
||||||
) : TangemBottomSheetConfigContent
|
) : TangemBottomSheetConfigContent
|
||||||
|
|
@ -12,7 +12,7 @@ internal sealed class QuickActionUM(
|
||||||
val title: TextReference,
|
val title: TextReference,
|
||||||
val description: TextReference,
|
val description: TextReference,
|
||||||
@DrawableRes val icon: Int,
|
@DrawableRes val icon: Int,
|
||||||
val longClickAvailable: Boolean = false,
|
val isLongClickAvailable: Boolean = false,
|
||||||
) {
|
) {
|
||||||
data object Buy : QuickActionUM(
|
data object Buy : QuickActionUM(
|
||||||
title = resourceReference(R.string.common_buy),
|
title = resourceReference(R.string.common_buy),
|
||||||
|
|
@ -21,7 +21,7 @@ internal sealed class QuickActionUM(
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Exchange(
|
data class Exchange(
|
||||||
val showBadge: Boolean,
|
val shouldShowBadge: Boolean,
|
||||||
) : QuickActionUM(
|
) : QuickActionUM(
|
||||||
title = resourceReference(R.string.common_exchange),
|
title = resourceReference(R.string.common_exchange),
|
||||||
description = resourceReference(R.string.exсhange_token_description),
|
description = resourceReference(R.string.exсhange_token_description),
|
||||||
|
|
@ -32,7 +32,7 @@ internal sealed class QuickActionUM(
|
||||||
title = resourceReference(R.string.common_receive),
|
title = resourceReference(R.string.common_receive),
|
||||||
description = resourceReference(R.string.receive_token_description),
|
description = resourceReference(R.string.receive_token_description),
|
||||||
icon = R.drawable.ic_arrow_down_24,
|
icon = R.drawable.ic_arrow_down_24,
|
||||||
longClickAvailable = true,
|
isLongClickAvailable = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
data object Stake : QuickActionUM(
|
data object Stake : QuickActionUM(
|
||||||
|
|
|
||||||
|
|
@ -79,8 +79,8 @@ internal class TokenMarketBlockModel @Inject constructor(
|
||||||
currencyID = params.cryptoCurrency.id,
|
currencyID = params.cryptoCurrency.id,
|
||||||
interval = PriceChangeInterval.H24,
|
interval = PriceChangeInterval.H24,
|
||||||
refresh = true,
|
refresh = true,
|
||||||
).collect {
|
).collect { quotesOption ->
|
||||||
it.onSome { res ->
|
quotesOption.onSome { res ->
|
||||||
quotesState = QuotesState(
|
quotesState = QuotesState(
|
||||||
currentPrice = res.fiatRate,
|
currentPrice = res.fiatRate,
|
||||||
h24Percent = res.priceChange,
|
h24Percent = res.priceChange,
|
||||||
|
|
|
||||||
|
|
@ -107,12 +107,12 @@ internal class DefaultMarketsTokenListComponent @AssistedInject constructor(
|
||||||
Scaffold(
|
Scaffold(
|
||||||
contentWindowInsets = WindowInsetsZero,
|
contentWindowInsets = WindowInsetsZero,
|
||||||
containerColor = TangemTheme.colors.background.primary,
|
containerColor = TangemTheme.colors.background.primary,
|
||||||
) {
|
) { paddingValues ->
|
||||||
MarketsListWithBack(
|
MarketsListWithBack(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.statusBarsPadding()
|
.statusBarsPadding()
|
||||||
.imePadding()
|
.imePadding()
|
||||||
.padding(it),
|
.padding(paddingValues),
|
||||||
state = state,
|
state = state,
|
||||||
bottomSheetState = BottomSheetState.EXPANDED,
|
bottomSheetState = BottomSheetState.EXPANDED,
|
||||||
onBackClick = router::pop,
|
onBackClick = router::pop,
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import com.tangem.features.markets.tokenlist.impl.ui.state.SortByTypeUM
|
||||||
|
|
||||||
internal sealed class MarketsListAnalyticsEvent(
|
internal sealed class MarketsListAnalyticsEvent(
|
||||||
event: String,
|
event: String,
|
||||||
params: Map<String, String> = mapOf(),
|
params: Map<String, String> = emptyMap(),
|
||||||
) : AnalyticsEvent(category = "Markets", event = event, params = params) {
|
) : AnalyticsEvent(category = "Markets", event = event, params = params) {
|
||||||
|
|
||||||
class BottomSheetOpened : MarketsListAnalyticsEvent(event = "Markets Screen Opened")
|
class BottomSheetOpened : MarketsListAnalyticsEvent(event = "Markets Screen Opened")
|
||||||
|
|
@ -39,10 +39,10 @@ internal sealed class MarketsListAnalyticsEvent(
|
||||||
|
|
||||||
class YieldModeMoreInfoClicked : MarketsListAnalyticsEvent(event = "Yield Mode More Info")
|
class YieldModeMoreInfoClicked : MarketsListAnalyticsEvent(event = "Yield Mode More Info")
|
||||||
|
|
||||||
data class TokenSearched(val tokenFound: Boolean) : MarketsListAnalyticsEvent(
|
data class TokenSearched(val wasTokenFound: Boolean) : MarketsListAnalyticsEvent(
|
||||||
event = "Token Searched",
|
event = "Token Searched",
|
||||||
params = mapOf(
|
params = mapOf(
|
||||||
"Result" to if (tokenFound) "Yes" else "No",
|
"Result" to if (wasTokenFound) "Yes" else "No",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ internal class MarketsListModel @Inject constructor(
|
||||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||||
) : Model() {
|
) : Model() {
|
||||||
|
|
||||||
private var updateQuotesJob = JobHolder()
|
private val updateQuotesJob = JobHolder()
|
||||||
|
|
||||||
private val currentAppCurrency = getSelectedAppCurrencyUseCase().map { maybeAppCurrency ->
|
private val currentAppCurrency = getSelectedAppCurrencyUseCase().map { maybeAppCurrency ->
|
||||||
maybeAppCurrency.getOrElse { AppCurrency.Default }
|
maybeAppCurrency.getOrElse { AppCurrency.Default }
|
||||||
|
|
@ -64,6 +64,8 @@ internal class MarketsListModel @Inject constructor(
|
||||||
|
|
||||||
private val visibleItemIds = MutableStateFlow<List<CryptoCurrency.RawID>>(emptyList())
|
private val visibleItemIds = MutableStateFlow<List<CryptoCurrency.RawID>>(emptyList())
|
||||||
|
|
||||||
|
private lateinit var activeListManager: MarketsListBatchFlowManager
|
||||||
|
|
||||||
private val marketsListUMStateManager = MarketsListUMStateManager(
|
private val marketsListUMStateManager = MarketsListUMStateManager(
|
||||||
currentVisibleIds = Provider { visibleItemIds.value },
|
currentVisibleIds = Provider { visibleItemIds.value },
|
||||||
onLoadMoreUiItems = { activeListManager.loadMore() },
|
onLoadMoreUiItems = { activeListManager.loadMore() },
|
||||||
|
|
@ -95,7 +97,9 @@ internal class MarketsListModel @Inject constructor(
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
)
|
)
|
||||||
|
|
||||||
private var activeListManager: MarketsListBatchFlowManager = mainMarketsListManager
|
init {
|
||||||
|
activeListManager = mainMarketsListManager
|
||||||
|
}
|
||||||
|
|
||||||
private val _tokenSelected = MutableSharedFlow<Pair<TokenMarket, AppCurrency>>()
|
private val _tokenSelected = MutableSharedFlow<Pair<TokenMarket, AppCurrency>>()
|
||||||
|
|
||||||
|
|
@ -173,8 +177,8 @@ internal class MarketsListModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.onEach {
|
state.onEach { currentState ->
|
||||||
if (it.list !is ListUM.Content) {
|
if (currentState.list !is ListUM.Content) {
|
||||||
visibleItemIds.value = emptyList()
|
visibleItemIds.value = emptyList()
|
||||||
}
|
}
|
||||||
}.launchIn(modelScope)
|
}.launchIn(modelScope)
|
||||||
|
|
@ -188,8 +192,8 @@ internal class MarketsListModel @Inject constructor(
|
||||||
}.launchIn(modelScope)
|
}.launchIn(modelScope)
|
||||||
|
|
||||||
// load charts when new batch is being loaded
|
// load charts when new batch is being loaded
|
||||||
mainMarketsListManager.onLastBatchLoadedSuccess.onEach {
|
mainMarketsListManager.onLastBatchLoadedSuccess.onEach { batchKey ->
|
||||||
mainMarketsListManager.loadCharts(setOf(it), marketsListUMStateManager.selectedInterval)
|
mainMarketsListManager.loadCharts(setOf(batchKey), marketsListUMStateManager.selectedInterval)
|
||||||
modelScope.loadQuotesWithTimer(timeMillis = UPDATE_QUOTES_TIMER_MILLIS)
|
modelScope.loadQuotesWithTimer(timeMillis = UPDATE_QUOTES_TIMER_MILLIS)
|
||||||
}.launchIn(modelScope)
|
}.launchIn(modelScope)
|
||||||
|
|
||||||
|
|
@ -210,15 +214,19 @@ internal class MarketsListModel @Inject constructor(
|
||||||
|
|
||||||
// reload list when sorting type has changed
|
// reload list when sorting type has changed
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
marketsListUMStateManager.state.map { it.selectedSortBy }.distinctUntilChanged().drop(1).collectLatest {
|
marketsListUMStateManager.state
|
||||||
mainMarketsListManager.reload()
|
.map { state -> state.selectedSortBy }
|
||||||
}
|
.distinctUntilChanged()
|
||||||
|
.drop(1)
|
||||||
|
.collectLatest {
|
||||||
|
mainMarketsListManager.reload()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// listen current visible batch and update charts
|
// listen current visible batch and update charts
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
visibleItemIds.mapNotNull {
|
visibleItemIds.mapNotNull { itemIds ->
|
||||||
if (it.isNotEmpty()) {
|
if (itemIds.isNotEmpty()) {
|
||||||
activeListManager.getBatchKeysByItemIds(visibleItemIds.value)
|
activeListManager.getBatchKeysByItemIds(visibleItemIds.value)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
|
|
@ -252,20 +260,20 @@ internal class MarketsListModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
searchMarketsListManager.onLastBatchLoadedSuccess.collectLatest {
|
searchMarketsListManager.onLastBatchLoadedSuccess.collectLatest { batchKey ->
|
||||||
searchMarketsListManager.loadCharts(setOf(it), marketsListUMStateManager.selectedInterval)
|
searchMarketsListManager.loadCharts(setOf(batchKey), marketsListUMStateManager.selectedInterval)
|
||||||
modelScope.loadQuotesWithTimer(timeMillis = UPDATE_QUOTES_TIMER_MILLIS)
|
modelScope.loadQuotesWithTimer(timeMillis = UPDATE_QUOTES_TIMER_MILLIS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
searchMarketsListManager.isSearchNotFoundState.onEach {
|
searchMarketsListManager.isSearchNotFoundState.onEach { isNotFound ->
|
||||||
if (it) {
|
if (isNotFound) {
|
||||||
analyticsEventHandler.send(MarketsListAnalyticsEvent.TokenSearched(tokenFound = false))
|
analyticsEventHandler.send(MarketsListAnalyticsEvent.TokenSearched(wasTokenFound = false))
|
||||||
}
|
}
|
||||||
}.launchIn(modelScope)
|
}.launchIn(modelScope)
|
||||||
|
|
||||||
searchMarketsListManager.onFirstBatchLoadedSuccess.onEach {
|
searchMarketsListManager.onFirstBatchLoadedSuccess.onEach {
|
||||||
analyticsEventHandler.send(MarketsListAnalyticsEvent.TokenSearched(tokenFound = true))
|
analyticsEventHandler.send(MarketsListAnalyticsEvent.TokenSearched(wasTokenFound = true))
|
||||||
}.launchIn(modelScope)
|
}.launchIn(modelScope)
|
||||||
|
|
||||||
// analytics
|
// analytics
|
||||||
|
|
|
||||||
|
|
@ -68,17 +68,17 @@ internal class MarketsListBatchFlowManager(
|
||||||
|
|
||||||
val onLastBatchLoadedSuccess = batchFlow.state
|
val onLastBatchLoadedSuccess = batchFlow.state
|
||||||
.distinctUntilChanged { old, new -> old.status == new.status && old.data.size == new.data.size }
|
.distinctUntilChanged { old, new -> old.status == new.status && old.data.size == new.data.size }
|
||||||
.mapNotNull {
|
.mapNotNull { state ->
|
||||||
when (val status = it.status) {
|
when (val status = state.status) {
|
||||||
is PaginationStatus.Paginating -> {
|
is PaginationStatus.Paginating -> {
|
||||||
if (status.lastResult is BatchFetchResult.Success) {
|
if (status.lastResult is BatchFetchResult.Success) {
|
||||||
it.data.lastOrNull()?.key
|
state.data.lastOrNull()?.key
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is PaginationStatus.EndOfPagination -> {
|
is PaginationStatus.EndOfPagination -> {
|
||||||
it.data.lastOrNull()?.key
|
state.data.lastOrNull()?.key
|
||||||
}
|
}
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
@ -86,22 +86,22 @@ internal class MarketsListBatchFlowManager(
|
||||||
|
|
||||||
val onFirstBatchLoadedSuccess = batchFlow.state
|
val onFirstBatchLoadedSuccess = batchFlow.state
|
||||||
.distinctUntilChanged { old, new -> old.status == new.status && old.data.size == new.data.size }
|
.distinctUntilChanged { old, new -> old.status == new.status && old.data.size == new.data.size }
|
||||||
.mapNotNull {
|
.mapNotNull { state ->
|
||||||
when (val status = it.status) {
|
when (val status = state.status) {
|
||||||
is PaginationStatus.Paginating -> {
|
is PaginationStatus.Paginating -> {
|
||||||
if (status.lastResult is BatchFetchResult.Success) {
|
if (status.lastResult is BatchFetchResult.Success) {
|
||||||
it.data.size == 1
|
state.data.size == 1
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is PaginationStatus.EndOfPagination -> {
|
is PaginationStatus.EndOfPagination -> {
|
||||||
it.data.size == 1
|
state.data.size == 1
|
||||||
}
|
}
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.filter { it }
|
.filter { isFirstBatch -> isFirstBatch }
|
||||||
|
|
||||||
val isInInitialLoadingErrorState = batchFlow.state
|
val isInInitialLoadingErrorState = batchFlow.state
|
||||||
.map { it.status is PaginationStatus.InitialLoadingError }
|
.map { it.status is PaginationStatus.InitialLoadingError }
|
||||||
|
|
@ -113,10 +113,10 @@ internal class MarketsListBatchFlowManager(
|
||||||
)
|
)
|
||||||
|
|
||||||
val isSearchNotFoundState = batchFlow.state
|
val isSearchNotFoundState = batchFlow.state
|
||||||
.map {
|
.map { state ->
|
||||||
currentSearchText().isNullOrEmpty().not() &&
|
currentSearchText().isNullOrEmpty().not() &&
|
||||||
it.status is PaginationStatus.EndOfPagination &&
|
state.status is PaginationStatus.EndOfPagination &&
|
||||||
it.data.isEmpty()
|
state.data.isEmpty()
|
||||||
}
|
}
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.stateIn(
|
.stateIn(
|
||||||
|
|
@ -175,22 +175,21 @@ internal class MarketsListBatchFlowManager(
|
||||||
forceUpdate || previousList.isNullOrEmpty() || newList.first().key != previousList.first().key
|
forceUpdate || previousList.isNullOrEmpty() || newList.first().key != previousList.first().key
|
||||||
|
|
||||||
val outItems = if (isInitialLoading) {
|
val outItems = if (isInitialLoading) {
|
||||||
newList.map {
|
newList.map { batch ->
|
||||||
Batch(
|
Batch(
|
||||||
key = it.key,
|
key = batch.key,
|
||||||
data = converter.convertList(it.data),
|
data = converter.convertList(batch.data),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
previousList!!
|
|
||||||
if (previousList.size != newList.size) {
|
if (previousList.size != newList.size) {
|
||||||
val keysToAdd = newList.map { it.key }.subtract(previousList.map { it.key }.toSet())
|
val keysToAdd = newList.map { it.key }.subtract(previousList.map { it.key }.toSet())
|
||||||
val newBatches = newList.filter { keysToAdd.contains(it.key) }
|
val newBatches = newList.filter { keysToAdd.contains(it.key) }
|
||||||
|
|
||||||
items + newBatches.map {
|
items + newBatches.map { batch ->
|
||||||
Batch(
|
Batch(
|
||||||
key = it.key,
|
key = batch.key,
|
||||||
data = converter.convertList(it.data),
|
data = converter.convertList(batch.data),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -264,8 +263,8 @@ internal class MarketsListBatchFlowManager(
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
val currentData = batchFlow.state.value.data
|
val currentData = batchFlow.state.value.data
|
||||||
val alreadyLoadedChartsBatchKeys = currentData
|
val alreadyLoadedChartsBatchKeys = currentData
|
||||||
.filter {
|
.filter { batch ->
|
||||||
val first = it.data.firstOrNull() ?: return@filter false
|
val first = batch.data.firstOrNull() ?: return@filter false
|
||||||
val chartByInterval = when (interval) {
|
val chartByInterval = when (interval) {
|
||||||
TrendInterval.H24 -> first.tokenCharts.h24
|
TrendInterval.H24 -> first.tokenCharts.h24
|
||||||
TrendInterval.D7 -> first.tokenCharts.week
|
TrendInterval.D7 -> first.tokenCharts.week
|
||||||
|
|
@ -273,7 +272,7 @@ internal class MarketsListBatchFlowManager(
|
||||||
}
|
}
|
||||||
chartByInterval != null
|
chartByInterval != null
|
||||||
}
|
}
|
||||||
.map { it.key }
|
.map { batch -> batch.key }
|
||||||
.toSet()
|
.toSet()
|
||||||
|
|
||||||
val batchesKeysToLoad = batchKeys.minus(alreadyLoadedChartsBatchKeys)
|
val batchesKeysToLoad = batchKeys.minus(alreadyLoadedChartsBatchKeys)
|
||||||
|
|
@ -302,9 +301,12 @@ internal class MarketsListBatchFlowManager(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val batchKeys = batchFlow.state.value.data
|
||||||
|
.map { it.key }
|
||||||
|
.toSet()
|
||||||
actionsFlow.emit(
|
actionsFlow.emit(
|
||||||
BatchAction.UpdateBatches(
|
BatchAction.UpdateBatches(
|
||||||
keys = batchFlow.state.value.data.map { it.key }.toSet(),
|
keys = batchKeys,
|
||||||
updateRequest = TokenMarketUpdateRequest.UpdateQuotes(
|
updateRequest = TokenMarketUpdateRequest.UpdateQuotes(
|
||||||
currencyId = currentAppCurrency().code,
|
currencyId = currentAppCurrency().code,
|
||||||
),
|
),
|
||||||
|
|
@ -332,7 +334,10 @@ internal class MarketsListBatchFlowManager(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTokenById(id: CryptoCurrency.RawID): TokenMarket? {
|
fun getTokenById(id: CryptoCurrency.RawID): TokenMarket? {
|
||||||
return batchFlow.state.value.data.map { it.data }.flatten().find { it.id == id }
|
val allTokens = batchFlow.state.value.data
|
||||||
|
.map { it.data }
|
||||||
|
.flatten()
|
||||||
|
return allTokens.find { it.id == id }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun SortByTypeUM.toRequestOrder(): TokenMarketListConfig.Order {
|
private fun SortByTypeUM.toRequestOrder(): TokenMarketListConfig.Order {
|
||||||
|
|
|
||||||
|
|
@ -29,15 +29,19 @@ internal class MarketsListUMStateManager(
|
||||||
private val onShowTokensUnder100kClicked: () -> Unit,
|
private val onShowTokensUnder100kClicked: () -> Unit,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private var sortByBottomSheetIsShown
|
val state = MutableStateFlow(createInitialState())
|
||||||
|
val isInSearchStateFlow = state.map { it.searchBar.isActive }.distinctUntilChanged()
|
||||||
|
val searchQueryFlow = state.map { it.searchBar.query }.distinctUntilChanged()
|
||||||
|
|
||||||
|
private var isSortByBottomSheetShown
|
||||||
get() = state.value.sortByBottomSheet.isShown
|
get() = state.value.sortByBottomSheet.isShown
|
||||||
set(value) = state.update { it.copy(sortByBottomSheet = it.sortByBottomSheet.copy(isShown = value)) }
|
set(value) = state.update { it.copy(sortByBottomSheet = it.sortByBottomSheet.copy(isShown = value)) }
|
||||||
|
|
||||||
var searchQuery
|
var searchQuery
|
||||||
get() = state.value.searchBar.query
|
get() = state.value.searchBar.query
|
||||||
private set(value) = state.update {
|
private set(value) = state.update { currentState ->
|
||||||
it.copy(
|
currentState.copy(
|
||||||
searchBar = it.searchBar.copy(
|
searchBar = currentState.searchBar.copy(
|
||||||
query = value,
|
query = value,
|
||||||
isActive = value.isNotEmpty(),
|
isActive = value.isNotEmpty(),
|
||||||
),
|
),
|
||||||
|
|
@ -50,42 +54,42 @@ internal class MarketsListUMStateManager(
|
||||||
|
|
||||||
var selectedSortByType
|
var selectedSortByType
|
||||||
get() = state.value.selectedSortBy
|
get() = state.value.selectedSortBy
|
||||||
set(value) = state.update {
|
set(value) = state.update { currentState ->
|
||||||
it.copy(
|
currentState.copy(
|
||||||
selectedSortBy = value,
|
selectedSortBy = value,
|
||||||
sortByBottomSheet = it.sortByBottomSheet.copy(
|
sortByBottomSheet = currentState.sortByBottomSheet.copy(
|
||||||
content = (it.sortByBottomSheet.content as SortByBottomSheetContentUM).copy(
|
content = (currentState.sortByBottomSheet.content as SortByBottomSheetContentUM).copy(
|
||||||
selectedOption = value,
|
selectedOption = value,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
list = if (it.list is ListUM.Content && it.selectedSortBy != value) {
|
list = if (currentState.list is ListUM.Content && currentState.selectedSortBy != value) {
|
||||||
it.list.copy(triggerScrollReset = triggeredEvent(Unit) { consumeTriggerResetScrollEvent() })
|
currentState.list.copy(
|
||||||
|
triggerScrollReset = triggeredEvent(Unit) { consumeTriggerResetScrollEvent() },
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
it.list
|
currentState.list
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
var selectedInterval
|
var selectedInterval
|
||||||
get() = state.value.selectedInterval
|
get() = state.value.selectedInterval
|
||||||
set(value) = state.update {
|
set(value) = state.update { currentState ->
|
||||||
it.copy(
|
currentState.copy(
|
||||||
selectedInterval = value,
|
selectedInterval = value,
|
||||||
list = if (it.list is ListUM.Content &&
|
list = if (currentState.list is ListUM.Content &&
|
||||||
it.selectedSortBy != SortByTypeUM.Rating &&
|
currentState.selectedSortBy != SortByTypeUM.Rating &&
|
||||||
it.selectedInterval != value
|
currentState.selectedInterval != value
|
||||||
) {
|
) {
|
||||||
it.list.copy(triggerScrollReset = triggeredEvent(Unit) { consumeTriggerResetScrollEvent() })
|
currentState.list.copy(
|
||||||
|
triggerScrollReset = triggeredEvent(Unit) { consumeTriggerResetScrollEvent() },
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
it.list
|
currentState.list
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val state = MutableStateFlow(state())
|
|
||||||
val isInSearchStateFlow = state.map { it.searchBar.isActive }.distinctUntilChanged()
|
|
||||||
val searchQueryFlow = state.map { it.searchBar.query }.distinctUntilChanged()
|
|
||||||
|
|
||||||
fun onUiItemsChanged(
|
fun onUiItemsChanged(
|
||||||
isInErrorState: Boolean,
|
isInErrorState: Boolean,
|
||||||
isSearchNotFound: Boolean,
|
isSearchNotFound: Boolean,
|
||||||
|
|
@ -127,7 +131,7 @@ internal class MarketsListUMStateManager(
|
||||||
return currentState.copy(
|
return currentState.copy(
|
||||||
list = generalContentState(itemsWithFilteredPriceChange)
|
list = generalContentState(itemsWithFilteredPriceChange)
|
||||||
.copy(
|
.copy(
|
||||||
showUnder100kTokensNotificationWasHidden = currentState.showUnder100kButtonAlreadyPressed(),
|
wasUnder100kTokensNotificationHidden = currentState.showUnder100kButtonAlreadyPressed(),
|
||||||
),
|
),
|
||||||
marketsNotificationUM = marketsNotificationUM,
|
marketsNotificationUM = marketsNotificationUM,
|
||||||
)
|
)
|
||||||
|
|
@ -144,8 +148,8 @@ internal class MarketsListUMStateManager(
|
||||||
|
|
||||||
return currentState.copy(
|
return currentState.copy(
|
||||||
list = generalContentState(filtered).copy(
|
list = generalContentState(filtered).copy(
|
||||||
showUnder100kTokensNotificationWasHidden = false,
|
wasUnder100kTokensNotificationHidden = false,
|
||||||
showUnder100kTokensNotification = true,
|
shouldShowUnder100kTokensNotification = true,
|
||||||
onShowTokensUnder100kClicked = {
|
onShowTokensUnder100kClicked = {
|
||||||
onShowTokensUnder100kClicked()
|
onShowTokensUnder100kClicked()
|
||||||
state.update { s ->
|
state.update { s ->
|
||||||
|
|
@ -153,8 +157,8 @@ internal class MarketsListUMStateManager(
|
||||||
s.copy(
|
s.copy(
|
||||||
list = s.list.copy(
|
list = s.list.copy(
|
||||||
items = searchUiItemsCached,
|
items = searchUiItemsCached,
|
||||||
showUnder100kTokensNotification = false,
|
shouldShowUnder100kTokensNotification = false,
|
||||||
showUnder100kTokensNotificationWasHidden = true,
|
wasUnder100kTokensNotificationHidden = true,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
} ?: s
|
} ?: s
|
||||||
|
|
@ -170,17 +174,17 @@ internal class MarketsListUMStateManager(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MarketsListUM.showUnder100kButtonAlreadyPressed(): Boolean {
|
private fun MarketsListUM.showUnder100kButtonAlreadyPressed(): Boolean {
|
||||||
return this.list is ListUM.Content && this.isInSearchMode && this.list.showUnder100kTokensNotificationWasHidden
|
return this.list is ListUM.Content && this.isInSearchMode && this.list.wasUnder100kTokensNotificationHidden
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show price change animation for visible items only
|
// Show price change animation for visible items only
|
||||||
private fun ImmutableList<MarketsListItemUM>.filterPriceChangeByVisibility(): ImmutableList<MarketsListItemUM> {
|
private fun ImmutableList<MarketsListItemUM>.filterPriceChangeByVisibility(): ImmutableList<MarketsListItemUM> {
|
||||||
val visibleItemIds = currentVisibleIds()
|
val visibleItemIds = currentVisibleIds()
|
||||||
return map {
|
return map { item ->
|
||||||
it.copy(
|
item.copy(
|
||||||
price = it.price.copy(
|
price = item.price.copy(
|
||||||
changeType = if (visibleItemIds.contains(it.id)) {
|
changeType = if (visibleItemIds.contains(item.id)) {
|
||||||
it.price.changeType
|
item.price.changeType
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
},
|
},
|
||||||
|
|
@ -194,15 +198,43 @@ internal class MarketsListUMStateManager(
|
||||||
items = newItems,
|
items = newItems,
|
||||||
loadMore = onLoadMoreUiItems,
|
loadMore = onLoadMoreUiItems,
|
||||||
visibleIdsChanged = visibleItemsChanged,
|
visibleIdsChanged = visibleItemsChanged,
|
||||||
showUnder100kTokensNotification = false,
|
shouldShowUnder100kTokensNotification = false,
|
||||||
onShowTokensUnder100kClicked = {},
|
onShowTokensUnder100kClicked = {},
|
||||||
triggerScrollReset = consumedEvent(),
|
triggerScrollReset = consumedEvent(),
|
||||||
onItemClick = onTokenClick,
|
onItemClick = onTokenClick,
|
||||||
showUnder100kTokensNotificationWasHidden = false,
|
wasUnder100kTokensNotificationHidden = false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun state(): MarketsListUM = MarketsListUM(
|
private fun onBottomSheetOptionClicked(sortByTypeUM: SortByTypeUM) {
|
||||||
|
state.update { currentState ->
|
||||||
|
currentState.copy(
|
||||||
|
selectedSortBy = sortByTypeUM,
|
||||||
|
sortByBottomSheet = currentState.sortByBottomSheet.copy(
|
||||||
|
isShown = false,
|
||||||
|
content = (currentState.sortByBottomSheet.content as SortByBottomSheetContentUM).copy(
|
||||||
|
selectedOption = sortByTypeUM,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun consumeTriggerResetScrollEvent() {
|
||||||
|
state.update { currentState ->
|
||||||
|
currentState.copy(
|
||||||
|
list = if (currentState.list is ListUM.Content) {
|
||||||
|
currentState.list.copy(
|
||||||
|
triggerScrollReset = consumedEvent(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
currentState.list
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createInitialState(): MarketsListUM = MarketsListUM(
|
||||||
list = ListUM.Loading,
|
list = ListUM.Loading,
|
||||||
searchBar = SearchBarUM(
|
searchBar = SearchBarUM(
|
||||||
placeholderText = resourceReference(R.string.markets_search_header_title),
|
placeholderText = resourceReference(R.string.markets_search_header_title),
|
||||||
|
|
@ -214,10 +246,10 @@ internal class MarketsListUMStateManager(
|
||||||
selectedSortBy = SortByTypeUM.Rating,
|
selectedSortBy = SortByTypeUM.Rating,
|
||||||
selectedInterval = MarketsListUM.TrendInterval.H24,
|
selectedInterval = MarketsListUM.TrendInterval.H24,
|
||||||
onIntervalClick = { selectedInterval = it },
|
onIntervalClick = { selectedInterval = it },
|
||||||
onSortByButtonClick = { sortByBottomSheetIsShown = true },
|
onSortByButtonClick = { isSortByBottomSheetShown = true },
|
||||||
sortByBottomSheet = TangemBottomSheetConfig(
|
sortByBottomSheet = TangemBottomSheetConfig(
|
||||||
isShown = false,
|
isShown = false,
|
||||||
onDismissRequest = { sortByBottomSheetIsShown = false },
|
onDismissRequest = { isSortByBottomSheetShown = false },
|
||||||
content = SortByBottomSheetContentUM(
|
content = SortByBottomSheetContentUM(
|
||||||
selectedOption = SortByTypeUM.Rating,
|
selectedOption = SortByTypeUM.Rating,
|
||||||
onOptionClicked = ::onBottomSheetOptionClicked,
|
onOptionClicked = ::onBottomSheetOptionClicked,
|
||||||
|
|
@ -225,32 +257,4 @@ internal class MarketsListUMStateManager(
|
||||||
),
|
),
|
||||||
marketsNotificationUM = null,
|
marketsNotificationUM = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun onBottomSheetOptionClicked(sortByTypeUM: SortByTypeUM) {
|
|
||||||
state.update {
|
|
||||||
it.copy(
|
|
||||||
selectedSortBy = sortByTypeUM,
|
|
||||||
sortByBottomSheet = it.sortByBottomSheet.copy(
|
|
||||||
isShown = false,
|
|
||||||
content = (it.sortByBottomSheet.content as SortByBottomSheetContentUM).copy(
|
|
||||||
selectedOption = sortByTypeUM,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun consumeTriggerResetScrollEvent() {
|
|
||||||
state.update {
|
|
||||||
it.copy(
|
|
||||||
list = if (it.list is ListUM.Content) {
|
|
||||||
it.list.copy(
|
|
||||||
triggerScrollReset = consumedEvent(),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
it.list
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -81,10 +81,10 @@ internal fun MarketsList(
|
||||||
end = TangemTheme.dimens.spacing16,
|
end = TangemTheme.dimens.spacing16,
|
||||||
bottom = 8.dp,
|
bottom = 8.dp,
|
||||||
)
|
)
|
||||||
.onGloballyPositioned {
|
.onGloballyPositioned { layoutCoordinates ->
|
||||||
if (it.size.height > 0) {
|
if (layoutCoordinates.size.height > 0) {
|
||||||
with(density) {
|
with(density) {
|
||||||
onHeaderSizeChange(it.size.height.toDp())
|
onHeaderSizeChange(layoutCoordinates.size.height.toDp())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -312,23 +312,23 @@ private fun ItemsList(
|
||||||
val searchLazyListState = rememberLazyListState()
|
val searchLazyListState = rememberLazyListState()
|
||||||
val mainLazyListState = rememberLazyListState()
|
val mainLazyListState = rememberLazyListState()
|
||||||
|
|
||||||
val mainScrolled by remember {
|
val isMainScrolled by remember {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
mainLazyListState.firstVisibleItemScrollOffset > 0
|
mainLazyListState.firstVisibleItemScrollOffset > 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val searchScrolledState by remember {
|
val isSearchScrolled by remember {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
searchLazyListState.firstVisibleItemScrollOffset > 0
|
searchLazyListState.firstVisibleItemScrollOffset > 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(mainScrolled, isInSearchMode, searchScrolledState) {
|
LaunchedEffect(isMainScrolled, isInSearchMode, isSearchScrolled) {
|
||||||
scrolledState.value = if (isInSearchMode) {
|
scrolledState.value = if (isInSearchMode) {
|
||||||
searchScrolledState
|
isSearchScrolled
|
||||||
} else {
|
} else {
|
||||||
mainScrolled
|
isMainScrolled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -392,8 +392,8 @@ private fun Preview() {
|
||||||
item.copy(id = CryptoCurrency.RawID(index.toString()))
|
item.copy(id = CryptoCurrency.RawID(index.toString()))
|
||||||
}
|
}
|
||||||
.toImmutableList(),
|
.toImmutableList(),
|
||||||
showUnder100kTokensNotification = false,
|
shouldShowUnder100kTokensNotification = false,
|
||||||
showUnder100kTokensNotificationWasHidden = false,
|
wasUnder100kTokensNotificationHidden = false,
|
||||||
loadMore = {},
|
loadMore = {},
|
||||||
visibleIdsChanged = {},
|
visibleIdsChanged = {},
|
||||||
onShowTokensUnder100kClicked = {},
|
onShowTokensUnder100kClicked = {},
|
||||||
|
|
|
||||||
|
|
@ -295,11 +295,11 @@ private fun Preview(@PreviewParameter(MarketChartListItemPreviewDataProvider::cl
|
||||||
|
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
prices = prices.map {
|
prices = prices.map { priceInfo ->
|
||||||
if (Random.nextBoolean()) {
|
if (Random.nextBoolean()) {
|
||||||
it.first.inc() to PriceChangeType.UP
|
priceInfo.first.inc() to PriceChangeType.UP
|
||||||
} else {
|
} else {
|
||||||
it.first.dec() to PriceChangeType.DOWN
|
priceInfo.first.dec() to PriceChangeType.DOWN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state1 = state1.copy(
|
state1 = state1.copy(
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ internal fun MarketsListLazyColumn(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isInSearchMode && state.showUnder100kTokensNotification) {
|
if (isInSearchMode && state.shouldShowUnder100kTokensNotification) {
|
||||||
item(key = "show tokens under 100k".hashCode()) {
|
item(key = "show tokens under 100k".hashCode()) {
|
||||||
ShowTokensUnder100kItem(
|
ShowTokensUnder100kItem(
|
||||||
onShowTokensClick = state.onShowTokensUnder100kClicked,
|
onShowTokensClick = state.onShowTokensUnder100kClicked,
|
||||||
|
|
@ -126,7 +126,7 @@ internal fun MarketsListLazyColumn(
|
||||||
triggerLoadMoreCheckOnItemsCountChange = true,
|
triggerLoadMoreCheckOnItemsCountChange = true,
|
||||||
onLoadMore = remember(state) {
|
onLoadMore = remember(state) {
|
||||||
{
|
{
|
||||||
if (state is ListUM.Content && state.showUnder100kTokensNotification.not()) {
|
if (state is ListUM.Content && state.shouldShowUnder100kTokensNotification.not()) {
|
||||||
state.loadMore()
|
state.loadMore()
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -197,8 +197,8 @@ private fun SearchNothingFoundText(modifier: Modifier = Modifier) {
|
||||||
private fun VisibleItemsTracker(listState: LazyListState, state: ListUM) {
|
private fun VisibleItemsTracker(listState: LazyListState, state: ListUM) {
|
||||||
val visibleItems by remember {
|
val visibleItems by remember {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
listState.layoutInfo.visibleItemsInfo.mapNotNull {
|
listState.layoutInfo.visibleItemsInfo.mapNotNull { item ->
|
||||||
(it.key as? String)?.split(TOKEN_LAZY_LIST_ID_SEPARATOR)?.first()
|
(item.key as? String)?.split(TOKEN_LAZY_LIST_ID_SEPARATOR)?.first()
|
||||||
?.let { rawId -> CryptoCurrency.RawID(rawId) }
|
?.let { rawId -> CryptoCurrency.RawID(rawId) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ data class MarketsListItemUM(
|
||||||
)
|
)
|
||||||
|
|
||||||
fun getComposeKey(): String {
|
fun getComposeKey(): String {
|
||||||
return id.value + TOKEN_LAZY_LIST_ID_SEPARATOR + marketCap.toString() + updateTimestamp
|
return id.value + TOKEN_LAZY_LIST_ID_SEPARATOR + marketCap.orEmpty() + updateTimestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,8 @@ sealed class ListUM {
|
||||||
|
|
||||||
data class Content(
|
data class Content(
|
||||||
val items: ImmutableList<MarketsListItemUM>,
|
val items: ImmutableList<MarketsListItemUM>,
|
||||||
val showUnder100kTokensNotification: Boolean,
|
val shouldShowUnder100kTokensNotification: Boolean,
|
||||||
val showUnder100kTokensNotificationWasHidden: Boolean,
|
val wasUnder100kTokensNotificationHidden: Boolean,
|
||||||
val loadMore: () -> Unit,
|
val loadMore: () -> Unit,
|
||||||
val visibleIdsChanged: (List<CryptoCurrency.RawID>) -> Unit,
|
val visibleIdsChanged: (List<CryptoCurrency.RawID>) -> Unit,
|
||||||
val onShowTokensUnder100kClicked: () -> Unit,
|
val onShowTokensUnder100kClicked: () -> Unit,
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
<ID>BooleanPropertyNaming: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 } } } }</ID>
|
<ID>BooleanPropertyNaming: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 } } } }</ID>
|
||||||
<ID>BooleanPropertyNaming:NFTCollectionsModel.kt$NFTCollectionsModel$val collectionFulfillQuery = query.isEmpty() || it.name?.lowercase()?.contains(query.lowercase()) == true</ID>
|
<ID>BooleanPropertyNaming:NFTCollectionsModel.kt$NFTCollectionsModel$val collectionFulfillQuery = query.isEmpty() || it.name?.lowercase()?.contains(query.lowercase()) == true</ID>
|
||||||
<ID>BooleanPropertyNaming:UpdateDataStateTransformer.kt$UpdateDataStateTransformer$val custom = derivationPath is Network.DerivationPath.Custom</ID>
|
<ID>BooleanPropertyNaming:UpdateDataStateTransformer.kt$UpdateDataStateTransformer$val custom = derivationPath is Network.DerivationPath.Custom</ID>
|
||||||
<ID>CanBeNonNullable:NFTReceive.kt$bottomSheetConfig: TangemBottomSheetConfig?</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:ChangeCollectionExpandedStateTransformer.kt$ChangeCollectionExpandedStateTransformer${ val collectionId = collection.collectionIdProvider() if (it.id == collectionId && it is NFTCollectionUM) { if (!it.isExpanded) { onFirstExpanded() } it.copy(isExpanded = !it.isExpanded) } else { it } }</ID>
|
<ID>MultilineLambdaItParameter:ChangeCollectionExpandedStateTransformer.kt$ChangeCollectionExpandedStateTransformer${ val collectionId = collection.collectionIdProvider() if (it.id == collectionId && it is NFTCollectionUM) { if (!it.isExpanded) { onFirstExpanded() } it.copy(isExpanded = !it.isExpanded) } else { it } }</ID>
|
||||||
<ID>MultilineLambdaItParameter:NFTCollectionsContent.kt${ key(it.id) { NFTCollectionWarning( modifier = Modifier .padding(top = TangemTheme.dimens.spacing16), state = it, ) } }</ID>
|
<ID>MultilineLambdaItParameter:NFTCollectionsContent.kt${ key(it.id) { NFTCollectionWarning( modifier = Modifier .padding(top = TangemTheme.dimens.spacing16), state = it, ) } }</ID>
|
||||||
<ID>MultilineLambdaItParameter:NFTCollectionsModel.kt$NFTCollectionsModel${ ChangeCollectionExpandedStateTransformer( collection = collection, collectionIdProvider = collectionIdProvider, onFirstExpanded = { onFirstExpanded(collection) }, ).transform(it) }</ID>
|
<ID>MultilineLambdaItParameter:NFTCollectionsModel.kt$NFTCollectionsModel${ ChangeCollectionExpandedStateTransformer( collection = collection, collectionIdProvider = collectionIdProvider, onFirstExpanded = { onFirstExpanded(collection) }, ).transform(it) }</ID>
|
||||||
|
|
|
||||||
|
|
@ -7,41 +7,30 @@
|
||||||
<ID>BooleanPropertyNaming:StakingModel.kt$StakingModel$val noBalanceState = balanceState == null</ID>
|
<ID>BooleanPropertyNaming:StakingModel.kt$StakingModel$val noBalanceState = balanceState == null</ID>
|
||||||
<ID>BooleanPropertyNaming:StakingUiState.kt$StakingStates.InitialInfoState.Data$val showBanner: Boolean</ID>
|
<ID>BooleanPropertyNaming:StakingUiState.kt$StakingStates.InitialInfoState.Data$val showBanner: Boolean</ID>
|
||||||
<ID>BooleanPropertyNaming:StakingUiState.kt$StakingUiState$val showColdWalletInteractionIcon: Boolean</ID>
|
<ID>BooleanPropertyNaming:StakingUiState.kt$StakingUiState$val showColdWalletInteractionIcon: Boolean</ID>
|
||||||
<ID>CanBeNonNullable:StakingScreen.kt$bottomSheetConfig: TangemBottomSheetConfig?</ID>
|
|
||||||
<ID>CastNullableToNonNullableType:SetApprovalBottomSheetInProgressTransformer.kt$SetApprovalBottomSheetInProgressTransformer$as</ID>
|
<ID>CastNullableToNonNullableType:SetApprovalBottomSheetInProgressTransformer.kt$SetApprovalBottomSheetInProgressTransformer$as</ID>
|
||||||
<ID>CastNullableToNonNullableType:SetApprovalBottomSheetTypeChangeTransformer.kt$SetApprovalBottomSheetTypeChangeTransformer$as</ID>
|
<ID>CastNullableToNonNullableType:SetApprovalBottomSheetTypeChangeTransformer.kt$SetApprovalBottomSheetTypeChangeTransformer$as</ID>
|
||||||
<ID>MultilineLambdaItParameter:AddStakingNotificationsTransformer.kt$AddStakingNotificationsTransformer${ it is StakingNotification.Error || it is NotificationUM.Error || it is NotificationUM.Warning.NetworkFeeUnreachable || it is StakingNotification.Warning.TransactionInProgress || it is StakingNotification.Warning.InitializeTonAccount }</ID>
|
<ID>MultilineLambdaItParameter:AddStakingNotificationsTransformer.kt$AddStakingNotificationsTransformer${ it is StakingNotification.Error || it is NotificationUM.Error || it is NotificationUM.Warning.NetworkFeeUnreachable || it is StakingNotification.Warning.TransactionInProgress || it is StakingNotification.Warning.InitializeTonAccount }</ID>
|
||||||
<ID>MultilineLambdaItParameter:DefaultStakingDeepLinkHandler.kt$DefaultStakingDeepLinkHandler${ val isNetwork = it.network.backendId.equals(networkId, ignoreCase = true) val isCurrency = it.id.rawCurrencyId?.value?.equals(tokenId, ignoreCase = true) == true isNetwork && isCurrency }</ID>
|
<ID>MultilineLambdaItParameter:DefaultStakingDeepLinkHandler.kt$DefaultStakingDeepLinkHandler${ val isNetwork = it.network.backendId.equals(networkId, ignoreCase = true) val isCurrency = it.id.rawCurrencyId?.value?.equals(tokenId, ignoreCase = true) == true isNetwork && isCurrency }</ID>
|
||||||
<ID>MultilineLambdaItParameter:StakingFeeBlock.kt${ if (it == FeeState.Error) { Text( text = DASH_SIGN, color = TangemTheme.colors.text.primary1, style = TangemTheme.typography.body1, ) } }</ID>
|
<ID>MultilineLambdaItParameter:StakingFeeBlock.kt${ if (it == FeeState.Error) { Text( text = DASH_SIGN, color = TangemTheme.colors.text.primary1, style = TangemTheme.typography.body1, ) } }</ID>
|
||||||
<ID>MultilineLambdaItParameter:StakingFeeBlock.kt${ if (it == FeeState.Loading) { RectangleShimmer( radius = TangemTheme.dimens.radius3, modifier = Modifier.size( height = TangemTheme.dimens.size24, width = TangemTheme.dimens.size90, ), ) } }</ID>
|
<ID>MultilineLambdaItParameter:StakingFeeBlock.kt${ if (it == FeeState.Loading) { RectangleShimmer( radius = TangemTheme.dimens.radius3, modifier = Modifier.size( height = TangemTheme.dimens.size24, width = TangemTheme.dimens.size90, ), ) } }</ID>
|
||||||
<ID>MultilineLambdaItParameter:StakingFeeTransactionLoader.kt$StakingFeeTransactionLoader${ onStakingFeeError(it) null }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:StakingFeeTransactionLoader.kt$StakingFeeTransactionLoader${ onStakingFeeError(it) return }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:StakingInfoNotificationsFactory.kt$StakingInfoNotificationsFactory${ it.type == BalanceType.PREPARING || it.type == BalanceType.STAKED || it.type == BalanceType.LOCKED }</ID>
|
<ID>MultilineLambdaItParameter:StakingInfoNotificationsFactory.kt$StakingInfoNotificationsFactory${ it.type == BalanceType.PREPARING || it.type == BalanceType.STAKED || it.type == BalanceType.LOCKED }</ID>
|
||||||
<ID>MultilineLambdaItParameter:StakingInfoNotificationsFactory.kt$StakingInfoNotificationsFactory${ it.type == BalanceType.STAKED || it.type == BalanceType.PREPARING || it.type == BalanceType.UNSTAKED }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:StakingModel.kt$StakingModel${ EnterAmountBoundary( amount = it, fiatRate = status.value.fiatRate.orZero(), ) }</ID>
|
<ID>MultilineLambdaItParameter:StakingModel.kt$StakingModel${ EnterAmountBoundary( amount = it, fiatRate = status.value.fiatRate.orZero(), ) }</ID>
|
||||||
<ID>MultilineLambdaItParameter:StakingModel.kt$StakingModel${ Timber.e(it) false }</ID>
|
<ID>MultilineLambdaItParameter:StakingModel.kt$StakingModel${ Timber.e(it) false }</ID>
|
||||||
<ID>MultilineLambdaItParameter:StakingModel.kt$StakingModel${ isBalanceHiddenFlow.value = it.isBalanceHidden stateController.update( transformer = HideBalanceStateTransformer( isBalanceHidden = it.isBalanceHidden, cryptoCurrencyStatus = cryptoCurrencyStatus, appCurrency = appCurrency, ), ) }</ID>
|
<ID>MultilineLambdaItParameter:StakingModel.kt$StakingModel${ isBalanceHiddenFlow.value = it.isBalanceHidden stateController.update( transformer = HideBalanceStateTransformer( isBalanceHidden = it.isBalanceHidden, cryptoCurrencyStatus = cryptoCurrencyStatus, appCurrency = appCurrency, ), ) }</ID>
|
||||||
<ID>MultilineLambdaItParameter:StakingModel.kt$StakingModel${ stateController.update( SetFeeToTonInitializeBottomSheetTransformer( appCurrencyProvider = Provider { appCurrency }, feeCryptoCurrencyStatus = feeCryptoCurrencyStatus, fee = it.normal, isFeeApproximate = false, ), ) }</ID>
|
<ID>MultilineLambdaItParameter:StakingModel.kt$StakingModel${ stateController.update( SetFeeToTonInitializeBottomSheetTransformer( appCurrencyProvider = Provider { appCurrency }, feeCryptoCurrencyStatus = feeCryptoCurrencyStatus, fee = it.normal, isFeeApproximate = false, ), ) }</ID>
|
||||||
<ID>MultilineLambdaItParameter:StakingStateController.kt$StakingStateController${ it.copy( showColdWalletInteractionIcon = userWallet is UserWallet.Cold, ) }</ID>
|
<ID>MultilineLambdaItParameter:StakingStateController.kt$StakingStateController${ it.copy( showColdWalletInteractionIcon = userWallet is UserWallet.Cold, ) }</ID>
|
||||||
<ID>MultilineLambdaItParameter:StakingTransactionSender.kt$StakingTransactionSender${ onConstructError(it) null }</ID>
|
|
||||||
<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>NullCheckOnMutableProperty:StakingModel.kt$StakingModel$if (feeCryptoCurrencyStatus != null && fee != null) { getBalanceNotEnoughForFeeWarningUseCase( fee = fee, userWalletId = userWalletId, tokenStatus = cryptoCurrencyStatus, coinStatus = feeCryptoCurrencyStatus ?: cryptoCurrencyStatus, ).getOrNull() } else { null }</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$$networkId</ID>
|
||||||
<ID>NullableToStringCall:DefaultStakingDeepLinkHandler.kt$DefaultStakingDeepLinkHandler$$tokenId</ID>
|
<ID>NullableToStringCall:DefaultStakingDeepLinkHandler.kt$DefaultStakingDeepLinkHandler$$tokenId</ID>
|
||||||
<ID>PropertyUsedBeforeDeclaration:StakingFeeBlock.kt$FeeBlockPreviewProvider$contentState</ID>
|
<ID>PropertyUsedBeforeDeclaration:StakingFeeBlock.kt$FeeBlockPreviewProvider$contentState</ID>
|
||||||
<ID>PropertyUsedBeforeDeclaration:StakingModel.kt$StakingModel$isAmountSubtractAvailable</ID>
|
<ID>PropertyUsedBeforeDeclaration:StakingModel.kt$StakingModel$isAmountSubtractAvailable</ID>
|
||||||
<ID>PropertyUsedBeforeDeclaration:StakingStateController.kt$StakingStateController$uiState</ID>
|
<ID>PropertyUsedBeforeDeclaration:StakingStateController.kt$StakingStateController$uiState</ID>
|
||||||
<ID>ReusedModifierInstance:StakingClaimRewardsValidatorContent.kt$InputRowImageInfo( subtitle = item.title, caption = item.getAprTextNeutral(), infoTitle = item.formattedFiatAmount, infoSubtitle = item.formattedCryptoAmount, imageUrl = item.validator?.image.orEmpty(), onImageError = { ValidatorImagePlaceholder() }, modifier = modifier .roundedShapeItemDecoration(index, state.rewards.lastIndex, false) .background(TangemTheme.colors.background.action) .clickable( enabled = item.pendingActions.isNotEmpty(), onClick = { clickIntents.onActiveStake(item) }, ), )</ID>
|
|
||||||
<ID>SuspendFunSwallowedCancellation:StakingModel.kt$StakingModel$runCatching</ID>
|
<ID>SuspendFunSwallowedCancellation:StakingModel.kt$StakingModel$runCatching</ID>
|
||||||
<ID>UnnecessaryEventHandlerParameter:StakingInitialInfoContent.kt$onClick: (BalanceState) -> Unit</ID>
|
<ID>UnnecessaryEventHandlerParameter:StakingInitialInfoContent.kt$onClick: (BalanceState) -> Unit</ID>
|
||||||
<ID>UnnecessaryFilter:StakingInfoNotificationsFactory.kt$StakingInfoNotificationsFactory$filter { it.validator?.address == validatorAddress }</ID>
|
|
||||||
<ID>UnnecessaryLet:StakingModel.kt$StakingModel$let(::add)</ID>
|
<ID>UnnecessaryLet:StakingModel.kt$StakingModel$let(::add)</ID>
|
||||||
<ID>UnnecessaryLet:StakingTosText.kt$let { onTextClick(PRIVACY_POLICY_URL) }</ID>
|
<ID>UnnecessaryLet:StakingTosText.kt$let { onTextClick(PRIVACY_POLICY_URL) }</ID>
|
||||||
<ID>UnnecessaryLet:StakingTosText.kt$let { onTextClick(TERMS_OF_USE_URL) }</ID>
|
<ID>UnnecessaryLet:StakingTosText.kt$let { onTextClick(TERMS_OF_USE_URL) }</ID>
|
||||||
<ID>UnsafeCallOnNullableType:StakingModel.kt$StakingModel$tonAccountInitializeTransaction!!</ID>
|
<ID>UnsafeCallOnNullableType:StakingModel.kt$StakingModel$tonAccountInitializeTransaction!!</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 actionsJobHolder: JobHolder = JobHolder()</ID>
|
||||||
<ID>VarCouldBeVal:StakingModel.kt$StakingModel$private var approvalJobHolder: JobHolder = JobHolder()</ID>
|
<ID>VarCouldBeVal:StakingModel.kt$StakingModel$private var approvalJobHolder: JobHolder = JobHolder()</ID>
|
||||||
<ID>VarCouldBeVal:StakingModel.kt$StakingModel$private var feeJobHolder: JobHolder = JobHolder()</ID>
|
<ID>VarCouldBeVal:StakingModel.kt$StakingModel$private var feeJobHolder: JobHolder = JobHolder()</ID>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
<ManuallySuppressedIssues/>
|
<ManuallySuppressedIssues/>
|
||||||
<CurrentIssues>
|
<CurrentIssues>
|
||||||
<ID>BooleanPropertyNaming:ExcludedBlockchainsScreenUM.kt$ExcludedBlockchainsScreenUM$val showRecoverWarning: Boolean</ID>
|
<ID>BooleanPropertyNaming:ExcludedBlockchainsScreenUM.kt$ExcludedBlockchainsScreenUM$val showRecoverWarning: Boolean</ID>
|
||||||
<ID>CanBeNonNullable:TestPushDeepLinkBottomSheet.kt$config: TangemBottomSheetConfig?</ID>
|
|
||||||
<ID>CastNullableToNonNullableType:TestPushMarketTokenSearchBottomSheetTransformer.kt$TestPushMarketTokenSearchBottomSheetTransformer$as</ID>
|
<ID>CastNullableToNonNullableType:TestPushMarketTokenSearchBottomSheetTransformer.kt$TestPushMarketTokenSearchBottomSheetTransformer$as</ID>
|
||||||
<ID>CastNullableToNonNullableType:TestPushViewModel.kt$TestPushViewModel$as</ID>
|
<ID>CastNullableToNonNullableType:TestPushViewModel.kt$TestPushViewModel$as</ID>
|
||||||
<ID>ExplicitCollectionElementAccessMethod:TestPushAddKeyDataTransformer.kt$TestPushAddKeyDataTransformer$mutableData.set(index = index, updated)</ID>
|
<ID>ExplicitCollectionElementAccessMethod:TestPushAddKeyDataTransformer.kt$TestPushAddKeyDataTransformer$mutableData.set(index = index, updated)</ID>
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@
|
||||||
<ID>BooleanPropertyNaming:WalletScreen.kt$var visible by remember { mutableStateOf(value = false) }</ID>
|
<ID>BooleanPropertyNaming:WalletScreen.kt$var visible by remember { mutableStateOf(value = false) }</ID>
|
||||||
<ID>BooleanPropertyNaming:WalletScreenState.kt$WalletScreenState$val showMarketsOnboarding: Boolean</ID>
|
<ID>BooleanPropertyNaming:WalletScreenState.kt$WalletScreenState$val showMarketsOnboarding: Boolean</ID>
|
||||||
<ID>BooleanPropertyNaming:WalletWithFundsChecker.kt$WalletWithFundsChecker$val prevStatus = statusByWalletId.get(userWalletId)</ID>
|
<ID>BooleanPropertyNaming:WalletWithFundsChecker.kt$WalletWithFundsChecker$val prevStatus = statusByWalletId.get(userWalletId)</ID>
|
||||||
<ID>CanBeNonNullable:WalletScreen.kt$bottomSheetConfig: TangemBottomSheetConfig?</ID>
|
|
||||||
<ID>IgnoredReturnValue:MultiWalletTokenListStore.kt$MultiWalletTokenListStore$remove(userWalletId)</ID>
|
<ID>IgnoredReturnValue:MultiWalletTokenListStore.kt$MultiWalletTokenListStore$remove(userWalletId)</ID>
|
||||||
<ID>MaxChainedCallsOnSameLine:HasSingleWalletSignedHashesUseCase.kt$HasSingleWalletSignedHashesUseCase$userWallet.scanResponse.card.wallets.firstOrNull()?.totalSignedHashes</ID>
|
<ID>MaxChainedCallsOnSameLine:HasSingleWalletSignedHashesUseCase.kt$HasSingleWalletSignedHashesUseCase$userWallet.scanResponse.card.wallets.firstOrNull()?.totalSignedHashes</ID>
|
||||||
<ID>MultilineLambdaItParameter:BasicTokenListSubscriber.kt$BasicTokenListSubscriber${ it.getOrElse { e -> Timber.e("Failed to load app currency: $e") AppCurrency.Default } }</ID>
|
<ID>MultilineLambdaItParameter:BasicTokenListSubscriber.kt$BasicTokenListSubscriber${ it.getOrElse { e -> Timber.e("Failed to load app currency: $e") AppCurrency.Default } }</ID>
|
||||||
|
|
@ -47,11 +46,7 @@
|
||||||
<ID>MultilineLambdaItParameter:SingleWalletExpressStatusesSubscriber.kt$SingleWalletExpressStatusesSubscriber${ Timber.e("Unable to get primary currency status: $it") return@onEach }</ID>
|
<ID>MultilineLambdaItParameter:SingleWalletExpressStatusesSubscriber.kt$SingleWalletExpressStatusesSubscriber${ Timber.e("Unable to get primary currency status: $it") return@onEach }</ID>
|
||||||
<ID>MultilineLambdaItParameter:TokenListAnalyticsSender.kt$TokenListAnalyticsSender${ val status = it.value if (status is CryptoCurrencyStatus.Loaded) { sendTokenBalancesForSpecificBlockchains(it, status) } }</ID>
|
<ID>MultilineLambdaItParameter:TokenListAnalyticsSender.kt$TokenListAnalyticsSender${ val status = it.value if (status is CryptoCurrencyStatus.Loaded) { sendTokenBalancesForSpecificBlockchains(it, status) } }</ID>
|
||||||
<ID>MultilineLambdaItParameter:TokenListStateConverter.kt$TokenListStateConverter${ if (isExtend) { clickIntents.onAccountCollapseClick(it) } else { clickIntents.onAccountExpandClick(it) } }</ID>
|
<ID>MultilineLambdaItParameter:TokenListStateConverter.kt$TokenListStateConverter${ if (isExtend) { clickIntents.onAccountCollapseClick(it) } else { clickIntents.onAccountExpandClick(it) } }</ID>
|
||||||
<ID>MultilineLambdaItParameter:TxHistorySubscriber.kt$TxHistorySubscriber${ SetTxHistoryCountErrorTransformer( userWallet = userWallet, error = it, pendingTransactions = status.value.pendingTransactions, clickIntents = clickIntents, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:TxHistorySubscriber.kt$TxHistorySubscriber${ SetTxHistoryCountTransformer( userWalletId = userWallet.walletId, transactionsCount = it, clickIntents = clickIntents, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:TxHistorySubscriber.kt$TxHistorySubscriber${ SetTxHistoryItemsErrorTransformer( userWalletId = userWallet.walletId, error = it, clickIntents = clickIntents, ) }</ID>
|
<ID>MultilineLambdaItParameter:TxHistorySubscriber.kt$TxHistorySubscriber${ SetTxHistoryItemsErrorTransformer( userWalletId = userWallet.walletId, error = it, clickIntents = clickIntents, ) }</ID>
|
||||||
<ID>MultilineLambdaItParameter:TxHistorySubscriberV2.kt$TxHistorySubscriberV2${ SetTxHistoryCountErrorTransformer( userWallet = userWallet, error = it, pendingTransactions = status.value.pendingTransactions, clickIntents = clickIntents, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:TxHistorySubscriberV2.kt$TxHistorySubscriberV2${ SetTxHistoryCountTransformer( userWalletId = userWallet.walletId, transactionsCount = it, clickIntents = clickIntents, ) }</ID>
|
|
||||||
<ID>MultilineLambdaItParameter:TxHistorySubscriberV2.kt$TxHistorySubscriberV2${ SetTxHistoryItemsErrorTransformer( userWalletId = userWallet.walletId, error = it, clickIntents = clickIntents, ) }</ID>
|
<ID>MultilineLambdaItParameter:TxHistorySubscriberV2.kt$TxHistorySubscriberV2${ SetTxHistoryItemsErrorTransformer( userWalletId = userWallet.walletId, error = it, clickIntents = clickIntents, ) }</ID>
|
||||||
<ID>MultilineLambdaItParameter:UpdateMultiWalletActionsTransformer.kt$UpdateMultiWalletActionsTransformer${ when (it) { is WalletManageButton.Buy -> { it.copy( enabled = buyStatus.isContent(), dimContent = !buyStatus.isContent(), ) } is WalletManageButton.Sell -> { it.copy( enabled = sellStatus.isContent(), dimContent = !sellStatus.isContent(), ) } is WalletManageButton.Swap -> { it.copy( enabled = swapStatus.isContent(), dimContent = !swapStatus.isContent(), ) } else -> it } }</ID>
|
<ID>MultilineLambdaItParameter:UpdateMultiWalletActionsTransformer.kt$UpdateMultiWalletActionsTransformer${ when (it) { is WalletManageButton.Buy -> { it.copy( enabled = buyStatus.isContent(), dimContent = !buyStatus.isContent(), ) } is WalletManageButton.Sell -> { it.copy( enabled = sellStatus.isContent(), dimContent = !sellStatus.isContent(), ) } is WalletManageButton.Swap -> { it.copy( enabled = swapStatus.isContent(), dimContent = !swapStatus.isContent(), ) } else -> it } }</ID>
|
||||||
<ID>MultilineLambdaItParameter:UseCaseExt.kt${ Timber.e("Impossible to get primary currency status $it") null }</ID>
|
<ID>MultilineLambdaItParameter:UseCaseExt.kt${ Timber.e("Impossible to get primary currency status $it") null }</ID>
|
||||||
|
|
@ -83,7 +78,6 @@
|
||||||
<ID>MultilineLambdaItParameter:WalletWarningsClickIntents.kt$WalletWarningsClickIntentsImplementor${ Timber.e( """ Unable to get user wallet |- ID: $userWalletId |- Exception: $it """.trimIndent(), ) null }</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${ router.openOnboardingScreen( scanResponse = it.scanResponse, continueBackup = true, ) }</ID>
|
||||||
<ID>MultilineLambdaItParameter:WalletWithFundsChecker.kt$WalletWithFundsChecker${ val amount = it.value.amount ?: return@any false !amount.isZero() }</ID>
|
<ID>MultilineLambdaItParameter:WalletWithFundsChecker.kt$WalletWithFundsChecker${ val amount = it.value.amount ?: return@any false !amount.isZero() }</ID>
|
||||||
<ID>NamedArguments:BasicAccountListSubscriber.kt$BasicAccountListSubscriber$updateContent(convertParams, appCurrency, yieldSupplyApyMap, stakingApyMap)</ID>
|
|
||||||
<ID>NamedArguments:GetMultiWalletWarningsFactory.kt$GetMultiWalletWarningsFactory$addInformationalNotifications(userWallet, cardTypesResolver, flattenCurrencies, clickIntents)</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:GetMultiWalletWarningsFactory.kt$GetMultiWalletWarningsFactory$addWarningNotifications(cardTypesResolver, flattenCurrencies, isNeedToBackup, clickIntents)</ID>
|
||||||
<ID>NamedArguments:TangemSnapFlingBehavior.kt$HighVelocityApproachAnimation$animateDecay(offset, animationState, decayAnimationSpec, onAnimationStep)</ID>
|
<ID>NamedArguments:TangemSnapFlingBehavior.kt$HighVelocityApproachAnimation$animateDecay(offset, animationState, decayAnimationSpec, onAnimationStep)</ID>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
<ID>BooleanPropertyNaming:WelcomeModel.kt$WelcomeModel$private var routedOut = false</ID>
|
<ID>BooleanPropertyNaming:WelcomeModel.kt$WelcomeModel$private var routedOut = false</ID>
|
||||||
<ID>BooleanPropertyNaming:WelcomeUM.kt$WelcomeUM.SelectWallet$val showUnlockWithBiometricButton: Boolean = false</ID>
|
<ID>BooleanPropertyNaming:WelcomeUM.kt$WelcomeUM.SelectWallet$val showUnlockWithBiometricButton: Boolean = false</ID>
|
||||||
<ID>MultilineLambdaItParameter:WelcomeModel.kt$WelcomeModel${ if (it.isEmpty()) { router.replaceAll(AppRoute.Home()) } wallets.value = it }</ID>
|
<ID>MultilineLambdaItParameter:WelcomeModel.kt$WelcomeModel${ if (it.isEmpty()) { router.replaceAll(AppRoute.Home()) } wallets.value = it }</ID>
|
||||||
<ID>MultilineLambdaItParameter:WelcomeModel.kt$WelcomeModel${ it.handle( specificWalletId = null, onUserCancelled = { tryToUnlockWithAccessCodeRightAway() }, ) setSelectWalletState() }</ID>
|
|
||||||
<ID>ReusedModifierInstance:Welcome.kt$WelcomePlain(modifier = modifier)</ID>
|
<ID>ReusedModifierInstance:Welcome.kt$WelcomePlain(modifier = modifier)</ID>
|
||||||
<ID>ReusedModifierInstance:Welcome.kt$WelcomeSelectWallet( state = st, modifier = modifier, )</ID>
|
<ID>ReusedModifierInstance:Welcome.kt$WelcomeSelectWallet( state = st, modifier = modifier, )</ID>
|
||||||
</CurrentIssues>
|
</CurrentIssues>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue