Updated on 2026-08-14
This commit is contained in:
commit
246ab4b47e
25 changed files with 362 additions and 3 deletions
|
|
@ -342,6 +342,8 @@ dependencies {
|
||||||
implementation(projects.features.tokenRecieve.impl)
|
implementation(projects.features.tokenRecieve.impl)
|
||||||
implementation(projects.features.yieldSupply.api)
|
implementation(projects.features.yieldSupply.api)
|
||||||
implementation(projects.features.yieldSupply.impl)
|
implementation(projects.features.yieldSupply.impl)
|
||||||
|
implementation(projects.features.polymarket.api)
|
||||||
|
implementation(projects.features.polymarket.impl)
|
||||||
implementation(projects.features.approval.api)
|
implementation(projects.features.approval.api)
|
||||||
implementation(projects.features.approval.impl)
|
implementation(projects.features.approval.impl)
|
||||||
implementation(projects.features.forYou.api)
|
implementation(projects.features.forYou.api)
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ import com.tangem.features.tokendetails.TokenDetailsComponent
|
||||||
import com.tangem.features.virtualaccount.onboarding.component.VirtualAccountOnboardingComponent
|
import com.tangem.features.virtualaccount.onboarding.component.VirtualAccountOnboardingComponent
|
||||||
import com.tangem.features.wallet.WalletEntryComponent
|
import com.tangem.features.wallet.WalletEntryComponent
|
||||||
import com.tangem.features.walletconnect.components.WalletConnectEntryComponent
|
import com.tangem.features.walletconnect.components.WalletConnectEntryComponent
|
||||||
|
import com.tangem.features.polymarket.api.PolymarketComponent
|
||||||
import com.tangem.features.yield.supply.api.YieldSupplyEntryComponent
|
import com.tangem.features.yield.supply.api.YieldSupplyEntryComponent
|
||||||
import com.tangem.tap.features.details.ui.appcurrency.api.AppCurrencySelectorComponent
|
import com.tangem.tap.features.details.ui.appcurrency.api.AppCurrencySelectorComponent
|
||||||
import com.tangem.tap.features.details.ui.appsettings.api.AppSettingsComponent
|
import com.tangem.tap.features.details.ui.appsettings.api.AppSettingsComponent
|
||||||
|
|
@ -118,6 +119,7 @@ internal class ChildFactory @Inject constructor(
|
||||||
private val kycComponentFactory: KycComponent.Factory,
|
private val kycComponentFactory: KycComponent.Factory,
|
||||||
private val surveyComponentFactory: SurveyComponent.Factory,
|
private val surveyComponentFactory: SurveyComponent.Factory,
|
||||||
private val yieldSupplyEntryComponentFactory: YieldSupplyEntryComponent.Factory,
|
private val yieldSupplyEntryComponentFactory: YieldSupplyEntryComponent.Factory,
|
||||||
|
private val polymarketComponentFactory: PolymarketComponent.Factory,
|
||||||
private val feedEntryComponentFactory: FeedEntryComponent.Factory,
|
private val feedEntryComponentFactory: FeedEntryComponent.Factory,
|
||||||
private val addressBookComponentFactory: AddressBookComponent.Factory,
|
private val addressBookComponentFactory: AddressBookComponent.Factory,
|
||||||
) {
|
) {
|
||||||
|
|
@ -748,6 +750,13 @@ internal class ChildFactory @Inject constructor(
|
||||||
componentFactory = yieldSupplyEntryComponentFactory,
|
componentFactory = yieldSupplyEntryComponentFactory,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
is AppRoute.Polymarket -> {
|
||||||
|
createComponentChild(
|
||||||
|
context = context,
|
||||||
|
params = PolymarketComponent.Params(userWalletId = route.userWalletId),
|
||||||
|
componentFactory = polymarketComponentFactory,
|
||||||
|
)
|
||||||
|
}
|
||||||
is AppRoute.NewsDetails -> {
|
is AppRoute.NewsDetails -> {
|
||||||
createComponentChild(
|
createComponentChild(
|
||||||
context = context,
|
context = context,
|
||||||
|
|
|
||||||
|
|
@ -558,6 +558,11 @@ sealed class AppRoute(val path: String) : Route {
|
||||||
val apy: String,
|
val apy: String,
|
||||||
) : AppRoute(path = "/yield_supply_entry/${userWalletId.stringValue}/${cryptoCurrency.symbol}")
|
) : AppRoute(path = "/yield_supply_entry/${userWalletId.stringValue}/${cryptoCurrency.symbol}")
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Polymarket(
|
||||||
|
val userWalletId: UserWalletId,
|
||||||
|
) : AppRoute(path = "/polymarket/${userWalletId.stringValue}")
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class NewsDetails(val newsId: Int) : AppRoute(path = "/news_details/$newsId")
|
data class NewsDetails(val newsId: Int) : AppRoute(path = "/news_details/$newsId")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -182,5 +182,9 @@
|
||||||
{
|
{
|
||||||
"name": "TWI_1192_TANGEM_PAY_CASHBACK_ENABLED",
|
"name": "TWI_1192_TANGEM_PAY_CASHBACK_ENABLED",
|
||||||
"version": "undefined"
|
"version": "undefined"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AND_16204_POLYMARKET_ENABLED",
|
||||||
|
"version": "undefined"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -874,6 +874,8 @@
|
||||||
<string name="main_add_funds_promo_description">Buy or receive crypto to start using your wallet.</string>
|
<string name="main_add_funds_promo_description">Buy or receive crypto to start using your wallet.</string>
|
||||||
<string name="main_add_funds_promo_title">Get your first crypto</string>
|
<string name="main_add_funds_promo_title">Get your first crypto</string>
|
||||||
<string name="main_empty_tokens_list_message">To begin tracking your crypto assets and transactions, add tokens</string>
|
<string name="main_empty_tokens_list_message">To begin tracking your crypto assets and transactions, add tokens</string>
|
||||||
|
<string name="prediction_account_subtitle">Add funds & start predict</string>
|
||||||
|
<string name="prediction_account_title">Prediction account</string>
|
||||||
<string name="main_manage_tokens">Manage tokens</string>
|
<string name="main_manage_tokens">Manage tokens</string>
|
||||||
<string name="main_qr_scan_hint">Scan QR code to send funds or connect to an app</string>
|
<string name="main_qr_scan_hint">Scan QR code to send funds or connect to an app</string>
|
||||||
<string name="main_scan_card_warning_view_subtitle">To access all the networks you need to scan the card</string>
|
<string name="main_scan_card_warning_view_subtitle">To access all the networks you need to scan the card</string>
|
||||||
|
|
|
||||||
21
features/polymarket/api/build.gradle.kts
Normal file
21
features/polymarket/api/build.gradle.kts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
plugins {
|
||||||
|
alias(deps.plugins.android.library)
|
||||||
|
alias(deps.plugins.kotlin.android)
|
||||||
|
id("configuration")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "com.tangem.features.polymarket.api"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
/** Core */
|
||||||
|
implementation(projects.core.decompose)
|
||||||
|
implementation(projects.core.ui)
|
||||||
|
|
||||||
|
/** Domain */
|
||||||
|
implementation(projects.domain.models)
|
||||||
|
|
||||||
|
/** Compose */
|
||||||
|
implementation(deps.compose.runtime)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.tangem.features.polymarket.api
|
||||||
|
|
||||||
|
import com.tangem.core.decompose.factory.ComponentFactory
|
||||||
|
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||||
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
|
||||||
|
interface PolymarketComponent : ComposableContentComponent {
|
||||||
|
|
||||||
|
data class Params(
|
||||||
|
val userWalletId: UserWalletId,
|
||||||
|
)
|
||||||
|
|
||||||
|
interface Factory : ComponentFactory<Params, PolymarketComponent>
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.tangem.features.polymarket.api
|
||||||
|
|
||||||
|
interface PolymarketFeatureToggles {
|
||||||
|
val isPolymarketEnabled: Boolean
|
||||||
|
}
|
||||||
32
features/polymarket/impl/build.gradle.kts
Normal file
32
features/polymarket/impl/build.gradle.kts
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
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.features.polymarket.impl"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
/** Feature */
|
||||||
|
implementation(projects.features.polymarket.api)
|
||||||
|
|
||||||
|
/** Core */
|
||||||
|
implementation(projects.core.configToggles)
|
||||||
|
implementation(projects.core.decompose)
|
||||||
|
implementation(projects.core.ui)
|
||||||
|
|
||||||
|
/** Compose */
|
||||||
|
implementation(deps.compose.foundation)
|
||||||
|
implementation(deps.compose.runtime)
|
||||||
|
implementation(deps.compose.material3)
|
||||||
|
implementation(deps.compose.ui)
|
||||||
|
|
||||||
|
/** DI */
|
||||||
|
implementation(deps.hilt.android)
|
||||||
|
kapt(deps.hilt.kapt)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.tangem.features.polymarket.impl
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import com.tangem.core.decompose.context.AppComponentContext
|
||||||
|
import com.tangem.features.polymarket.api.PolymarketComponent
|
||||||
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedFactory
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
|
|
||||||
|
internal class DefaultPolymarketComponent @AssistedInject constructor(
|
||||||
|
@Assisted appComponentContext: AppComponentContext,
|
||||||
|
@Assisted private val params: PolymarketComponent.Params,
|
||||||
|
) : PolymarketComponent, AppComponentContext by appComponentContext {
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun Content(modifier: Modifier) {
|
||||||
|
// TODO([REDACTED_TASK_KEY]): real Polymarket prediction account UI
|
||||||
|
Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
|
Text(text = params.userWalletId.stringValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AssistedFactory
|
||||||
|
interface Factory : PolymarketComponent.Factory {
|
||||||
|
override fun create(
|
||||||
|
context: AppComponentContext,
|
||||||
|
params: PolymarketComponent.Params,
|
||||||
|
): DefaultPolymarketComponent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.tangem.features.polymarket.impl.di
|
||||||
|
|
||||||
|
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
||||||
|
import com.tangem.features.polymarket.api.PolymarketComponent
|
||||||
|
import com.tangem.features.polymarket.api.PolymarketFeatureToggles
|
||||||
|
import com.tangem.features.polymarket.impl.DefaultPolymarketComponent
|
||||||
|
import com.tangem.features.polymarket.impl.featuretoggles.DefaultPolymarketFeatureToggles
|
||||||
|
import dagger.Binds
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
internal interface PolymarketBindsModule {
|
||||||
|
@Binds
|
||||||
|
@Singleton
|
||||||
|
fun providePolymarketComponentFactory(impl: DefaultPolymarketComponent.Factory): PolymarketComponent.Factory
|
||||||
|
}
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
internal object PolymarketFeatureTogglesModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun providePolymarketFeatureToggles(featureTogglesManager: FeatureTogglesManager): PolymarketFeatureToggles {
|
||||||
|
return DefaultPolymarketFeatureToggles(featureTogglesManager = featureTogglesManager)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.tangem.features.polymarket.impl.featuretoggles
|
||||||
|
|
||||||
|
import com.tangem.core.configtoggle.FeatureToggles
|
||||||
|
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
||||||
|
import com.tangem.features.polymarket.api.PolymarketFeatureToggles
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal class DefaultPolymarketFeatureToggles @Inject constructor(
|
||||||
|
private val featureTogglesManager: FeatureTogglesManager,
|
||||||
|
) : PolymarketFeatureToggles {
|
||||||
|
override val isPolymarketEnabled: Boolean
|
||||||
|
get() = featureTogglesManager.isFeatureEnabled(FeatureToggles.AND_16204_POLYMARKET_ENABLED)
|
||||||
|
}
|
||||||
|
|
@ -135,6 +135,7 @@ dependencies {
|
||||||
api(projects.features.feed.api)
|
api(projects.features.feed.api)
|
||||||
api(projects.features.hotWallet.api)
|
api(projects.features.hotWallet.api)
|
||||||
api(projects.features.promoBanners.api)
|
api(projects.features.promoBanners.api)
|
||||||
|
api(projects.features.polymarket.api)
|
||||||
api(projects.features.pushNotifications.api)
|
api(projects.features.pushNotifications.api)
|
||||||
api(projects.features.pushNotificationSettings.api)
|
api(projects.features.pushNotificationSettings.api)
|
||||||
api(projects.features.send.api)
|
api(projects.features.send.api)
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,11 @@ internal class WalletClickIntents @Inject constructor(
|
||||||
router.openAddFunds(userWalletId)
|
router.openAddFunds(userWalletId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onPredictionAccountClick(userWalletId: UserWalletId) {
|
||||||
|
// TODO([REDACTED_TASK_KEY]): add analytics for prediction account entry point
|
||||||
|
router.openPolymarket(userWalletId)
|
||||||
|
}
|
||||||
|
|
||||||
private fun refreshMultiCurrencyContent(showRefreshState: Boolean) {
|
private fun refreshMultiCurrencyContent(showRefreshState: Boolean) {
|
||||||
val userWallet = getSelectedWalletSyncUseCase.unwrap() ?: return
|
val userWallet = getSelectedWalletSyncUseCase.unwrap() ?: return
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,10 @@ internal class DefaultWalletRouter @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun openPolymarket(userWalletId: UserWalletId) {
|
||||||
|
router.push(AppRoute.Polymarket(userWalletId = userWalletId))
|
||||||
|
}
|
||||||
|
|
||||||
override fun isWalletLastScreen(): Boolean {
|
override fun isWalletLastScreen(): Boolean {
|
||||||
return router.stack.lastOrNull() is AppRoute.Wallet
|
return router.stack.lastOrNull() is AppRoute.Wallet
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -125,4 +125,7 @@ internal interface InnerWalletRouter {
|
||||||
|
|
||||||
/** Open Transfer screen */
|
/** Open Transfer screen */
|
||||||
fun openTransfer(userWalletId: UserWalletId)
|
fun openTransfer(userWalletId: UserWalletId)
|
||||||
|
|
||||||
|
/** Open Polymarket (prediction account) entry screen */
|
||||||
|
fun openPolymarket(userWalletId: UserWalletId)
|
||||||
}
|
}
|
||||||
|
|
@ -95,6 +95,15 @@ internal sealed interface TokensListItemUM2 {
|
||||||
override val tokenRowUM: TangemTokenRowUM,
|
override val tokenRowUM: TangemTokenRowUM,
|
||||||
) : TokensListItemUM2
|
) : TokensListItemUM2
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synthetic, feature-flag-gated entry-point row for the Polymarket prediction account.
|
||||||
|
* Rendered as a standalone rounded card above the real account rows. Not backed by a domain
|
||||||
|
* account yet — will become an [AccountStatus]-backed row once the external contract lands.
|
||||||
|
*/
|
||||||
|
data class Prediction(
|
||||||
|
override val tokenRowUM: TangemTokenRowUM,
|
||||||
|
) : TokensListItemUM2
|
||||||
|
|
||||||
data class Portfolio(
|
data class Portfolio(
|
||||||
override val tokenRowUM: TangemTokenRowUM,
|
override val tokenRowUM: TangemTokenRowUM,
|
||||||
val onEmptyClick: () -> Unit,
|
val onEmptyClick: () -> Unit,
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ internal class SetTokenListTransformer(
|
||||||
private val isAccountsModeEnabled: Boolean,
|
private val isAccountsModeEnabled: Boolean,
|
||||||
private val isRedesignEnabled: Boolean,
|
private val isRedesignEnabled: Boolean,
|
||||||
private val isMultipleCardsEnabled: Boolean,
|
private val isMultipleCardsEnabled: Boolean,
|
||||||
|
private val isPolymarketEnabled: Boolean,
|
||||||
) : WalletStateTransformer(userWallet.walletId) {
|
) : WalletStateTransformer(userWallet.walletId) {
|
||||||
|
|
||||||
private val tangemPayConverter by lazy {
|
private val tangemPayConverter by lazy {
|
||||||
|
|
@ -165,6 +166,7 @@ internal class SetTokenListTransformer(
|
||||||
shouldShowMainPromo = shouldShowMainPromo,
|
shouldShowMainPromo = shouldShowMainPromo,
|
||||||
isAccountsModeEnabled = isAccountsModeEnabled,
|
isAccountsModeEnabled = isAccountsModeEnabled,
|
||||||
expandedAccounts = params.expandedAccounts,
|
expandedAccounts = params.expandedAccounts,
|
||||||
|
isPolymarketEnabled = isPolymarketEnabled,
|
||||||
).convert(value = params.accountList)
|
).convert(value = params.accountList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,13 +1,20 @@
|
||||||
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
|
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.SpanStyle
|
||||||
|
import com.tangem.core.ui.components.account.AccountIconSize
|
||||||
|
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||||
import com.tangem.core.ui.ds.button.TangemButtonShape
|
import com.tangem.core.ui.ds.button.TangemButtonShape
|
||||||
import com.tangem.core.ui.ds.button.TangemButtonSize
|
import com.tangem.core.ui.ds.button.TangemButtonSize
|
||||||
import com.tangem.core.ui.ds.button.TangemButtonType
|
import com.tangem.core.ui.ds.button.TangemButtonType
|
||||||
import com.tangem.core.ui.ds.button.TangemButtonUM
|
import com.tangem.core.ui.ds.button.TangemButtonUM
|
||||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||||
import com.tangem.core.ui.ds.row.header.TangemHeaderRowUM
|
import com.tangem.core.ui.ds.row.header.TangemHeaderRowUM
|
||||||
|
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
|
||||||
import com.tangem.core.ui.extensions.resourceReference
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
import com.tangem.core.ui.extensions.wrappedList
|
import com.tangem.core.ui.extensions.wrappedList
|
||||||
|
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||||
|
import com.tangem.core.ui.format.bigdecimal.formatStyled
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.domain.account.models.AccountStatusList
|
import com.tangem.domain.account.models.AccountStatusList
|
||||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||||
|
|
@ -38,6 +45,7 @@ internal class WalletTokensListUMConverter(
|
||||||
private val isAccountsModeEnabled: Boolean,
|
private val isAccountsModeEnabled: Boolean,
|
||||||
private val expandedAccounts: Set<AccountId>,
|
private val expandedAccounts: Set<AccountId>,
|
||||||
private val stakingAvailabilityMap: Map<CryptoCurrency, StakingAvailability>,
|
private val stakingAvailabilityMap: Map<CryptoCurrency, StakingAvailability>,
|
||||||
|
private val isPolymarketEnabled: Boolean,
|
||||||
shouldShowMainPromo: Boolean,
|
shouldShowMainPromo: Boolean,
|
||||||
) : Converter<AccountStatusList, WalletTokensListUM> {
|
) : Converter<AccountStatusList, WalletTokensListUM> {
|
||||||
|
|
||||||
|
|
@ -100,13 +108,52 @@ internal class WalletTokensListUMConverter(
|
||||||
}
|
}
|
||||||
}.toPersistentList()
|
}.toPersistentList()
|
||||||
|
|
||||||
|
val tokenListWithEntryPoints = if (isPolymarketEnabled) {
|
||||||
|
tokenListUM.add(index = 0, element = TokensListItemUM2.Prediction(tokenRowUM = getPredictionRow()))
|
||||||
|
} else {
|
||||||
|
tokenListUM
|
||||||
|
}
|
||||||
|
|
||||||
WalletTokensListUM.Content(
|
WalletTokensListUM.Content(
|
||||||
tokenList = tokenListUM,
|
tokenList = tokenListWithEntryPoints,
|
||||||
organizeButtonUM = getOrganizeButtonUM(value),
|
organizeButtonUM = getOrganizeButtonUM(value),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getPredictionRow(): TangemTokenRowUM {
|
||||||
|
return TangemTokenRowUM.Content(
|
||||||
|
id = PREDICTION_ROW_ID,
|
||||||
|
// TODO([REDACTED_TASK_KEY]): replace the placeholder icon with the final Polymarket account asset
|
||||||
|
headIconUM = TangemIconUM.Currency(
|
||||||
|
currencyIconState = CurrencyIconState.CryptoPortfolio.Icon(
|
||||||
|
resId = R.drawable.ic_analytics_up_24,
|
||||||
|
color = PREDICTION_ICON_COLOR,
|
||||||
|
isGrayscale = false,
|
||||||
|
size = AccountIconSize.ExtraSmall,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
titleUM = TangemTokenRowUM.TitleUM.Content(
|
||||||
|
text = resourceReference(R.string.prediction_account_title),
|
||||||
|
),
|
||||||
|
subtitleUM = TangemTokenRowUM.SubtitleUM.Content(
|
||||||
|
text = resourceReference(R.string.prediction_account_subtitle),
|
||||||
|
),
|
||||||
|
topEndContentUM = TangemTokenRowUM.EndContentUM.Content(
|
||||||
|
text = BigDecimal.ZERO.formatStyled {
|
||||||
|
fiat(
|
||||||
|
fiatCurrencyCode = appCurrency.code,
|
||||||
|
fiatCurrencySymbol = appCurrency.symbol,
|
||||||
|
spanStyleReference = { SpanStyle(color = TangemTheme.colors2.text.neutral.secondary) },
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
bottomEndContentUM = TangemTokenRowUM.EndContentUM.Empty,
|
||||||
|
onItemClick = { clickIntents.onPredictionAccountClick(selectedWallet.walletId) },
|
||||||
|
onItemLongClick = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun getTokenListItems(
|
private fun getTokenListItems(
|
||||||
accountStatus: AccountStatus.CryptoPortfolio,
|
accountStatus: AccountStatus.CryptoPortfolio,
|
||||||
promoCryptoCurrency: CryptoCurrencyStatus?,
|
promoCryptoCurrency: CryptoCurrencyStatus?,
|
||||||
|
|
@ -185,4 +232,10 @@ internal class WalletTokensListUMConverter(
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("MagicNumber")
|
||||||
|
private companion object {
|
||||||
|
const val PREDICTION_ROW_ID = "polymarket_prediction_account"
|
||||||
|
val PREDICTION_ICON_COLOR = Color(0xFF5A5AF0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -12,6 +12,7 @@ import com.tangem.domain.yield.supply.usecase.YieldSupplyGetShouldShowMainPromoU
|
||||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||||
|
import com.tangem.features.polymarket.api.PolymarketFeatureToggles
|
||||||
import com.tangem.features.tangempay.TangemPayFeatureToggles
|
import com.tangem.features.tangempay.TangemPayFeatureToggles
|
||||||
import com.tangem.utils.coroutines.combine7
|
import com.tangem.utils.coroutines.combine7
|
||||||
import com.tangem.utils.logging.TangemLogger
|
import com.tangem.utils.logging.TangemLogger
|
||||||
|
|
@ -39,6 +40,7 @@ internal class AccountListSubscriber @AssistedInject constructor(
|
||||||
private val yieldSupplyGetShouldShowMainPromoUseCase: YieldSupplyGetShouldShowMainPromoUseCase,
|
private val yieldSupplyGetShouldShowMainPromoUseCase: YieldSupplyGetShouldShowMainPromoUseCase,
|
||||||
private val designFeatureToggles: DesignFeatureToggles,
|
private val designFeatureToggles: DesignFeatureToggles,
|
||||||
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
|
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
|
||||||
|
private val polymarketFeatureToggles: PolymarketFeatureToggles,
|
||||||
) : BasicAccountListSubscriber() {
|
) : BasicAccountListSubscriber() {
|
||||||
|
|
||||||
override fun create(coroutineScope: CoroutineScope): Flow<*> {
|
override fun create(coroutineScope: CoroutineScope): Flow<*> {
|
||||||
|
|
@ -93,6 +95,7 @@ internal class AccountListSubscriber @AssistedInject constructor(
|
||||||
stakingAvailabilityMap = stakingAvailabilityMap,
|
stakingAvailabilityMap = stakingAvailabilityMap,
|
||||||
shouldShowMainPromo = shouldShowMainPromo,
|
shouldShowMainPromo = shouldShowMainPromo,
|
||||||
isMultipleCardsEnabled = tangemPayFeatureToggles.isMultipleCardsEnabled,
|
isMultipleCardsEnabled = tangemPayFeatureToggles.isMultipleCardsEnabled,
|
||||||
|
isPolymarketEnabled = polymarketFeatureToggles.isPolymarketEnabled,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
updateState(
|
updateState(
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ internal abstract class BasicAccountListSubscriber : BasicWalletSubscriber() {
|
||||||
expandedAccounts: Set<AccountId>,
|
expandedAccounts: Set<AccountId>,
|
||||||
isAccountMode: Boolean,
|
isAccountMode: Boolean,
|
||||||
isMultipleCardsEnabled: Boolean,
|
isMultipleCardsEnabled: Boolean,
|
||||||
|
isPolymarketEnabled: Boolean = false,
|
||||||
yieldSupplyApyMap: Map<String, BigDecimal> = emptyMap(),
|
yieldSupplyApyMap: Map<String, BigDecimal> = emptyMap(),
|
||||||
stakingAvailabilityMap: Map<CryptoCurrency, StakingAvailability> = emptyMap(),
|
stakingAvailabilityMap: Map<CryptoCurrency, StakingAvailability> = emptyMap(),
|
||||||
shouldShowMainPromo: Boolean = false,
|
shouldShowMainPromo: Boolean = false,
|
||||||
|
|
@ -107,6 +108,7 @@ internal abstract class BasicAccountListSubscriber : BasicWalletSubscriber() {
|
||||||
isAccountsModeEnabled = isAccountMode,
|
isAccountsModeEnabled = isAccountMode,
|
||||||
isRedesignEnabled = true,
|
isRedesignEnabled = true,
|
||||||
isMultipleCardsEnabled = isMultipleCardsEnabled,
|
isMultipleCardsEnabled = isMultipleCardsEnabled,
|
||||||
|
isPolymarketEnabled = isPolymarketEnabled,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -171,6 +173,7 @@ internal abstract class BasicAccountListSubscriber : BasicWalletSubscriber() {
|
||||||
isAccountsModeEnabled = false,
|
isAccountsModeEnabled = false,
|
||||||
isRedesignEnabled = false,
|
isRedesignEnabled = false,
|
||||||
isMultipleCardsEnabled = false,
|
isMultipleCardsEnabled = false,
|
||||||
|
isPolymarketEnabled = false,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,12 @@ internal fun LazyListScope.tokensListItems2(
|
||||||
isBalanceHidden = isBalanceHidden,
|
isBalanceHidden = isBalanceHidden,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
is TokensListItemUM2.Prediction -> predictionItem(
|
||||||
|
listItem = listItem,
|
||||||
|
index = index,
|
||||||
|
isBalanceHidden = isBalanceHidden,
|
||||||
|
modifier = modifier,
|
||||||
|
)
|
||||||
is TokensListItemUM2.Portfolio -> portfolioItem(
|
is TokensListItemUM2.Portfolio -> portfolioItem(
|
||||||
listItem = listItem,
|
listItem = listItem,
|
||||||
index = index,
|
index = index,
|
||||||
|
|
@ -183,6 +189,41 @@ private fun LazyListScope.tokenItem(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun LazyListScope.predictionItem(
|
||||||
|
listItem: TokensListItemUM2.Prediction,
|
||||||
|
index: Int,
|
||||||
|
isBalanceHidden: Boolean,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
item(
|
||||||
|
key = listItem.tokenRowUM.id,
|
||||||
|
contentType = listItem.tokenRowUM::class.java,
|
||||||
|
) {
|
||||||
|
val tokenRowUM = listItem.tokenRowUM
|
||||||
|
val itemModifier = modifier
|
||||||
|
.testTag(MainScreenTestTags.ACCOUNT_LIST_ITEM)
|
||||||
|
.semantics { lazyListItemPosition = index }
|
||||||
|
.padding(top = if (index == 0) TangemTheme.dimens2.x3 else TangemTheme.dimens2.x2)
|
||||||
|
// Standalone fully-rounded card, matching the account rows above/below it.
|
||||||
|
.roundedShapeItemDecoration(
|
||||||
|
radius = TangemTheme.dimens2.x5,
|
||||||
|
currentIndex = 0,
|
||||||
|
addDefaultPadding = false,
|
||||||
|
lastIndex = 0,
|
||||||
|
backgroundColor = TangemTheme.colors2.surface.level3,
|
||||||
|
)
|
||||||
|
.combinedClickable(
|
||||||
|
enabled = tokenRowUM.onItemClick != null,
|
||||||
|
onClick = tokenRowUM.onItemClick ?: {},
|
||||||
|
)
|
||||||
|
TangemTokenRow(
|
||||||
|
tokenRowUM = tokenRowUM,
|
||||||
|
isBalanceHidden = isBalanceHidden,
|
||||||
|
modifier = itemModifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("LongMethod")
|
@Suppress("LongMethod")
|
||||||
private fun LazyListScope.portfolioItem(
|
private fun LazyListScope.portfolioItem(
|
||||||
listItem: TokensListItemUM2.Portfolio,
|
listItem: TokensListItemUM2.Portfolio,
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ class SetTokenListTransformerTest {
|
||||||
isAccountsModeEnabled = false,
|
isAccountsModeEnabled = false,
|
||||||
isRedesignEnabled = true,
|
isRedesignEnabled = true,
|
||||||
isMultipleCardsEnabled = false,
|
isMultipleCardsEnabled = false,
|
||||||
|
isPolymarketEnabled = false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
|
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
|
||||||
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
|
||||||
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
import com.tangem.domain.account.models.AccountStatusList
|
import com.tangem.domain.account.models.AccountStatusList
|
||||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||||
import com.tangem.domain.card.CardTypesResolver
|
import com.tangem.domain.card.CardTypesResolver
|
||||||
|
|
@ -20,12 +22,17 @@ import com.tangem.domain.models.scan.ScanResponse
|
||||||
import com.tangem.domain.models.tokenlist.TokenList
|
import com.tangem.domain.models.tokenlist.TokenList
|
||||||
import com.tangem.domain.models.wallet.UserWallet
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||||
|
import com.tangem.feature.wallet.impl.R
|
||||||
|
import com.tangem.feature.wallet.presentation.wallet.state.model.TokensListItemUM2
|
||||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListUM
|
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListUM
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.mockkStatic
|
import io.mockk.mockkStatic
|
||||||
import io.mockk.unmockkStatic
|
import io.mockk.unmockkStatic
|
||||||
|
import io.mockk.verify
|
||||||
import org.junit.jupiter.api.AfterEach
|
import org.junit.jupiter.api.AfterEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import org.junit.jupiter.params.ParameterizedTest
|
import org.junit.jupiter.params.ParameterizedTest
|
||||||
import org.junit.jupiter.params.provider.MethodSource
|
import org.junit.jupiter.params.provider.MethodSource
|
||||||
|
|
@ -59,15 +66,65 @@ internal class WalletTokensListUMConverterTest {
|
||||||
assertThat(result.organizeButtonUM != null).isEqualTo(model.expectedButtonShown)
|
assertThat(result.organizeButtonUM != null).isEqualTo(model.expectedButtonShown)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createConverter(selectedWallet: UserWallet): WalletTokensListUMConverter = WalletTokensListUMConverter(
|
@Test
|
||||||
|
fun `GIVEN polymarket enabled WHEN convert THEN prediction row is first with expected content and click`() {
|
||||||
|
// Arrange
|
||||||
|
val clickIntents = mockk<WalletClickIntents>(relaxed = true)
|
||||||
|
val selectedWallet = mockColdWallet(isSingleCurrency = false, isSingleWalletWithToken = false)
|
||||||
|
every { selectedWallet.walletId } returns userWalletId
|
||||||
|
val converter = createConverter(
|
||||||
|
selectedWallet = selectedWallet,
|
||||||
|
isPolymarketEnabled = true,
|
||||||
|
clickIntents = clickIntents,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val result = converter.convert(value = nonEmptyAccountList()) as WalletTokensListUM.Content
|
||||||
|
val firstItem = result.tokenList.first()
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(firstItem).isInstanceOf(TokensListItemUM2.Prediction::class.java)
|
||||||
|
val row = (firstItem as TokensListItemUM2.Prediction).tokenRowUM as TangemTokenRowUM.Content
|
||||||
|
assertThat(row.id).isEqualTo("polymarket_prediction_account")
|
||||||
|
assertThat(row.titleUM).isEqualTo(
|
||||||
|
TangemTokenRowUM.TitleUM.Content(text = resourceReference(R.string.prediction_account_title)),
|
||||||
|
)
|
||||||
|
assertThat(row.subtitleUM).isEqualTo(
|
||||||
|
TangemTokenRowUM.SubtitleUM.Content(text = resourceReference(R.string.prediction_account_subtitle)),
|
||||||
|
)
|
||||||
|
row.onItemClick?.invoke()
|
||||||
|
verify(exactly = 1) { clickIntents.onPredictionAccountClick(userWalletId) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN polymarket disabled WHEN convert THEN no prediction row present`() {
|
||||||
|
// Arrange
|
||||||
|
val converter = createConverter(
|
||||||
|
selectedWallet = mockColdWallet(isSingleCurrency = false, isSingleWalletWithToken = false),
|
||||||
|
isPolymarketEnabled = false,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
val result = converter.convert(value = nonEmptyAccountList()) as WalletTokensListUM.Content
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(result.tokenList.filterIsInstance<TokensListItemUM2.Prediction>()).isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createConverter(
|
||||||
|
selectedWallet: UserWallet,
|
||||||
|
isPolymarketEnabled: Boolean = false,
|
||||||
|
clickIntents: WalletClickIntents = mockk(relaxed = true),
|
||||||
|
): WalletTokensListUMConverter = WalletTokensListUMConverter(
|
||||||
appCurrency = AppCurrency.Default,
|
appCurrency = AppCurrency.Default,
|
||||||
selectedWallet = selectedWallet,
|
selectedWallet = selectedWallet,
|
||||||
clickIntents = mockk(relaxed = true),
|
clickIntents = clickIntents,
|
||||||
yieldModuleApyMap = emptyMap(),
|
yieldModuleApyMap = emptyMap(),
|
||||||
isAccountsModeEnabled = false,
|
isAccountsModeEnabled = false,
|
||||||
expandedAccounts = emptySet(),
|
expandedAccounts = emptySet(),
|
||||||
stakingAvailabilityMap = emptyMap(),
|
stakingAvailabilityMap = emptyMap(),
|
||||||
shouldShowMainPromo = false,
|
shouldShowMainPromo = false,
|
||||||
|
isPolymarketEnabled = isPolymarketEnabled,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun mockColdWallet(isSingleCurrency: Boolean, isSingleWalletWithToken: Boolean): UserWallet.Cold {
|
private fun mockColdWallet(isSingleCurrency: Boolean, isSingleWalletWithToken: Boolean): UserWallet.Cold {
|
||||||
|
|
|
||||||
|
|
@ -367,6 +367,9 @@ include(":features:token-recieve:impl")
|
||||||
include(":features:yield-supply:api")
|
include(":features:yield-supply:api")
|
||||||
include(":features:yield-supply:impl")
|
include(":features:yield-supply:impl")
|
||||||
|
|
||||||
|
include(":features:polymarket:api")
|
||||||
|
include(":features:polymarket:impl")
|
||||||
|
|
||||||
include(":features:approval:api")
|
include(":features:approval:api")
|
||||||
include(":features:approval:impl")
|
include(":features:approval:impl")
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue