Updated on 2026-08-14
This commit is contained in:
parent
590888db59
commit
682349e353
48 changed files with 860 additions and 111 deletions
|
|
@ -67,6 +67,7 @@ dependencies {
|
|||
implementation(projects.domain.feedback)
|
||||
implementation(projects.domain.qrScanning)
|
||||
implementation(projects.domain.qrScanning.models)
|
||||
implementation(projects.domain.staking)
|
||||
|
||||
implementation(projects.common)
|
||||
implementation(projects.core.analytics)
|
||||
|
|
@ -99,6 +100,7 @@ dependencies {
|
|||
implementation(projects.data.onboarding)
|
||||
implementation(projects.data.feedback)
|
||||
implementation(projects.data.qrScanning)
|
||||
implementation(projects.data.staking)
|
||||
|
||||
/** Features */
|
||||
implementation(projects.features.onboarding)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
package com.tangem.tap.di.domain
|
||||
|
||||
import com.tangem.domain.settings.*
|
||||
import com.tangem.domain.staking.GetStakingAvailabilityUseCase
|
||||
import com.tangem.domain.staking.GetStakingEntryInfoUseCase
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object StakingDomainModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetStakingEntryInfoUseCase(stakingRepository: StakingRepository): GetStakingEntryInfoUseCase {
|
||||
return GetStakingEntryInfoUseCase(
|
||||
stakingRepository = stakingRepository,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetStakingAvailabilityUseCase(stakingRepository: StakingRepository): GetStakingAvailabilityUseCase {
|
||||
return GetStakingAvailabilityUseCase(
|
||||
stakingRepository = stakingRepository,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import com.squareup.moshi.Moshi
|
|||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import com.tangem.common.json.MoshiJsonConverter
|
||||
import com.tangem.common.json.TangemSdkAdapter
|
||||
import com.tangem.datasource.api.common.adapter.BigDecimalAdapter
|
||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.datasource.api.common
|
||||
package com.tangem.datasource.api.common.adapter
|
||||
|
||||
import com.squareup.moshi.FromJson
|
||||
import com.squareup.moshi.ToJson
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.datasource.api.common
|
||||
package com.tangem.datasource.api.common.adapter
|
||||
|
||||
import com.squareup.moshi.*
|
||||
import org.joda.time.DateTime
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.datasource.api.common
|
||||
package com.tangem.datasource.api.common.adapter
|
||||
|
||||
import com.squareup.moshi.*
|
||||
import org.joda.time.LocalDate
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.datasource.api.stakekit
|
||||
package com.tangem.datasource.api.common.adapter
|
||||
|
||||
import com.squareup.moshi.*
|
||||
import com.squareup.moshi.adapters.EnumJsonAdapter
|
||||
|
|
@ -1,21 +1,23 @@
|
|||
package com.tangem.datasource.api.stakekit
|
||||
|
||||
import com.tangem.datasource.api.common.response.ApiResponse
|
||||
import com.tangem.datasource.api.stakekit.models.request.MultipleYieldBalancesRequestBody
|
||||
import com.tangem.datasource.api.stakekit.models.request.YieldBalanceRequestBody
|
||||
import com.tangem.datasource.api.stakekit.models.request.RevenueOption
|
||||
import com.tangem.datasource.api.stakekit.models.request.YieldType
|
||||
import com.tangem.datasource.api.stakekit.models.response.EnabledYieldsResponse
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.TokenWithYield
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalances
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.Yield
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapper
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Path
|
||||
import retrofit2.http.Query
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
interface StakeKitApi {
|
||||
|
||||
@GET("yields/enabled")
|
||||
fun getAllYields(
|
||||
suspend fun getMultipleYields(
|
||||
@Query("ledgerWalletAPICompatible") ledgerWalletAPICompatible: Boolean,
|
||||
@Query("type") type: YieldType,
|
||||
@Query("revenueOption") revenueOption: RevenueOption,
|
||||
|
|
@ -24,9 +26,23 @@ interface StakeKitApi {
|
|||
@Query("limit") limit: Int,
|
||||
): ApiResponse<EnabledYieldsResponse>
|
||||
|
||||
@GET("yields/{integrationId}")
|
||||
suspend fun getSingleYield(
|
||||
@Path("integrationId") integrationId: String,
|
||||
@Query("ledgerWalletAPICompatible") ledgerWalletAPICompatible: Boolean = false,
|
||||
): ApiResponse<Yield>
|
||||
|
||||
@GET("yields/balances")
|
||||
fun getMultipleYieldBalances(@Body body: List<MultipleYieldBalancesRequestBody>): ApiResponse<List<YieldBalances>>
|
||||
suspend fun getMultipleYieldBalances(
|
||||
@Body body: List<YieldBalanceRequestBody>,
|
||||
): ApiResponse<List<YieldBalanceWrapper>>
|
||||
|
||||
@GET("yields/{integrationId}/balances")
|
||||
suspend fun getSingleYieldBalance(
|
||||
@Path("integrationId") integrationId: String,
|
||||
@Body body: YieldBalanceRequestBody,
|
||||
): ApiResponse<YieldBalanceWrapper>
|
||||
|
||||
@GET("tokens")
|
||||
fun getTokens(): ApiResponse<List<TokenWithYield>>
|
||||
suspend fun getTokens(): ApiResponse<List<TokenWithYield>>
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tangem.datasource.api.stakekit.models.request
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
data class YieldBalanceRequestBody(
|
||||
@Json(name = "addresses") val addresses: Address,
|
||||
@Json(name = "args") val args: YieldBalanceRequestArgs,
|
||||
@Json(name = "integrationId") val integrationId: String? = null,
|
||||
) {
|
||||
|
||||
data class Address(
|
||||
@Json(name = "address") val address: String,
|
||||
@Json(name = "additionalAddresses") val additionalAddresses: AdditionalAddresses? = null,
|
||||
@Json(name = "explorerUrl") val explorerUrl: String,
|
||||
) {
|
||||
|
||||
data class AdditionalAddresses(
|
||||
@Json(name = "cosmosPubKey") val cosmosPubKey: String? = null,
|
||||
@Json(name = "binanceBeaconAddress") val binanceBeaconAddress: String? = null,
|
||||
@Json(name = "stakeAccounts") val stakeAccounts: List<String>? = null,
|
||||
@Json(name = "lidoStakeAccounts") val lidoStakeAccounts: List<String>? = null,
|
||||
@Json(name = "tezosPubKey") val tezosPubKey: String? = null,
|
||||
@Json(name = "cAddressBech") val cAddressBech: String? = null,
|
||||
@Json(name = "pAddressBech") val pAddressBech: String? = null,
|
||||
)
|
||||
}
|
||||
|
||||
data class YieldBalanceRequestArgs(
|
||||
@Json(name = "validatorAddresses") val validatorAddresses: List<String>,
|
||||
)
|
||||
}
|
||||
|
|
@ -12,11 +12,11 @@ data class Token(
|
|||
@Json(name = "address") val address: String?,
|
||||
@Json(name = "coinGeckoId") val coinGeckoId: String?,
|
||||
@Json(name = "logoURI") val logoURI: String?,
|
||||
@Json(name = "isPoints") val isPoints: Boolean,
|
||||
@Json(name = "isPoints") val isPoints: Boolean?,
|
||||
) {
|
||||
enum class NetworkType {
|
||||
@Json(name = "avalanche")
|
||||
AVALANCHE,
|
||||
@Json(name = "avalanche-c")
|
||||
AVALANCHE_C,
|
||||
|
||||
@Json(name = "avalanche-atomic")
|
||||
AVALANCHE_ATOMIC,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.datasource.api.stakekit.models.response.model
|
|||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import java.math.BigDecimal
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Yield(
|
||||
|
|
@ -16,7 +17,7 @@ data class Yield(
|
|||
@Json(name = "status")
|
||||
val status: Status,
|
||||
@Json(name = "apy")
|
||||
val apy: Double,
|
||||
val apy: BigDecimal,
|
||||
@Json(name = "rewardRate")
|
||||
val rewardRate: Double,
|
||||
@Json(name = "rewardType")
|
||||
|
|
@ -114,7 +115,7 @@ data class Yield(
|
|||
@Json(name = "defaultValidator")
|
||||
val defaultValidator: String?,
|
||||
@Json(name = "minimumStake")
|
||||
val minimumStake: Int,
|
||||
val minimumStake: Int?,
|
||||
@Json(name = "supportsMultipleValidators")
|
||||
val supportsMultipleValidators: Boolean,
|
||||
@Json(name = "revshare")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,138 @@
|
|||
package com.tangem.datasource.api.stakekit.models.response.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import org.joda.time.DateTime
|
||||
import java.math.BigDecimal
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class YieldBalanceWrapper(
|
||||
@Json(name = "balances")
|
||||
val balances: List<Balance>,
|
||||
@Json(name = "integrationId")
|
||||
val integrationId: String?,
|
||||
) {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Balance(
|
||||
@Json(name = "groupId")
|
||||
val groupId: String,
|
||||
@Json(name = "type")
|
||||
val type: BalanceType,
|
||||
@Json(name = "amount")
|
||||
val amount: BigDecimal,
|
||||
@Json(name = "date")
|
||||
val date: DateTime?,
|
||||
@Json(name = "pricePerShare")
|
||||
val pricePerShare: BigDecimal,
|
||||
@Json(name = "pendingActions")
|
||||
val pendingActions: List<PendingAction>,
|
||||
@Json(name = "token")
|
||||
val token: Token,
|
||||
@Json(name = "validatorAddress")
|
||||
val validatorAddress: String?,
|
||||
@Json(name = "validatorAddresses")
|
||||
val validatorAddresses: List<String>?,
|
||||
@Json(name = "providerId")
|
||||
val providerId: String?,
|
||||
) {
|
||||
|
||||
enum class BalanceType {
|
||||
@Json(name = "available")
|
||||
AVAILABLE,
|
||||
|
||||
@Json(name = "staked")
|
||||
STAKED,
|
||||
|
||||
@Json(name = "unstaking")
|
||||
UNSTAKING,
|
||||
|
||||
@Json(name = "unstaked")
|
||||
UNSTAKED,
|
||||
|
||||
@Json(name = "preparing")
|
||||
PREPARING,
|
||||
|
||||
@Json(name = "rewards")
|
||||
REWARDS,
|
||||
|
||||
@Json(name = "locked")
|
||||
LOCKED,
|
||||
|
||||
@Json(name = "unlocking")
|
||||
UNLOCKING,
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class PendingAction(
|
||||
@Json(name = "type")
|
||||
val type: StakingActionType,
|
||||
@Json(name = "passthrough")
|
||||
val passthrough: String,
|
||||
@Json(name = "args")
|
||||
val args: PendingActionArgs?,
|
||||
) {
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class PendingActionArgs(
|
||||
@Json(name = "amount")
|
||||
val amount: Amount?,
|
||||
@Json(name = "duration")
|
||||
val duration: Duration?,
|
||||
@Json(name = "validatorAddress")
|
||||
val validatorAddress: Required?,
|
||||
@Json(name = "validatorAddresses")
|
||||
val validatorAddresses: Required?,
|
||||
@Json(name = "nfts")
|
||||
val nfts: List<Nft>?,
|
||||
@Json(name = "tronResource")
|
||||
val tronResource: TronResource?,
|
||||
@Json(name = "signatureVerification")
|
||||
val signatureVerification: Required?,
|
||||
) {
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Amount(
|
||||
@Json(name = "required")
|
||||
val required: Boolean,
|
||||
@Json(name = "minimum")
|
||||
val minimum: BigDecimal?,
|
||||
@Json(name = "maximum")
|
||||
val maximum: BigDecimal?,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Duration(
|
||||
@Json(name = "required")
|
||||
val required: Boolean,
|
||||
@Json(name = "minimum")
|
||||
val minimum: Int?,
|
||||
@Json(name = "maximum")
|
||||
val maximum: Int?,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Nft(
|
||||
@Json(name = "baycId")
|
||||
val baycId: Required?,
|
||||
@Json(name = "maycId")
|
||||
val maycId: Required?,
|
||||
@Json(name = "bakcId")
|
||||
val bakcId: Required?,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class TronResource(
|
||||
@Json(name = "required")
|
||||
val required: Boolean,
|
||||
@Json(name = "options")
|
||||
val options: List<String>,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Required(
|
||||
@Json(name = "required")
|
||||
val required: Boolean,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -4,10 +4,10 @@ import com.squareup.moshi.Moshi
|
|||
import com.squareup.moshi.adapters.PolymorphicJsonAdapterFactory
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import com.tangem.common.json.MoshiJsonConverter
|
||||
import com.tangem.datasource.api.common.BigDecimalAdapter
|
||||
import com.tangem.datasource.api.common.DateTimeAdapter
|
||||
import com.tangem.datasource.api.common.LocalDateAdapter
|
||||
import com.tangem.datasource.api.stakekit.UnknownEnumMoshiAdapter
|
||||
import com.tangem.datasource.api.common.adapter.BigDecimalAdapter
|
||||
import com.tangem.datasource.api.common.adapter.DateTimeAdapter
|
||||
import com.tangem.datasource.api.common.adapter.LocalDateAdapter
|
||||
import com.tangem.datasource.api.common.adapter.UnknownEnumMoshiAdapter
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.Token
|
||||
import com.tangem.datasource.config.models.ProviderModel
|
||||
import dagger.Module
|
||||
|
|
|
|||
|
|
@ -580,6 +580,8 @@
|
|||
<string name="token_details_hide_alert_message">Вы скрываете токен с главного экрана, но в любой момент сможете добавить его обратно через страницу управления токенами.</string>
|
||||
<string name="token_details_hide_alert_title">Скрыть %s</string>
|
||||
<string name="token_details_hide_token">Скрыть токен</string>
|
||||
<string name="token_details_staking_block_subtitle">Стейкинг позволяет вам зарабатывать %1$s и получать вознаграждения каждые %2$s дней</string>
|
||||
<string name="token_details_staking_block_title">Зарабатывайте до %s вознаграждений за стейкинг ежегодно</string>
|
||||
<string name="token_details_token_type_subtitle">%1$s токен в сети %%image%% %2$s</string>
|
||||
<string name="token_details_token_type_subtitle_no_standard">Токен в сети %%image%% %1$s</string>
|
||||
<string name="token_details_unable_hide_alert_message">Токен %1$s (%2$s) является основной валютой в сети %3$s и не может быть скрыт до тех пор, пока у вас в списке есть другие токены этой сети</string>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
<string name="app_settings_theme_mode_light">Light</string>
|
||||
<string name="app_settings_theme_mode_system">System default</string>
|
||||
<string name="app_settings_theme_selector_title">Theme</string>
|
||||
<string name="app_settings_title">App Settings</string>
|
||||
<string name="app_settings_title">App settings</string>
|
||||
<string name="balance_hidden_description">To hide or show your balances, simply flip your device screen down, or switch it off in Settings</string>
|
||||
<string name="balance_hidden_do_not_show_button">Don\'t show again</string>
|
||||
<string name="balance_hidden_got_it_button">Got it</string>
|
||||
|
|
@ -49,7 +49,7 @@
|
|||
<string name="card_settings_change_access_code_footer">Access code will be changed on this card only</string>
|
||||
<string name="card_settings_reset_card_to_factory">Reset to Factory Settings</string>
|
||||
<string name="card_settings_security_mode">Security Mode</string>
|
||||
<string name="card_settings_title">Card Settings</string>
|
||||
<string name="card_settings_title">Card settings</string>
|
||||
<string name="cardano_coin_will_be_send_with_token_description">In addition to network fee, the Cardano network charges %1$s ADA when transacting with the %2$s token</string>
|
||||
<string name="cardano_coin_will_be_send_with_token_title">Cardano transaction requirements</string>
|
||||
<string name="cardano_insufficient_balance_to_send_token_description">To make a %1$s transaction, you must deposit some ADA to cover the network fee and minimum ADA value (5 ADA recommended)</string>
|
||||
|
|
@ -173,7 +173,7 @@
|
|||
<string name="details_row_description_flip_to_hide">Flip your device screen down to quickly hide and show balances</string>
|
||||
<string name="details_row_subtitle_signed_hashes_format">%s hashes</string>
|
||||
<string name="details_row_title_cid">Card ID</string>
|
||||
<string name="details_row_title_contact_to_support">Contact to Support</string>
|
||||
<string name="details_row_title_contact_to_support">Contact support</string>
|
||||
<string name="details_row_title_create_backup">Link More Cards</string>
|
||||
<string name="details_row_title_currency">App Currency</string>
|
||||
<string name="details_row_title_flip_to_hide">Flip-to-Hide Balances</string>
|
||||
|
|
@ -181,7 +181,7 @@
|
|||
<string name="details_row_title_signed_hashes">Signed</string>
|
||||
<string name="details_title">Details</string>
|
||||
<string name="disclaimer_error_loading">Check your internet connection or switch to a different network</string>
|
||||
<string name="disclaimer_title">Terms of Service</string>
|
||||
<string name="disclaimer_title">Terms of service</string>
|
||||
<string name="error_wrong_wallet_tapped">You have used a card from another wallet. Tap the card associated with this wallet</string>
|
||||
<string name="exchange_tokens_available_tokens_header">My tokens</string>
|
||||
<string name="exchange_tokens_empty_tokens">You haven\'t added any tokens yet. Add tokens via Market to swap</string>
|
||||
|
|
@ -573,6 +573,8 @@
|
|||
<string name="token_details_hide_alert_message">You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page.</string>
|
||||
<string name="token_details_hide_alert_title">Hide %s</string>
|
||||
<string name="token_details_hide_token">Hide token</string>
|
||||
<string name="token_details_staking_block_subtitle">Staking allows you to earn %1$s and get rewards every %2$s days</string>
|
||||
<string name="token_details_staking_block_title">Earn up to %s staking rewards yearly</string>
|
||||
<string name="token_details_token_type_subtitle">%1$s token in %%image%% %2$s network</string>
|
||||
<string name="token_details_token_type_subtitle_no_standard">Token in %%image%% %1$s network</string>
|
||||
<string name="token_details_unable_hide_alert_message">The %1$s (%2$s) token is the main currency on the %3$s network and cannot be hidden as long as you have other tokens on this network in the list</string>
|
||||
|
|
|
|||
|
|
@ -4,15 +4,12 @@ import androidx.annotation.DrawableRes
|
|||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.ColorMatrix
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.currency.DefaultCurrencyIcon
|
||||
|
||||
private const val GRAY_SCALE_SATURATION = 0f
|
||||
private const val GRAY_SCALE_ALPHA = 0.4f
|
||||
import com.tangem.core.ui.utils.GRAY_SCALE_ALPHA
|
||||
import com.tangem.core.ui.utils.GrayscaleColorFilter
|
||||
|
||||
/**
|
||||
* Simple icon from network
|
||||
|
|
@ -46,7 +43,4 @@ fun FiatIcon(
|
|||
},
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
private val GrayscaleColorFilter: ColorFilter
|
||||
get() = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(GRAY_SCALE_SATURATION) })
|
||||
}
|
||||
|
|
@ -10,14 +10,11 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.ColorMatrix
|
||||
import com.tangem.core.ui.components.CircleShimmer
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
private const val GRAY_SCALE_SATURATION = 0f
|
||||
private const val GRAY_SCALE_ALPHA = 0.4f
|
||||
private const val NORMAL_ALPHA = 1f
|
||||
import com.tangem.core.ui.utils.GRAY_SCALE_ALPHA
|
||||
import com.tangem.core.ui.utils.GrayscaleColorFilter
|
||||
import com.tangem.core.ui.utils.NORMAL_ALPHA
|
||||
|
||||
/**
|
||||
* Cryptocurrency icon with network badge
|
||||
|
|
@ -112,7 +109,4 @@ private fun BoxScope.ContentIconContainer(
|
|||
@Composable
|
||||
private inline fun BaseContainer(modifier: Modifier = Modifier, content: @Composable BoxScope.() -> Unit) {
|
||||
Box(modifier = modifier.size(size = TangemTheme.dimens.size40), content = content)
|
||||
}
|
||||
|
||||
private val GrayscaleColorFilter: ColorFilter
|
||||
get() = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(GRAY_SCALE_SATURATION) })
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.core.ui.utils
|
||||
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.ColorMatrix
|
||||
|
||||
const val GRAY_SCALE_SATURATION = 0f
|
||||
const val GRAY_SCALE_ALPHA = 0.4f
|
||||
const val NORMAL_ALPHA = 1f
|
||||
|
||||
val GrayscaleColorFilter: ColorFilter
|
||||
get() = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(GRAY_SCALE_SATURATION) })
|
||||
1
data/staking/.gitignore
vendored
Normal file
1
data/staking/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
36
data/staking/build.gradle.kts
Normal file
36
data/staking/build.gradle.kts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
plugins {
|
||||
alias(deps.plugins.android.library)
|
||||
alias(deps.plugins.kotlin.android)
|
||||
alias(deps.plugins.kotlin.kapt)
|
||||
alias(deps.plugins.hilt.android)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.tangem.data.staking"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation(projects.core.datasource)
|
||||
implementation(projects.core.utils)
|
||||
implementation(projects.domain.staking)
|
||||
implementation(projects.features.staking.api)
|
||||
|
||||
|
||||
// region DI
|
||||
implementation(deps.hilt.android)
|
||||
kapt(deps.hilt.kapt)
|
||||
// endregion
|
||||
|
||||
// region Others dependencies
|
||||
implementation(deps.jodatime)
|
||||
implementation(deps.kotlin.coroutines)
|
||||
implementation(deps.moshi)
|
||||
implementation(deps.moshi.kotlin)
|
||||
|
||||
implementation(deps.tangem.blockchain) {
|
||||
exclude(module = "joda-time")
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
package com.tangem.data.staking
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
import com.tangem.datasource.api.stakekit.StakeKitApi
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.features.staking.api.featuretoggles.StakingFeatureToggles
|
||||
|
||||
internal class DefaultStakingRepository(
|
||||
private val stakeKitApi: StakeKitApi,
|
||||
private val stakingFeatureToggles: StakingFeatureToggles,
|
||||
) : StakingRepository {
|
||||
|
||||
override fun getStakingAvailability(blockchainId: String): StakingAvailability {
|
||||
if (!stakingFeatureToggles.isStakingEnabled) {
|
||||
return StakingAvailability.Unavailable
|
||||
}
|
||||
|
||||
return integrationIdMap[Blockchain.fromId(blockchainId)]?.let {
|
||||
StakingAvailability.Available(it)
|
||||
} ?: StakingAvailability.Unavailable
|
||||
}
|
||||
|
||||
override suspend fun getEntryInfo(integrationId: String): StakingEntryInfo {
|
||||
val yield = stakeKitApi.getSingleYield(integrationId).getOrThrow()
|
||||
|
||||
return StakingEntryInfo(
|
||||
interestRate = yield.apy,
|
||||
periodInDays = yield.metadata.cooldownPeriod.days,
|
||||
tokenSymbol = yield.token.symbol,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val SOLANA_INTEGRATION_ID = "solana-sol-native-multivalidator-staking"
|
||||
private const val COSMOS_INTEGRATION_ID = "cosmos-atom-native-staking"
|
||||
private const val POLKADOT_INTEGRATION_ID = "polkadot-dot-validator-staking"
|
||||
private const val ETHEREUM_INTEGRATION_ID = "ethereum-matic-native-staking"
|
||||
private const val AVALANCHE_INTEGRATION_ID = "avalanche-avax-native-staking"
|
||||
private const val TRON_INTEGRATION_ID = "tron-trx-native-staking"
|
||||
private const val CRONOS_INTEGRATION_ID = "cronos-cro-native-staking"
|
||||
private const val BINANCE_INTEGRATION_ID = "binance-bnb-native-staking"
|
||||
private const val KAVA_INTEGRATION_ID = "kava-kava-native-staking"
|
||||
private const val NEAR_INTEGRATION_ID = "near-near-native-staking"
|
||||
private const val TEZOS_INTEGRATION_ID = "tezos-xtz-native-staking"
|
||||
|
||||
private val integrationIdMap = mapOf(
|
||||
Blockchain.Solana to SOLANA_INTEGRATION_ID,
|
||||
Blockchain.Cosmos to COSMOS_INTEGRATION_ID,
|
||||
Blockchain.Polkadot to POLKADOT_INTEGRATION_ID,
|
||||
Blockchain.Polygon to ETHEREUM_INTEGRATION_ID,
|
||||
Blockchain.Avalanche to AVALANCHE_INTEGRATION_ID,
|
||||
Blockchain.Tron to TRON_INTEGRATION_ID,
|
||||
Blockchain.Cronos to CRONOS_INTEGRATION_ID,
|
||||
Blockchain.Binance to BINANCE_INTEGRATION_ID,
|
||||
Blockchain.Kava to KAVA_INTEGRATION_ID,
|
||||
Blockchain.Near to NEAR_INTEGRATION_ID,
|
||||
Blockchain.Tezos to TEZOS_INTEGRATION_ID,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package com.tangem.data.staking.di
|
||||
|
||||
import com.tangem.data.staking.DefaultStakingRepository
|
||||
import com.tangem.datasource.api.stakekit.StakeKitApi
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.features.staking.api.featuretoggles.StakingFeatureToggles
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object StakingDataModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideStakingRepository(
|
||||
stakeKitApi: StakeKitApi,
|
||||
stakingFeatureToggles: StakingFeatureToggles,
|
||||
): StakingRepository {
|
||||
return DefaultStakingRepository(
|
||||
stakeKitApi = stakeKitApi,
|
||||
stakingFeatureToggles = stakingFeatureToggles,
|
||||
)
|
||||
}
|
||||
}
|
||||
1
domain/staking/.gitignore
vendored
Normal file
1
domain/staking/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
9
domain/staking/build.gradle.kts
Normal file
9
domain/staking/build.gradle.kts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
plugins {
|
||||
alias(deps.plugins.kotlin.jvm)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(deps.kotlin.coroutines)
|
||||
implementation(deps.arrow.core)
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.domain.staking
|
||||
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
|
||||
/**
|
||||
* Use case for getting info about staking availability for certain blockchain.
|
||||
*/
|
||||
class GetStakingAvailabilityUseCase(
|
||||
private val stakingRepository: StakingRepository,
|
||||
) {
|
||||
|
||||
operator fun invoke(blockchainNetworkId: String): StakingAvailability {
|
||||
return stakingRepository.getStakingAvailability(blockchainNetworkId)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.domain.staking
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
|
||||
/**
|
||||
* Use case for getting entry info about staking on token screen.
|
||||
*/
|
||||
class GetStakingEntryInfoUseCase(private val stakingRepository: StakingRepository) {
|
||||
|
||||
suspend operator fun invoke(integrationId: String): Either<Throwable, StakingEntryInfo> {
|
||||
return Either.catch { stakingRepository.getEntryInfo(integrationId) }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.domain.staking.model
|
||||
|
||||
sealed class StakingAvailability {
|
||||
|
||||
data class Available(val integrationId: String) : StakingAvailability()
|
||||
|
||||
data object Unavailable : StakingAvailability()
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tangem.domain.staking.model
|
||||
|
||||
import java.math.BigDecimal
|
||||
|
||||
data class StakingEntryInfo(
|
||||
val interestRate: BigDecimal,
|
||||
val periodInDays: Int,
|
||||
val tokenSymbol: String,
|
||||
)
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.domain.staking.repositories
|
||||
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
|
||||
interface StakingRepository {
|
||||
|
||||
fun getStakingAvailability(blockchainId: String): StakingAvailability
|
||||
|
||||
suspend fun getEntryInfo(integrationId: String): StakingEntryInfo
|
||||
}
|
||||
|
|
@ -12,8 +12,6 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.ColorMatrix
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
|
@ -29,6 +27,8 @@ import com.tangem.core.ui.extensions.resolveReference
|
|||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.utils.GRAY_SCALE_ALPHA
|
||||
import com.tangem.core.ui.utils.GrayscaleColorFilter
|
||||
import com.tangem.feature.swap.models.states.PercentDifference
|
||||
import com.tangem.feature.swap.models.states.ProviderState
|
||||
|
||||
|
|
@ -38,11 +38,6 @@ import com.tangem.feature.swap.models.states.ProviderState
|
|||
* https://www.figma.com/file/Vs6SkVsFnUPsSCNwlnVf5U/Android-%E2%80%93-UI?type=design&node-id=7856-41909&mode=design&t=vo7dyElitnzSPSW3-4
|
||||
*/
|
||||
|
||||
private const val GRAY_SCALE_SATURATION = 0f
|
||||
private const val GRAY_SCALE_ALPHA = 0.4f
|
||||
private val GrayscaleColorFilter: ColorFilter
|
||||
get() = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(GRAY_SCALE_SATURATION) })
|
||||
|
||||
@Composable
|
||||
fun ProviderItemBlock(state: ProviderState, modifier: Modifier = Modifier) {
|
||||
if (state !is ProviderState.Empty) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.features.tokendetails.featuretoggles
|
||||
|
||||
interface TokenDetailsFeatureToggles {
|
||||
|
||||
fun isGenerateXPubEnabled(): Boolean
|
||||
}
|
||||
|
|
@ -75,6 +75,7 @@ dependencies {
|
|||
implementation(projects.domain.balanceHiding)
|
||||
implementation(projects.domain.balanceHiding.models)
|
||||
implementation(projects.domain.transaction)
|
||||
implementation(projects.domain.staking)
|
||||
|
||||
/** Temp dependency to swap domain */
|
||||
implementation(projects.features.swap.domain)
|
||||
|
|
|
|||
|
|
@ -6,5 +6,6 @@ import com.tangem.features.tokendetails.featuretoggles.TokenDetailsFeatureToggle
|
|||
internal class DefaultTokenDetailsFeatureToggles(
|
||||
private val featureTogglesManager: FeatureTogglesManager,
|
||||
) : TokenDetailsFeatureToggles {
|
||||
|
||||
override fun isGenerateXPubEnabled() = featureTogglesManager.isFeatureEnabled(name = "GENERATE_XPUB_ENABLED")
|
||||
}
|
||||
|
|
@ -35,7 +35,7 @@ internal object TokenDetailsPreviewData {
|
|||
|
||||
val tokenInfoBlockStateWithLongNameInMainCurrency = TokenInfoBlockState(
|
||||
name = "Stellar (XLM) with long name test",
|
||||
iconState = TokenInfoBlockState.IconState.CoinIcon(
|
||||
iconState = IconState.CoinIcon(
|
||||
url = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/large/stellar.png",
|
||||
fallbackResId = R.drawable.img_stellar_22,
|
||||
isGrayscale = false,
|
||||
|
|
@ -44,7 +44,7 @@ internal object TokenDetailsPreviewData {
|
|||
)
|
||||
val tokenInfoBlockStateWithLongName = TokenInfoBlockState(
|
||||
name = "Tether (USDT) with long name test",
|
||||
iconState = TokenInfoBlockState.IconState.TokenIcon(
|
||||
iconState = IconState.TokenIcon(
|
||||
url = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/large/stellar.png",
|
||||
fallbackTint = Color.Cyan,
|
||||
fallbackBackground = Color.Blue,
|
||||
|
|
@ -59,7 +59,7 @@ internal object TokenDetailsPreviewData {
|
|||
|
||||
val tokenInfoBlockStateWithLongNameNoStandard = TokenInfoBlockState(
|
||||
name = "Tether (USDT) with long name test",
|
||||
iconState = TokenInfoBlockState.IconState.TokenIcon(
|
||||
iconState = IconState.TokenIcon(
|
||||
url = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/large/stellar.png",
|
||||
fallbackTint = Color.Cyan,
|
||||
fallbackBackground = Color.Blue,
|
||||
|
|
@ -74,7 +74,7 @@ internal object TokenDetailsPreviewData {
|
|||
|
||||
val tokenInfoBlockState = TokenInfoBlockState(
|
||||
name = "Tether USDT",
|
||||
iconState = TokenInfoBlockState.IconState.CustomTokenIcon(
|
||||
iconState = IconState.CustomTokenIcon(
|
||||
tint = Color.Green,
|
||||
background = Color.Magenta,
|
||||
isGrayscale = true,
|
||||
|
|
@ -86,6 +86,13 @@ internal object TokenDetailsPreviewData {
|
|||
),
|
||||
)
|
||||
|
||||
val iconState = IconState.TokenIcon(
|
||||
url = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/large/stellar.png",
|
||||
fallbackTint = Color.Cyan,
|
||||
fallbackBackground = Color.Blue,
|
||||
isGrayscale = false,
|
||||
)
|
||||
|
||||
private val actionButtons = persistentListOf(
|
||||
TokenDetailsActionButton.Buy(dimContent = false, onClick = {}),
|
||||
TokenDetailsActionButton.Send(dimContent = false, onClick = {}),
|
||||
|
|
@ -103,6 +110,8 @@ internal object TokenDetailsPreviewData {
|
|||
|
||||
private val marketPriceLoading = MarketPriceBlockState.Loading(currencySymbol = "USDT")
|
||||
|
||||
private val stakingLoading = StakingBlockState.Loading(iconState = iconState)
|
||||
|
||||
private val pullToRefreshConfig = TokenDetailsPullToRefreshConfig(
|
||||
isRefreshing = false,
|
||||
onRefresh = {},
|
||||
|
|
@ -237,6 +246,7 @@ internal object TokenDetailsPreviewData {
|
|||
tokenInfoBlockState = tokenInfoBlockState,
|
||||
tokenBalanceBlockState = balanceLoading,
|
||||
marketPriceBlockState = marketPriceLoading,
|
||||
stakingBlockState = stakingLoading,
|
||||
notifications = persistentListOf(),
|
||||
txHistoryState = TxHistoryState.Content(
|
||||
contentItems = MutableStateFlow(
|
||||
|
|
@ -250,6 +260,7 @@ internal object TokenDetailsPreviewData {
|
|||
bottomSheetConfig = null,
|
||||
isBalanceHidden = false,
|
||||
isMarketPriceAvailable = false,
|
||||
isStakingAvailable = false,
|
||||
event = consumedEvent(),
|
||||
)
|
||||
|
||||
|
|
@ -267,6 +278,12 @@ internal object TokenDetailsPreviewData {
|
|||
type = PriceChangeType.UP,
|
||||
),
|
||||
),
|
||||
stakingBlockState = StakingBlockState.Content(
|
||||
interestRate = "7.38",
|
||||
periodInDays = 4,
|
||||
tokenSymbol = "XLM",
|
||||
iconState = iconState,
|
||||
),
|
||||
notifications = persistentListOf(),
|
||||
txHistoryState = TxHistoryState.NotSupported(
|
||||
onExploreClick = {},
|
||||
|
|
@ -279,6 +296,7 @@ internal object TokenDetailsPreviewData {
|
|||
bottomSheetConfig = null,
|
||||
isBalanceHidden = false,
|
||||
isMarketPriceAvailable = true,
|
||||
isStakingAvailable = true,
|
||||
event = consumedEvent(),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.state
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
@Immutable
|
||||
internal sealed class IconState {
|
||||
|
||||
abstract val isGrayscale: Boolean
|
||||
|
||||
data class CoinIcon(
|
||||
val url: String?,
|
||||
@DrawableRes val fallbackResId: Int,
|
||||
override val isGrayscale: Boolean,
|
||||
) : IconState()
|
||||
|
||||
data class TokenIcon(
|
||||
val url: String?,
|
||||
val fallbackTint: Color,
|
||||
val fallbackBackground: Color,
|
||||
override val isGrayscale: Boolean,
|
||||
) : IconState()
|
||||
|
||||
data class CustomTokenIcon(
|
||||
val tint: Color,
|
||||
val background: Color,
|
||||
override val isGrayscale: Boolean,
|
||||
) : IconState()
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.state
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
|
||||
@Immutable
|
||||
internal sealed interface StakingBlockState {
|
||||
|
||||
val iconState: IconState
|
||||
|
||||
data class Error(override val iconState: IconState) : StakingBlockState
|
||||
|
||||
data class Loading(override val iconState: IconState) : StakingBlockState
|
||||
|
||||
data class Content(
|
||||
override val iconState: IconState,
|
||||
val interestRate: String,
|
||||
val periodInDays: Int,
|
||||
val tokenSymbol: String,
|
||||
) : StakingBlockState
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ internal data class TokenDetailsState(
|
|||
val tokenInfoBlockState: TokenInfoBlockState,
|
||||
val tokenBalanceBlockState: TokenDetailsBalanceBlockState,
|
||||
val marketPriceBlockState: MarketPriceBlockState,
|
||||
val stakingBlockState: StakingBlockState,
|
||||
val notifications: ImmutableList<TokenDetailsNotification>,
|
||||
val pendingTxs: PersistentList<TransactionState>,
|
||||
val swapTxs: PersistentList<SwapTransactionsState>,
|
||||
|
|
@ -26,5 +27,6 @@ internal data class TokenDetailsState(
|
|||
val bottomSheetConfig: TangemBottomSheetConfig?,
|
||||
val isBalanceHidden: Boolean,
|
||||
val isMarketPriceAvailable: Boolean,
|
||||
val isStakingAvailable: Boolean,
|
||||
val event: StateEvent<TextReference>,
|
||||
)
|
||||
|
|
@ -2,7 +2,6 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state
|
|||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
internal data class TokenInfoBlockState(
|
||||
val name: String,
|
||||
|
|
@ -11,7 +10,7 @@ internal data class TokenInfoBlockState(
|
|||
) {
|
||||
@Immutable
|
||||
sealed class Currency {
|
||||
object Native : Currency()
|
||||
data object Native : Currency()
|
||||
|
||||
/**
|
||||
* @param standardName - token standard. Samples: ERC20, BEP20, BEP2, TRC20 and etc.
|
||||
|
|
@ -24,29 +23,4 @@ internal data class TokenInfoBlockState(
|
|||
@DrawableRes val networkIcon: Int,
|
||||
) : Currency()
|
||||
}
|
||||
|
||||
@Immutable
|
||||
sealed class IconState {
|
||||
|
||||
abstract val isGrayscale: Boolean
|
||||
|
||||
data class CoinIcon(
|
||||
val url: String?,
|
||||
@DrawableRes val fallbackResId: Int,
|
||||
override val isGrayscale: Boolean,
|
||||
) : IconState()
|
||||
|
||||
data class TokenIcon(
|
||||
val url: String?,
|
||||
val fallbackTint: Color,
|
||||
val fallbackBackground: Color,
|
||||
override val isGrayscale: Boolean,
|
||||
) : IconState()
|
||||
|
||||
data class CustomTokenIcon(
|
||||
val tint: Color,
|
||||
val background: Color,
|
||||
override val isGrayscale: Boolean,
|
||||
) : IconState()
|
||||
}
|
||||
}
|
||||
|
|
@ -4,39 +4,39 @@ import com.tangem.core.ui.extensions.getTintForTokenIcon
|
|||
import com.tangem.core.ui.extensions.networkIconResId
|
||||
import com.tangem.core.ui.extensions.tryGetBackgroundForTokenIcon
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenInfoBlockState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.IconState
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class TokenDetailsIconStateConverter : Converter<CryptoCurrency, TokenInfoBlockState.IconState> {
|
||||
internal class TokenDetailsIconStateConverter : Converter<CryptoCurrency, IconState> {
|
||||
|
||||
override fun convert(value: CryptoCurrency): TokenInfoBlockState.IconState {
|
||||
override fun convert(value: CryptoCurrency): IconState {
|
||||
return when (value) {
|
||||
is CryptoCurrency.Coin -> getIconStateForCoin(value)
|
||||
is CryptoCurrency.Token -> getIconStateForToken(value)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getIconStateForCoin(coin: CryptoCurrency.Coin): TokenInfoBlockState.IconState.CoinIcon {
|
||||
return TokenInfoBlockState.IconState.CoinIcon(
|
||||
private fun getIconStateForCoin(coin: CryptoCurrency.Coin): IconState.CoinIcon {
|
||||
return IconState.CoinIcon(
|
||||
url = coin.iconUrl,
|
||||
fallbackResId = coin.networkIconResId,
|
||||
isGrayscale = coin.network.isTestnet,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getIconStateForToken(token: CryptoCurrency.Token): TokenInfoBlockState.IconState {
|
||||
private fun getIconStateForToken(token: CryptoCurrency.Token): IconState {
|
||||
val isGrayscale = token.network.isTestnet
|
||||
val background = token.tryGetBackgroundForTokenIcon(isGrayscale)
|
||||
val tint = getTintForTokenIcon(background)
|
||||
|
||||
return if (token.isCustom && token.iconUrl == null) {
|
||||
TokenInfoBlockState.IconState.CustomTokenIcon(
|
||||
IconState.CustomTokenIcon(
|
||||
tint = tint,
|
||||
background = background,
|
||||
isGrayscale = isGrayscale,
|
||||
)
|
||||
} else {
|
||||
TokenInfoBlockState.IconState.TokenIcon(
|
||||
IconState.TokenIcon(
|
||||
url = token.iconUrl,
|
||||
isGrayscale = isGrayscale,
|
||||
fallbackTint = tint,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.extensions.networkIconResId
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.*
|
||||
|
|
@ -16,6 +17,7 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.Toke
|
|||
import com.tangem.features.tokendetails.featuretoggles.TokenDetailsFeatureToggles
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isBitcoin
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
@ -25,11 +27,13 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
internal class TokenDetailsSkeletonStateConverter(
|
||||
private val clickIntents: TokenDetailsClickIntents,
|
||||
private val featureToggles: TokenDetailsFeatureToggles,
|
||||
private val stakingAvailabilityProvider: Provider<StakingAvailability>,
|
||||
) : Converter<CryptoCurrency, TokenDetailsState> {
|
||||
|
||||
private val iconStateConverter by lazy { TokenDetailsIconStateConverter() }
|
||||
|
||||
override fun convert(value: CryptoCurrency): TokenDetailsState {
|
||||
val iconState = iconStateConverter.convert(value)
|
||||
return TokenDetailsState(
|
||||
topAppBarConfig = TokenDetailsTopAppBarConfig(
|
||||
onBackClick = clickIntents::onBackClick,
|
||||
|
|
@ -37,7 +41,7 @@ internal class TokenDetailsSkeletonStateConverter(
|
|||
),
|
||||
tokenInfoBlockState = TokenInfoBlockState(
|
||||
name = value.name,
|
||||
iconState = iconStateConverter.convert(value),
|
||||
iconState = iconState,
|
||||
currency = when (value) {
|
||||
is CryptoCurrency.Coin -> TokenInfoBlockState.Currency.Native
|
||||
is CryptoCurrency.Token -> TokenInfoBlockState.Currency.Token(
|
||||
|
|
@ -49,6 +53,7 @@ internal class TokenDetailsSkeletonStateConverter(
|
|||
),
|
||||
tokenBalanceBlockState = TokenDetailsBalanceBlockState.Loading(actionButtons = createButtons()),
|
||||
marketPriceBlockState = MarketPriceBlockState.Loading(value.symbol),
|
||||
stakingBlockState = StakingBlockState.Loading(iconState = iconState),
|
||||
notifications = persistentListOf(),
|
||||
pendingTxs = persistentListOf(),
|
||||
swapTxs = persistentListOf(),
|
||||
|
|
@ -62,6 +67,7 @@ internal class TokenDetailsSkeletonStateConverter(
|
|||
bottomSheetConfig = null,
|
||||
isBalanceHidden = true,
|
||||
isMarketPriceAvailable = value.id.rawCurrencyId != null,
|
||||
isStakingAvailable = stakingAvailabilityProvider.invoke() is StakingAvailability.Available,
|
||||
event = consumedEvent(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ import com.tangem.core.ui.extensions.wrappedList
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.common.CardTypesResolver
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.model.*
|
||||
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
|
||||
|
|
@ -38,10 +40,11 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
@Suppress("TooManyFunctions", "LargeClass")
|
||||
@Suppress("TooManyFunctions", "LargeClass", "LongParameterList")
|
||||
internal class TokenDetailsStateFactory(
|
||||
private val currentStateProvider: Provider<TokenDetailsState>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val stakingAvailabilityProvider: Provider<StakingAvailability>,
|
||||
private val clickIntents: TokenDetailsClickIntents,
|
||||
private val featureToggles: TokenDetailsFeatureToggles,
|
||||
symbol: String,
|
||||
|
|
@ -52,6 +55,7 @@ internal class TokenDetailsStateFactory(
|
|||
TokenDetailsSkeletonStateConverter(
|
||||
clickIntents = clickIntents,
|
||||
featureToggles = featureToggles,
|
||||
stakingAvailabilityProvider = stakingAvailabilityProvider,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -98,6 +102,12 @@ internal class TokenDetailsStateFactory(
|
|||
)
|
||||
}
|
||||
|
||||
private val stakingStateConverter by lazy {
|
||||
TokenStakingStateConverter(
|
||||
currentStateProvider = currentStateProvider,
|
||||
)
|
||||
}
|
||||
|
||||
fun getInitialState(screenArgument: CryptoCurrency): TokenDetailsState {
|
||||
return skeletonStateConverter.convert(value = screenArgument)
|
||||
}
|
||||
|
|
@ -201,6 +211,12 @@ internal class TokenDetailsStateFactory(
|
|||
)
|
||||
}
|
||||
|
||||
fun getStateWithStaking(stakingEither: Either<Throwable, StakingEntryInfo>): TokenDetailsState {
|
||||
return currentStateProvider().copy(
|
||||
stakingBlockState = stakingStateConverter.convert(stakingEither),
|
||||
)
|
||||
}
|
||||
|
||||
fun getRefreshingState(): TokenDetailsState {
|
||||
return refreshStateConverter.convert(true)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.StakingBlockState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class TokenStakingStateConverter(
|
||||
private val currentStateProvider: Provider<TokenDetailsState>,
|
||||
) : Converter<Either<Throwable, StakingEntryInfo>, StakingBlockState> {
|
||||
|
||||
override fun convert(value: Either<Throwable, StakingEntryInfo>): StakingBlockState {
|
||||
value.fold(
|
||||
ifLeft = {
|
||||
return StakingBlockState.Error(
|
||||
iconState = currentStateProvider().tokenInfoBlockState.iconState,
|
||||
)
|
||||
},
|
||||
ifRight = {
|
||||
return StakingBlockState.Content(
|
||||
interestRate = BigDecimalFormatter.formatPercent(
|
||||
percent = it.interestRate,
|
||||
useAbsoluteValue = true,
|
||||
),
|
||||
periodInDays = it.periodInDays,
|
||||
tokenSymbol = it.tokenSymbol,
|
||||
iconState = currentStateProvider().tokenInfoBlockState.iconState,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -42,8 +42,10 @@ import com.tangem.core.ui.extensions.resolveReference
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.StakingBlockState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.*
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsBalanceBlock
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsDialogs
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsTopAppBar
|
||||
|
|
@ -133,6 +135,14 @@ internal fun TokenDetailsScreen(state: TokenDetailsState) {
|
|||
)
|
||||
}
|
||||
|
||||
if (state.isStakingAvailable) {
|
||||
item(
|
||||
key = StakingBlockState::class.java,
|
||||
contentType = StakingBlockState::class.java,
|
||||
content = { TokenStakingBlock(modifier = itemModifier, state = state.stakingBlockState) },
|
||||
)
|
||||
}
|
||||
|
||||
swapTransactionsItems(
|
||||
state.swapTxs,
|
||||
itemModifier,
|
||||
|
|
|
|||
|
|
@ -21,26 +21,21 @@ import coil.request.ImageRequest
|
|||
import com.tangem.core.ui.components.CircleShimmer
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.utils.ImageBackgroundContrastChecker
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenInfoBlockState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.IconState
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
internal fun CurrencyIcon(
|
||||
icon: TokenInfoBlockState.IconState,
|
||||
alpha: Float,
|
||||
colorFilter: ColorFilter?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
internal fun CurrencyIcon(icon: IconState, alpha: Float, colorFilter: ColorFilter?, modifier: Modifier = Modifier) {
|
||||
when (icon) {
|
||||
is TokenInfoBlockState.IconState.CoinIcon -> CoinIcon(
|
||||
is IconState.CoinIcon -> CoinIcon(
|
||||
modifier = modifier,
|
||||
url = icon.url,
|
||||
fallbackResId = icon.fallbackResId,
|
||||
alpha = alpha,
|
||||
colorFilter = colorFilter,
|
||||
)
|
||||
is TokenInfoBlockState.IconState.TokenIcon -> TokenIcon(
|
||||
is IconState.TokenIcon -> TokenIcon(
|
||||
modifier = modifier,
|
||||
url = icon.url,
|
||||
alpha = alpha,
|
||||
|
|
@ -54,7 +49,7 @@ internal fun CurrencyIcon(
|
|||
)
|
||||
},
|
||||
)
|
||||
is TokenInfoBlockState.IconState.CustomTokenIcon -> CustomTokenIcon(
|
||||
is IconState.CustomTokenIcon -> CustomTokenIcon(
|
||||
modifier = modifier,
|
||||
tint = icon.tint,
|
||||
background = icon.background,
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.ColorMatrix
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -20,14 +18,13 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
|||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.utils.GRAY_SCALE_ALPHA
|
||||
import com.tangem.core.ui.utils.GrayscaleColorFilter
|
||||
import com.tangem.core.ui.utils.NORMAL_ALPHA
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenInfoBlockState
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
|
||||
private const val GRAY_SCALE_SATURATION = 0f
|
||||
private const val GRAY_SCALE_ALPHA = 0.4f
|
||||
private const val NORMAL_ALPHA = 1f
|
||||
|
||||
@Composable
|
||||
internal fun TokenInfoBlock(state: TokenInfoBlockState, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
|
|
@ -132,10 +129,10 @@ private fun extractNetwork(tokenCurrency: TokenInfoBlockState.Currency.Token): E
|
|||
}
|
||||
}
|
||||
|
||||
private data class ExtractedTokenNetworkText(val normalText: String, val boldText: String)
|
||||
|
||||
private val GrayscaleColorFilter: ColorFilter
|
||||
get() = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(GRAY_SCALE_SATURATION) })
|
||||
private data class ExtractedTokenNetworkText(
|
||||
val normalText: String,
|
||||
val boldText: String,
|
||||
)
|
||||
|
||||
@Preview
|
||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,206 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.utils.GRAY_SCALE_ALPHA
|
||||
import com.tangem.core.ui.utils.GrayscaleColorFilter
|
||||
import com.tangem.core.ui.utils.NORMAL_ALPHA
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.IconState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.StakingBlockState
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
|
||||
/**
|
||||
* Token staking block
|
||||
*
|
||||
* @param state component state
|
||||
* @param modifier modifier
|
||||
*/
|
||||
@Composable
|
||||
internal fun TokenStakingBlock(state: StakingBlockState, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.background.primary,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = TangemTheme.dimens.size72)
|
||||
.padding(all = TangemTheme.dimens.spacing12),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
Content(state = state)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Content(state: StakingBlockState, modifier: Modifier = Modifier) {
|
||||
AnimatedContent(
|
||||
modifier = modifier.heightIn(min = TangemTheme.dimens.size60),
|
||||
targetState = state,
|
||||
contentAlignment = Alignment.CenterStart,
|
||||
label = "Update the content",
|
||||
) { stakingBlockState ->
|
||||
when (stakingBlockState) {
|
||||
is StakingBlockState.Content -> {
|
||||
StakingContent(
|
||||
stakingBlockState = stakingBlockState,
|
||||
iconState = stakingBlockState.iconState,
|
||||
)
|
||||
}
|
||||
is StakingBlockState.Loading -> {
|
||||
StakingLoading(
|
||||
iconState = stakingBlockState.iconState,
|
||||
)
|
||||
}
|
||||
is StakingBlockState.Error -> Row {} // TODO staking
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StakingContent(stakingBlockState: StakingBlockState.Content, iconState: IconState) {
|
||||
Column {
|
||||
Row {
|
||||
val (alpha, colorFilter) = remember(iconState.isGrayscale) {
|
||||
if (iconState.isGrayscale) {
|
||||
GRAY_SCALE_ALPHA to GrayscaleColorFilter
|
||||
} else {
|
||||
NORMAL_ALPHA to null
|
||||
}
|
||||
}
|
||||
CurrencyIcon(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size20)
|
||||
.clip(TangemTheme.shapes.roundedCorners8)
|
||||
.align(Alignment.CenterVertically),
|
||||
icon = iconState,
|
||||
alpha = alpha,
|
||||
colorFilter = colorFilter,
|
||||
)
|
||||
SpacerW8()
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(
|
||||
R.string.token_details_staking_block_title,
|
||||
stakingBlockState.interestRate,
|
||||
),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.size(TangemTheme.dimens.size4))
|
||||
|
||||
Text(
|
||||
text = stringResource(
|
||||
R.string.token_details_staking_block_subtitle,
|
||||
stakingBlockState.tokenSymbol,
|
||||
stakingBlockState.periodInDays,
|
||||
),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.size(TangemTheme.dimens.size8))
|
||||
}
|
||||
}
|
||||
SecondaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Stake",
|
||||
onClick = { /* [REDACTED_TODO_COMMENT] */ },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StakingLoading(iconState: IconState) {
|
||||
Column {
|
||||
Row {
|
||||
val (alpha, colorFilter) = remember(iconState.isGrayscale) {
|
||||
if (iconState.isGrayscale) {
|
||||
GRAY_SCALE_ALPHA to GrayscaleColorFilter
|
||||
} else {
|
||||
NORMAL_ALPHA to null
|
||||
}
|
||||
}
|
||||
CurrencyIcon(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size20)
|
||||
.clip(TangemTheme.shapes.roundedCorners8)
|
||||
.align(Alignment.CenterVertically),
|
||||
icon = iconState,
|
||||
alpha = alpha,
|
||||
colorFilter = colorFilter,
|
||||
)
|
||||
SpacerW8()
|
||||
Column {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(TangemTheme.dimens.size20),
|
||||
)
|
||||
Spacer(modifier = Modifier.size(TangemTheme.dimens.size4))
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(TangemTheme.dimens.size20),
|
||||
)
|
||||
Spacer(modifier = Modifier.size(TangemTheme.dimens.size8))
|
||||
}
|
||||
}
|
||||
SecondaryButton(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = "Loading", // TODO staking
|
||||
onClick = { /* [REDACTED_TODO_COMMENT] */ },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun Preview_TokenStakingBlock(
|
||||
@PreviewParameter(StakingBlockStateProvider::class)
|
||||
state: StakingBlockState,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
TokenStakingBlock(state = state)
|
||||
}
|
||||
}
|
||||
|
||||
private class StakingBlockStateProvider : CollectionPreviewParameterProvider<StakingBlockState>(
|
||||
collection = listOf(
|
||||
StakingBlockState.Content(
|
||||
iconState = iconState,
|
||||
interestRate = "10",
|
||||
periodInDays = 4,
|
||||
tokenSymbol = "SOL",
|
||||
),
|
||||
StakingBlockState.Loading(iconState = iconState),
|
||||
StakingBlockState.Error(iconState = iconState),
|
||||
),
|
||||
)
|
||||
|
||||
private val iconState = IconState.TokenIcon(
|
||||
url = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/large/stellar.png",
|
||||
fallbackTint = Color.Cyan,
|
||||
fallbackBackground = Color.Blue,
|
||||
isGrayscale = false,
|
||||
)
|
||||
// endregion Preview
|
||||
|
|
@ -27,6 +27,9 @@ import com.tangem.domain.common.util.cardTypesResolver
|
|||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.settings.ShouldShowSwapPromoTokenUseCase
|
||||
import com.tangem.domain.staking.GetStakingAvailabilityUseCase
|
||||
import com.tangem.domain.staking.GetStakingEntryInfoUseCase
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.tokens.*
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction.TransactionInfo
|
||||
|
|
@ -97,6 +100,8 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
private val shouldShowSwapPromoTokenUseCase: ShouldShowSwapPromoTokenUseCase,
|
||||
private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase,
|
||||
private val getExtendedPublicKeyForCurrencyUseCase: GetExtendedPublicKeyForCurrencyUseCase,
|
||||
private val getStakingEntryInfoUseCase: GetStakingEntryInfoUseCase,
|
||||
private val getStakingAvailabilityUseCase: GetStakingAvailabilityUseCase,
|
||||
private val swapRepository: SwapRepository,
|
||||
private val swapTransactionRepository: SwapTransactionRepository,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
|
|
@ -107,8 +112,8 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||
private val hapticManager: HapticManager,
|
||||
private val clipboardManager: ClipboardManager,
|
||||
tokenDetailsFeatureToggles: TokenDetailsFeatureToggles,
|
||||
getUserWalletUseCase: GetUserWalletUseCase,
|
||||
featureToggles: TokenDetailsFeatureToggles,
|
||||
deepLinksRegistry: DeepLinksRegistry,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel(), DefaultLifecycleObserver, TokenDetailsClickIntents {
|
||||
|
|
@ -137,10 +142,13 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
private val stateFactory = TokenDetailsStateFactory(
|
||||
currentStateProvider = Provider { uiState },
|
||||
appCurrencyProvider = Provider(selectedAppCurrencyFlow::value),
|
||||
stakingAvailabilityProvider = Provider {
|
||||
getStakingAvailabilityUseCase.invoke(cryptoCurrency.network.id.value)
|
||||
},
|
||||
clickIntents = this,
|
||||
symbol = cryptoCurrency.symbol,
|
||||
decimals = cryptoCurrency.decimals,
|
||||
featureToggles = featureToggles,
|
||||
featureToggles = tokenDetailsFeatureToggles,
|
||||
)
|
||||
|
||||
private val exchangeStatusFactory by lazy(mode = LazyThreadSafetyMode.NONE) {
|
||||
|
|
@ -207,6 +215,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
subscribeOnCurrencyStatusUpdates()
|
||||
subscribeOnExchangeTransactionsUpdates()
|
||||
updateTxHistory(refresh = false, showItemsLoading = true)
|
||||
updateStakingInfo()
|
||||
}
|
||||
|
||||
private fun handleBalanceHiding(owner: LifecycleOwner) {
|
||||
|
|
@ -358,6 +367,16 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateStakingInfo() {
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
val stakingAvailability = getStakingAvailabilityUseCase(cryptoCurrency.network.id.value)
|
||||
if (stakingAvailability is StakingAvailability.Available) {
|
||||
val stakingInfo = getStakingEntryInfoUseCase(stakingAvailability.integrationId)
|
||||
uiState = stateFactory.getStateWithStaking(stakingInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateTopBarMenu() {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
uiState = stateFactory.getStateWithUpdatedMenu(
|
||||
|
|
|
|||
|
|
@ -190,6 +190,7 @@ include(":domain:onboarding")
|
|||
include(":domain:feedback")
|
||||
include(":domain:qr-scanning")
|
||||
include(":domain:qr-scanning:models")
|
||||
include(":domain:staking")
|
||||
// endregion Domain modules
|
||||
|
||||
// region Data modules
|
||||
|
|
@ -209,4 +210,5 @@ include(":data:promo")
|
|||
include(":data:onboarding")
|
||||
include(":data:feedback")
|
||||
include(":data:qr-scanning")
|
||||
include(":data:staking")
|
||||
// endregion Data modules
|
||||
Loading…
Add table
Add a link
Reference in a new issue