diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml
index a0dde9c0c3..bd9bd6be0d 100644
--- a/core/res/src/main/res/values-ja/strings.xml
+++ b/core/res/src/main/res/values-ja/strings.xml
@@ -261,6 +261,7 @@
残り%1$s
レガシービットコイン
ロックされています
+ ロックされたウォレット
メインネットワーク
月
ネットワーク手数料
@@ -310,6 +311,7 @@
署名
署名して送信
スキップ
+ 問題が発生しました
ステーキング
ステーキング
始める
@@ -1146,7 +1148,7 @@
獲得した報酬をステーキングに再投資し、潜在的な収益を増やします。
再ステーキングを使うと、ステーキングを解除することなく、あるバリデータから別のバリデータに資金を移動できます。
残高のすべてをステーキングしようとしています。ステーキング解除や報酬請求にかかるネットワーク手数料をカバーするために、少額を残しておくことをお勧めします。
- TONでステーキングを開始するには、まず任意の金額の送金を行ってください。これにより、ウォレットがアクティブになります。
+ TONのステーキングを開始するには、まず自分のアドレスに少額の取引を送信します。これによりウォレットが有効になります。
取引を完了するには、ネットワーク手数料に加えて最大0.2TONが必要になる場合があります。未使用分は返金されます。
この操作を続行するには、ネットワーク手数料に加えて0.2 TONが必要です。残高を補充してください。
TONの準備金が必要です
diff --git a/domain/account/status/src/main/java/com/tangem/domain/account/status/di/AccountStatusListSupplierModule.kt b/domain/account/status/src/main/java/com/tangem/domain/account/status/di/AccountStatusListSupplierModule.kt
index 51b10dc806..f71bbe7191 100644
--- a/domain/account/status/src/main/java/com/tangem/domain/account/status/di/AccountStatusListSupplierModule.kt
+++ b/domain/account/status/src/main/java/com/tangem/domain/account/status/di/AccountStatusListSupplierModule.kt
@@ -4,6 +4,10 @@ import com.tangem.domain.account.status.producer.MultiAccountStatusListProducer
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
import com.tangem.domain.account.status.supplier.MultiAccountStatusListSupplier
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
+import com.tangem.domain.account.status.usecase.GetAccountCurrencyByAddressUseCase
+import com.tangem.domain.account.supplier.SingleAccountListSupplier
+import com.tangem.domain.common.wallets.UserWalletsListRepository
+import com.tangem.domain.networks.multi.MultiNetworkStatusSupplier
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@@ -35,4 +39,18 @@ internal object AccountStatusListSupplierModule {
keyCreator = { "multi_account_status_list" },
) {}
}
+
+ @Provides
+ @Singleton
+ fun provideGetAccountCurrencyByAddressUseCase(
+ userWalletsListRepository: UserWalletsListRepository,
+ multiNetworkStatusSupplier: MultiNetworkStatusSupplier,
+ singleAccountListSupplier: SingleAccountListSupplier,
+ ): GetAccountCurrencyByAddressUseCase {
+ return GetAccountCurrencyByAddressUseCase(
+ userWalletsListRepository = userWalletsListRepository,
+ multiNetworkStatusSupplier = multiNetworkStatusSupplier,
+ singleAccountListSupplier = singleAccountListSupplier,
+ )
+ }
}
\ No newline at end of file
diff --git a/features/account/api/src/main/java/com/tangem/features/account/PortfolioSelectorComponent.kt b/features/account/api/src/main/java/com/tangem/features/account/PortfolioSelectorComponent.kt
index 825d41cf48..7216cd8ad7 100644
--- a/features/account/api/src/main/java/com/tangem/features/account/PortfolioSelectorComponent.kt
+++ b/features/account/api/src/main/java/com/tangem/features/account/PortfolioSelectorComponent.kt
@@ -1,11 +1,11 @@
package com.tangem.features.account
-import com.tangem.core.decompose.context.AppComponentContext
+import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.extensions.TextReference
-import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountId
+import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.wallet.UserWallet
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -20,9 +20,7 @@ interface PortfolioSelectorComponent : ComposableBottomSheetComponent, Composabl
val controller: PortfolioSelectorController,
)
- interface Factory {
- fun create(appComponentContext: AppComponentContext, params: Params): PortfolioSelectorComponent
- }
+ interface Factory : ComponentFactory
}
/**
@@ -30,8 +28,8 @@ interface PortfolioSelectorComponent : ComposableBottomSheetComponent, Composabl
*/
interface PortfolioSelectorController {
val isAccountMode: Flow
- val selectedAccount: Flow
+ val selectedAccount: StateFlow
fun selectAccount(accountId: AccountId?)
- fun selectedAccountWithData(portfolioFetcher: PortfolioFetcher): Flow?>
+ fun selectedAccountWithData(portfolioFetcher: PortfolioFetcher): Flow?>
}
\ No newline at end of file
diff --git a/features/account/impl/src/main/java/com/tangem/features/account/di/AccountFeatureModule.kt b/features/account/impl/src/main/java/com/tangem/features/account/di/AccountFeatureModule.kt
new file mode 100644
index 0000000000..67ce3bbd1a
--- /dev/null
+++ b/features/account/impl/src/main/java/com/tangem/features/account/di/AccountFeatureModule.kt
@@ -0,0 +1,28 @@
+package com.tangem.features.account.di
+
+import com.tangem.features.account.PortfolioFetcher
+import com.tangem.features.account.PortfolioSelectorComponent
+import com.tangem.features.account.PortfolioSelectorController
+import com.tangem.features.account.fetcher.DefaultPortfolioFetcher
+import com.tangem.features.account.selector.DefaultPortfolioSelectorComponent
+import com.tangem.features.account.selector.DefaultPortfolioSelectorController
+import dagger.Binds
+import dagger.Module
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+
+@Module
+@InstallIn(SingletonComponent::class)
+internal interface AccountFeatureModule {
+
+ @Binds
+ fun bindPortfolioFetcherFactory(impl: DefaultPortfolioFetcher.Factory): PortfolioFetcher.Factory
+
+ @Binds
+ fun bindPortfolioSelectorController(impl: DefaultPortfolioSelectorController): PortfolioSelectorController
+
+ @Binds
+ fun bindPortfolioSelectorComponentFactory(
+ impl: DefaultPortfolioSelectorComponent.Factory,
+ ): PortfolioSelectorComponent.Factory
+}
\ No newline at end of file
diff --git a/features/account/impl/src/main/java/com/tangem/features/account/selector/DefaultPortfolioSelectorController.kt b/features/account/impl/src/main/java/com/tangem/features/account/selector/DefaultPortfolioSelectorController.kt
index 1c55c39755..f607006e40 100644
--- a/features/account/impl/src/main/java/com/tangem/features/account/selector/DefaultPortfolioSelectorController.kt
+++ b/features/account/impl/src/main/java/com/tangem/features/account/selector/DefaultPortfolioSelectorController.kt
@@ -1,8 +1,8 @@
package com.tangem.features.account.selector
import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
-import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountId
+import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.features.account.PortfolioSelectorController
import com.tangem.features.account.PortfolioFetcher
@@ -23,18 +23,18 @@ internal class DefaultPortfolioSelectorController @Inject constructor(
_selectedAccount.update { accountId }
}
- override fun selectedAccountWithData(portfolioFetcher: PortfolioFetcher): Flow?> =
+ override fun selectedAccountWithData(portfolioFetcher: PortfolioFetcher): Flow?> =
combine(
flow = _selectedAccount,
flow2 = portfolioFetcher.data,
transform = { accountId, data ->
accountId ?: return@combine null
- var result: Pair? = null
+ var result: Pair? = null
data.balances.forEach { wallet, balance ->
val accountStatuses = balance.accountsBalance.accountStatuses
.find { accountId == it.account.accountId }
- if (accountStatuses != null) result = wallet to accountStatuses.account
+ if (accountStatuses != null) result = wallet to accountStatuses
}
return@combine result
diff --git a/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralInteractor.kt b/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralInteractor.kt
index 2b752a2c1d..a673964cf8 100644
--- a/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralInteractor.kt
+++ b/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralInteractor.kt
@@ -1,11 +1,16 @@
package com.tangem.feature.referral.domain
+import com.tangem.domain.models.PortfolioId
+import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.feature.referral.domain.models.ReferralData
+import com.tangem.feature.referral.domain.models.TokenData
interface ReferralInteractor {
suspend fun getReferralStatus(userWalletId: UserWalletId): ReferralData
- suspend fun startReferral(userWalletId: UserWalletId): ReferralData
+ suspend fun startReferral(portfolioId: PortfolioId): ReferralData
+
+ suspend fun getCryptoCurrency(userWalletId: UserWalletId, tokenData: TokenData): CryptoCurrency?
}
\ No newline at end of file
diff --git a/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralInteractorImpl.kt b/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralInteractorImpl.kt
index 9520d1847e..0e24966a47 100644
--- a/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralInteractorImpl.kt
+++ b/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralInteractorImpl.kt
@@ -2,9 +2,11 @@ package com.tangem.feature.referral.domain
import arrow.core.getOrElse
import com.tangem.common.core.TangemSdkError
-import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase
-import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase
+import com.tangem.domain.models.PortfolioId
+import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
+import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase
+import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.feature.referral.domain.errors.ReferralError
import com.tangem.feature.referral.domain.models.ReferralData
@@ -31,10 +33,11 @@ internal class ReferralInteractorImpl(
return referralData
}
- override suspend fun startReferral(userWalletId: UserWalletId): ReferralData {
+ override suspend fun startReferral(portfolioId: PortfolioId): ReferralData {
if (tokensForReferral.isEmpty()) error("Tokens for ref is empty")
val tokenData = tokensForReferral.first()
+ val userWalletId = portfolioId.userWalletId
val userWallet = getUserWalletUseCase(userWalletId).getOrElse {
error("Failed to get user wallet $userWalletId: $it")
}
@@ -42,20 +45,27 @@ internal class ReferralInteractorImpl(
val cryptoCurrency = repository.getCryptoCurrency(userWalletId = userWallet.walletId, tokenData = tokenData)
?: error("Failed to create crypto currency")
+ // todo account different for account?
derivePublicKeysUseCase(userWallet.walletId, listOfNotNull(cryptoCurrency)).getOrElse {
Timber.e("Failed to derive public keys: $it")
throw it.mapToDomainError()
}
- addCryptoCurrenciesUseCase(
- userWalletId = userWallet.walletId,
- currency = cryptoCurrency,
- )
+ when (portfolioId) {
+ is PortfolioId.Account -> TODO("account")
+ is PortfolioId.Wallet -> addCryptoCurrenciesUseCase(
+ userWalletId = userWallet.walletId,
+ currency = cryptoCurrency,
+ )
+ }
- val publicAddress = userWalletManager.getWalletAddress(
- networkId = tokenData.networkId,
- derivationPath = cryptoCurrency.network.derivationPath.value,
- )
+ val publicAddress = when (portfolioId) {
+ is PortfolioId.Account -> TODO("account")
+ is PortfolioId.Wallet -> userWalletManager.getWalletAddress(
+ networkId = tokenData.networkId,
+ derivationPath = cryptoCurrency.network.derivationPath.value,
+ )
+ }
return repository.startReferral(
walletId = userWalletManager.getWalletId(),
@@ -65,6 +75,9 @@ internal class ReferralInteractorImpl(
)
}
+ override suspend fun getCryptoCurrency(userWalletId: UserWalletId, tokenData: TokenData): CryptoCurrency? =
+ repository.getCryptoCurrency(userWalletId = userWalletId, tokenData = tokenData)
+
private fun saveReferralTokens(tokens: List) {
tokensForReferral.clear()
tokensForReferral.addAll(tokens)
diff --git a/features/referral/impl/build.gradle.kts b/features/referral/impl/build.gradle.kts
index 13e30dd409..1ced33e61d 100644
--- a/features/referral/impl/build.gradle.kts
+++ b/features/referral/impl/build.gradle.kts
@@ -13,6 +13,7 @@ android {
dependencies {
/** Api */
api(projects.features.referral.api)
+ api(projects.features.account.api)
/** Core modules */
implementation(projects.core.analytics)
@@ -44,10 +45,18 @@ dependencies {
implementation(projects.domain.card)
implementation(projects.domain.wallets.models)
implementation(projects.domain.notifications.models)
+ implementation(projects.domain.account.status)
+ implementation(projects.domain.account)
+ implementation(projects.domain.balanceHiding)
+ implementation(projects.domain.balanceHiding.models)
+ implementation(projects.domain.appCurrency)
+ implementation(projects.domain.appCurrency.models)
implementation(projects.features.referral.domain)
/** Other libraries */
implementation(deps.compose.shimmer)
+ implementation(deps.kotlin.immutable.collections)
+ implementation(deps.decompose.ext.compose)
implementation(deps.compose.accompanist.systemUiController)
implementation(deps.timber)
diff --git a/features/referral/impl/src/main/java/com/tangem/feature/referral/DefaultReferralComponent.kt b/features/referral/impl/src/main/java/com/tangem/feature/referral/DefaultReferralComponent.kt
index f30cefaab8..bb045bb975 100644
--- a/features/referral/impl/src/main/java/com/tangem/feature/referral/DefaultReferralComponent.kt
+++ b/features/referral/impl/src/main/java/com/tangem/feature/referral/DefaultReferralComponent.kt
@@ -1,28 +1,55 @@
package com.tangem.feature.referral
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
+import com.arkivanov.decompose.ComponentContext
+import com.arkivanov.decompose.extensions.compose.subscribeAsState
+import com.arkivanov.decompose.router.slot.childSlot
+import com.arkivanov.decompose.router.slot.dismiss
import com.tangem.core.decompose.context.AppComponentContext
+import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.model.getOrCreateModel
+import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.feature.referral.api.ReferralComponent
import com.tangem.feature.referral.model.ReferralModel
import com.tangem.feature.referral.ui.ReferralScreen
+import com.tangem.features.account.PortfolioSelectorComponent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
class DefaultReferralComponent @AssistedInject constructor(
+ private val portfolioSelectorComponentFactory: PortfolioSelectorComponent.Factory,
@Assisted appComponentContext: AppComponentContext,
@Assisted params: ReferralComponent.Params,
) : ReferralComponent, AppComponentContext by appComponentContext {
private val model: ReferralModel = getOrCreateModel(params)
+ private val bottomSheetSlot = childSlot(
+ source = model.bottomSheetNavigation,
+ serializer = null,
+ handleBackButton = false,
+ childFactory = { configuration, context -> bottomSheetChild(context) },
+ )
@Composable
override fun Content(modifier: Modifier) {
+ val bottomSheet by bottomSheetSlot.subscribeAsState()
ReferralScreen(stateHolder = model.uiState)
+ bottomSheet.child?.instance?.BottomSheet()
}
+ private fun bottomSheetChild(componentContext: ComponentContext): ComposableBottomSheetComponent =
+ portfolioSelectorComponentFactory.create(
+ context = childByContext(componentContext),
+ params = PortfolioSelectorComponent.Params(
+ portfolioFetcher = model.portfolioFetcher,
+ controller = model.portfolioSelectorController,
+ onDismiss = model.bottomSheetNavigation::dismiss,
+ ),
+ )
+
@AssistedFactory
interface Factory : ReferralComponent.Factory {
override fun create(context: AppComponentContext, params: ReferralComponent.Params): DefaultReferralComponent
diff --git a/features/referral/impl/src/main/java/com/tangem/feature/referral/model/AccountAwardConverter.kt b/features/referral/impl/src/main/java/com/tangem/feature/referral/model/AccountAwardConverter.kt
new file mode 100644
index 0000000000..1bcaa44999
--- /dev/null
+++ b/features/referral/impl/src/main/java/com/tangem/feature/referral/model/AccountAwardConverter.kt
@@ -0,0 +1,66 @@
+package com.tangem.feature.referral.model
+
+import com.tangem.common.ui.account.toUM
+import com.tangem.common.ui.tokens.TokenItemStateConverter.Companion.getFormattedCryptoAmount
+import com.tangem.common.ui.tokens.TokenItemStateConverter.Companion.getFormattedFiatAmount
+import com.tangem.common.ui.tokens.TokenItemStateConverter.Companion.isFlickering
+import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
+import com.tangem.core.ui.components.token.state.TokenItemState
+import com.tangem.core.ui.extensions.stringReference
+import com.tangem.domain.appcurrency.model.AppCurrency
+import com.tangem.domain.models.account.AccountStatus
+import com.tangem.domain.models.currency.CryptoCurrency
+import com.tangem.domain.models.currency.CryptoCurrencyStatus
+import com.tangem.feature.referral.models.ReferralStateHolder.AccountAward
+import com.tangem.utils.converter.Converter
+
+internal class AccountAwardConverter(
+ private val isBalanceHidden: Boolean,
+ private val appCurrency: AppCurrency,
+ private val awardCryptoCurrency: CryptoCurrency,
+ private val accountAwardToken: CryptoCurrencyStatus?,
+ private val cryptoPortfolio: AccountStatus.CryptoPortfolio,
+ private val onAccountClick: () -> Unit,
+) : Converter {
+
+ override fun convert(value: Unit): AccountAward {
+ val tokenState = if (accountAwardToken != null) {
+ val currency = accountAwardToken.currency
+ TokenItemState.Content(
+ id = currency.id.value,
+ iconState = CryptoCurrencyToIconStateConverter().convert(currency),
+ titleState = TokenItemState.TitleState.Content(stringReference(currency.name)),
+ fiatAmountState = TokenItemState.FiatAmountState.Content(
+ text = accountAwardToken.getFormattedFiatAmount(appCurrency = appCurrency),
+ ),
+ subtitle2State = TokenItemState.Subtitle2State.TextContent(
+ text = accountAwardToken.getFormattedCryptoAmount(),
+ isFlickering = accountAwardToken.value.isFlickering(),
+ ),
+ subtitleState = TokenItemState.SubtitleState.TextContent(stringReference(currency.symbol)),
+ onItemClick = null,
+ onItemLongClick = null,
+ )
+ } else {
+ val currency = awardCryptoCurrency
+ TokenItemState.Content(
+ id = currency.id.value,
+ iconState = CryptoCurrencyToIconStateConverter().convert(currency),
+ titleState = TokenItemState.TitleState.Content(stringReference(currency.name)),
+ fiatAmountState = TokenItemState.FiatAmountState.Content(text = ""),
+ subtitle2State = TokenItemState.Subtitle2State.TextContent(text = ""),
+ subtitleState = TokenItemState.SubtitleState.TextContent(stringReference(currency.symbol)),
+ onItemClick = null,
+ onItemLongClick = null,
+ )
+ }
+
+ return AccountAward(
+ accountName = cryptoPortfolio.account.accountName.toUM().value,
+ accountIcon = cryptoPortfolio.account.icon.toUM(),
+ onAccountClick = onAccountClick,
+ isBalanceHidden = isBalanceHidden,
+ tokenState = tokenState,
+ )
+ }
+}
\ No newline at end of file
diff --git a/features/referral/impl/src/main/java/com/tangem/feature/referral/model/ReferralModel.kt b/features/referral/impl/src/main/java/com/tangem/feature/referral/model/ReferralModel.kt
index 91384054bc..fbf7aa2332 100644
--- a/features/referral/impl/src/main/java/com/tangem/feature/referral/model/ReferralModel.kt
+++ b/features/referral/impl/src/main/java/com/tangem/feature/referral/model/ReferralModel.kt
@@ -4,6 +4,9 @@ import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
+import arrow.core.getOrElse
+import com.arkivanov.decompose.router.slot.SlotNavigation
+import com.arkivanov.decompose.router.slot.activate
import com.tangem.common.routing.AppRouter
import com.tangem.common.ui.userwallet.ext.walletInterationIcon
import com.tangem.core.analytics.api.AnalyticsEventHandler
@@ -12,8 +15,20 @@ import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.navigation.url.UrlOpener
+import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
+import com.tangem.domain.account.models.AccountStatusList
+import com.tangem.domain.account.status.model.AccountCryptoCurrency
+import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
+import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
+import com.tangem.domain.account.status.usecase.GetAccountCurrencyByAddressUseCase
+import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
+import com.tangem.domain.appcurrency.model.AppCurrency
+import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.demo.IsDemoCardUseCase
+import com.tangem.domain.models.PortfolioId
+import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.wallet.UserWallet
+import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.feature.referral.analytics.ReferralEvents
import com.tangem.feature.referral.api.ReferralComponent
@@ -24,9 +39,11 @@ import com.tangem.feature.referral.domain.models.ReferralData
import com.tangem.feature.referral.domain.models.ReferralInfo
import com.tangem.feature.referral.models.DemoModeException
import com.tangem.feature.referral.models.ReferralStateHolder
-import com.tangem.feature.referral.models.ReferralStateHolder.ErrorSnackbar
-import com.tangem.feature.referral.models.ReferralStateHolder.ReferralInfoState
+import com.tangem.feature.referral.models.ReferralStateHolder.*
+import com.tangem.features.account.PortfolioFetcher
+import com.tangem.features.account.PortfolioSelectorController
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
+import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject
@@ -42,12 +59,27 @@ internal class ReferralModel @Inject constructor(
private val urlOpener: UrlOpener,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val isDemoCardUseCase: IsDemoCardUseCase,
+ private val accountsFeatureToggles: AccountsFeatureToggles,
+ private val portfolioFetcherFactory: PortfolioFetcher.Factory,
+ val portfolioSelectorController: PortfolioSelectorController,
+ private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
+ private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
+ private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
+ private val getAccountCurrencyByAddressUseCase: GetAccountCurrencyByAddressUseCase,
private val appRouter: AppRouter,
) : Model() {
private val params = paramsContainer.require()
+ val bottomSheetNavigation: SlotNavigation = SlotNavigation()
+ val portfolioFetcher: PortfolioFetcher by lazy {
+ portfolioFetcherFactory.create(
+ mode = PortfolioFetcher.Mode.Wallet(params.userWalletId),
+ scope = modelScope,
+ )
+ }
- private var lastReferralData: ReferralData? = null
+ private var referralData: MutableStateFlow = MutableStateFlow(null)
+ private val lastReferralData get() = referralData.value
var uiState: ReferralStateHolder by mutableStateOf(createInitiallyUiState())
private set
@@ -55,6 +87,47 @@ internal class ReferralModel @Inject constructor(
init {
analyticsEventHandler.send(ReferralEvents.ReferralScreenOpened)
loadReferralData()
+ combine(
+ flow = referralData.filterNotNull().onEach(::selectAccount),
+ flow2 = portfolioSelectorController.isAccountMode,
+ transform = { referralData, isAccountMode -> referralData to isAccountMode },
+ ).transformLatest { (referralData, isAccountMode) ->
+ when (isAccountMode) {
+ false -> showContent(referralData)
+ true -> combineAccountUI(referralData)
+ .map { referralData to it }
+ .collect(::emit)
+ }
+ }
+ .onEach { (referralData, accountAward) -> showContent(referralData, accountAward) }
+ .launchIn(modelScope)
+ }
+
+ private fun combineAccountUI(referralData: ReferralData): Flow = combine(
+ flow = portfolioSelectorController.selectedAccountWithData(portfolioFetcher),
+ flow2 = getBalanceHidingSettingsFlow(),
+ flow3 = appCurrencyFlow(),
+ ) { a, b, c -> Triple(a, b, c) }.mapLatest { triple ->
+ val (_, selectedAccount) = triple.first ?: return@mapLatest null
+ val isBalanceHidden = triple.second
+ val appCurrency: AppCurrency = triple.third
+ val awardCryptoCurrency = referralInteractor.getCryptoCurrency(
+ userWalletId = params.userWalletId,
+ tokenData = referralData.getToken(),
+ ) ?: return@mapLatest null
+ val cryptoPortfolio = when (selectedAccount) {
+ is AccountStatus.CryptoPortfolio -> selectedAccount
+ }
+ val accountAwardToken = cryptoPortfolio.tokenList.flattenCurrencies()
+ .find { it.currency.id == awardCryptoCurrency.id }
+ return@mapLatest AccountAwardConverter(
+ isBalanceHidden = isBalanceHidden,
+ awardCryptoCurrency = awardCryptoCurrency,
+ accountAwardToken = accountAwardToken,
+ cryptoPortfolio = cryptoPortfolio,
+ appCurrency = appCurrency,
+ onAccountClick = { bottomSheetNavigation.activate(Unit) },
+ ).convert(Unit)
}
private fun createInitiallyUiState() = ReferralStateHolder(
@@ -75,10 +148,9 @@ internal class ReferralModel @Inject constructor(
modelScope.launch {
runCatching {
referralInteractor.getReferralStatus(params.userWalletId).apply {
- lastReferralData = this
+ referralData.value = this
}
}
- .onSuccess(::showContent)
.onFailure(::showErrorSnackbar)
}
}
@@ -92,16 +164,19 @@ internal class ReferralModel @Inject constructor(
analyticsEventHandler.send(ReferralEvents.ClickParticipate)
uiState = uiState.copy(referralInfoState = ReferralInfoState.Loading)
modelScope.launch {
- runCatching { referralInteractor.startReferral(params.userWalletId) }
+ val lastInfoState = uiState.referralInfoState
+ val portfolioId = when (accountsFeatureToggles.isFeatureEnabled) {
+ true -> PortfolioId(requireNotNull(portfolioSelectorController.selectedAccount.value))
+ false -> PortfolioId(params.userWalletId)
+ }
+ runCatching { referralInteractor.startReferral(portfolioId) }
.onSuccess {
analyticsEventHandler.send(ReferralEvents.ParticipateSuccessful)
- showContent(it)
+ referralData.value = it
}
.onFailure { throwable ->
if (throwable is ReferralError.UserCancelledException) {
- lastReferralData?.let { referralData ->
- showContent(referralData)
- }
+ uiState = uiState.copy(referralInfoState = lastInfoState)
} else {
showErrorSnackbar(throwable)
}
@@ -110,8 +185,8 @@ internal class ReferralModel @Inject constructor(
}
}
- private fun showContent(referralData: ReferralData) {
- uiState = uiState.copy(referralInfoState = referralData.convertToReferralInfoState())
+ private fun showContent(referralData: ReferralData, accountAward: AccountAward? = null) {
+ uiState = uiState.copy(referralInfoState = referralData.convertToReferralInfoState(accountAward))
}
private fun showErrorSnackbar(throwable: Throwable) {
@@ -136,7 +211,7 @@ internal class ReferralModel @Inject constructor(
shareManager.shareText(text = text)
}
- private fun ReferralData.convertToReferralInfoState(): ReferralInfoState = when (this) {
+ private fun ReferralData.convertToReferralInfoState(accountAward: AccountAward?): ReferralInfoState = when (this) {
is ReferralData.ParticipantData -> ReferralInfoState.ParticipantContent(
award = getAwardValue(),
networkName = getNetworkName(),
@@ -147,6 +222,7 @@ internal class ReferralModel @Inject constructor(
shareLink = referral.shareLink,
url = tosLink,
expectedAwards = expectedAwards,
+ accountAward = accountAward,
)
is ReferralData.NonParticipantData -> {
val userWallet = getUserWalletUseCase(params.userWalletId).getOrNull() ?: error("User wallet not found")
@@ -158,6 +234,7 @@ internal class ReferralModel @Inject constructor(
url = tosLink,
onParticipateClicked = ::participate,
participateButtonIcon = walletInterationIcon(userWallet),
+ accountAward = accountAward,
)
}
}
@@ -166,6 +243,39 @@ internal class ReferralModel @Inject constructor(
private fun ReferralData.getNetworkName(): String = getToken().networkId.replaceFirstChar(Char::uppercase)
+ private fun walletAccounts(walletId: UserWalletId): Flow = singleAccountStatusListSupplier(
+ SingleAccountStatusListProducer.Params(walletId),
+ )
+
+ private suspend fun findAccountByAddress(address: String): AccountCryptoCurrency? {
+ return getAccountCurrencyByAddressUseCase(address).getOrNull()
+ }
+
+ private suspend fun selectAccount(referralData: ReferralData) {
+ when (referralData) {
+ is ReferralData.NonParticipantData -> if (portfolioSelectorController.selectedAccount.value == null) {
+ portfolioSelectorController.selectAccount(
+ accountId = walletAccounts(params.userWalletId).first().mainAccount.account.accountId,
+ )
+ }
+ is ReferralData.ParticipantData -> portfolioSelectorController.selectAccount(
+ accountId = findAccountByAddress(referralData.referral.address)?.account?.accountId,
+ )
+ }
+ }
+
+ private fun getBalanceHidingSettingsFlow(): Flow {
+ return getBalanceHidingSettingsUseCase()
+ .map { it.isBalanceHidden }
+ .distinctUntilChanged()
+ }
+
+ private fun appCurrencyFlow(): Flow {
+ return getSelectedAppCurrencyUseCase()
+ .map { it.getOrElse { AppCurrency.Default } }
+ .distinctUntilChanged()
+ }
+
@Suppress("MagicNumber")
private fun ReferralInfo.getAddressValue(): String {
check(address.length > 5) { "Invalid address" }
diff --git a/features/referral/impl/src/main/java/com/tangem/feature/referral/models/ReferralStateHolder.kt b/features/referral/impl/src/main/java/com/tangem/feature/referral/models/ReferralStateHolder.kt
index 3631e0c996..616e7715ab 100644
--- a/features/referral/impl/src/main/java/com/tangem/feature/referral/models/ReferralStateHolder.kt
+++ b/features/referral/impl/src/main/java/com/tangem/feature/referral/models/ReferralStateHolder.kt
@@ -1,6 +1,9 @@
package com.tangem.feature.referral.models
import androidx.annotation.DrawableRes
+import com.tangem.common.ui.account.CryptoPortfolioIconUM
+import com.tangem.core.ui.components.token.state.TokenItemState
+import com.tangem.core.ui.extensions.TextReference
import com.tangem.feature.referral.domain.models.ExpectedAwards
internal data class ReferralStateHolder(
@@ -17,6 +20,7 @@ internal data class ReferralStateHolder(
val networkName: String
val discount: String
val url: String
+ val accountAward: AccountAward?
}
sealed interface ReferralInfoState {
@@ -29,6 +33,7 @@ internal data class ReferralStateHolder(
val code: String,
val shareLink: String,
override val url: String,
+ override val accountAward: AccountAward? = null,
val expectedAwards: ExpectedAwards?,
) : ReferralInfoState, ReferralInfoContentState
@@ -37,6 +42,7 @@ internal data class ReferralStateHolder(
override val networkName: String,
override val discount: String,
override val url: String,
+ override val accountAward: AccountAward? = null,
@DrawableRes val participateButtonIcon: Int?,
val onParticipateClicked: () -> Unit,
) : ReferralInfoState, ReferralInfoContentState
@@ -54,4 +60,12 @@ internal data class ReferralStateHolder(
val onCopyClicked: () -> Unit,
val onShareClicked: (String) -> Unit,
)
+
+ data class AccountAward(
+ val tokenState: TokenItemState,
+ val isBalanceHidden: Boolean,
+ val accountName: TextReference,
+ val accountIcon: CryptoPortfolioIconUM,
+ val onAccountClick: () -> Unit,
+ )
}
\ No newline at end of file
diff --git a/features/referral/impl/src/main/java/com/tangem/feature/referral/ui/ReferralScreen.kt b/features/referral/impl/src/main/java/com/tangem/feature/referral/ui/ReferralScreen.kt
index fcda66f563..3c1f14d3fc 100644
--- a/features/referral/impl/src/main/java/com/tangem/feature/referral/ui/ReferralScreen.kt
+++ b/features/referral/impl/src/main/java/com/tangem/feature/referral/ui/ReferralScreen.kt
@@ -22,17 +22,29 @@ import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.tangem.common.ui.account.AccountIcon
+import com.tangem.common.ui.account.AccountIconPreviewData
import com.tangem.core.res.getStringSafe
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.SpacerH16
import com.tangem.core.ui.components.SpacerH24
import com.tangem.core.ui.components.SpacerH32
+import com.tangem.core.ui.components.account.AccountIconSize
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
+import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.snackbar.CopiedTextSnackbar
import com.tangem.core.ui.components.snackbar.TangemSnackbar
+import com.tangem.core.ui.components.token.TokenItem
+import com.tangem.core.ui.components.token.state.TokenItemState
+import com.tangem.core.ui.components.token.state.TokenItemState.FiatAmountState
+import com.tangem.core.ui.extensions.resolveReference
+import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.stringResourceSafe
+import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.ReferralProgramScreenTestTags
@@ -43,6 +55,7 @@ import com.tangem.feature.referral.models.ReferralStateHolder
import com.tangem.feature.referral.models.ReferralStateHolder.*
import com.tangem.feature.referral.presentation.R
import kotlinx.coroutines.launch
+import java.util.UUID
/**
* Referral program screen for participant and non-participant
@@ -179,7 +192,13 @@ private fun ReferralInfo(
is ReferralInfoState.NonParticipantContent -> {
Conditions(state = state)
- Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing44))
+ if (state.accountAward != null) {
+ SpacerH16()
+ NonParticipateAccountAwardBlock(state.accountAward)
+ SpacerH24()
+ } else {
+ Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing44))
+ }
NonParticipateBottomBlock(
onAgreementClick = onAgreementClick,
onParticipateClick = state.onParticipateClicked,
@@ -366,6 +385,80 @@ private fun ShimmerInfo() {
}
}
+@Composable
+private fun NonParticipateAccountAwardBlock(accountAward: AccountAward) {
+ Column(
+ modifier = Modifier
+ .padding(horizontal = TangemTheme.dimens.spacing16)
+ .background(
+ color = TangemTheme.colors.background.primary,
+ shape = RoundedCornerShape(TangemTheme.dimens.spacing16),
+ ),
+ ) {
+ Text(
+ modifier = Modifier.padding(
+ start = TangemTheme.dimens.spacing16,
+ end = TangemTheme.dimens.spacing16,
+ top = TangemTheme.dimens.spacing12,
+ ),
+ text = stringResourceSafe(R.string.referral_address_for_rewards),
+ style = TangemTheme.typography.subtitle2,
+ color = TangemTheme.colors.text.tertiary,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ )
+
+ TokenItem(
+ state = accountAward.tokenState,
+ isBalanceHidden = accountAward.isBalanceHidden,
+ )
+
+ HorizontalDivider(
+ thickness = 0.5.dp,
+ color = TangemTheme.colors.stroke.primary,
+ modifier = Modifier.padding(horizontal = 12.dp),
+ )
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(TangemTheme.dimens.spacing12),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Text(
+ modifier = Modifier.weight(1f),
+ text = stringResourceSafe(R.string.account_details_title),
+ style = TangemTheme.typography.body1,
+ color = TangemTheme.colors.text.primary1,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ )
+
+ AccountIcon(
+ name = accountAward.accountName,
+ icon = accountAward.accountIcon,
+ size = AccountIconSize.Small,
+ )
+
+ Text(
+ modifier = Modifier.padding(horizontal = 4.dp),
+ text = accountAward.accountName.resolveReference(),
+ style = TangemTheme.typography.body1,
+ color = TangemTheme.colors.text.primary1,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ )
+
+ Icon(
+ modifier = Modifier.size(width = 18.dp, height = 24.dp),
+ painter = painterResource(id = R.drawable.ic_select_18_24),
+ contentDescription = null,
+ tint = TangemTheme.colors.icon.informative,
+ )
+ }
+ }
+}
+
private fun Resources.getMessageForErrorSnackbar(throwable: Throwable): String {
return when {
throwable is DemoModeException -> getStringSafe(R.string.alert_demo_feature_disabled)
@@ -479,6 +572,60 @@ private fun Preview_ReferralScreen_NonParticipant() {
}
}
+@Preview(widthDp = 360, showBackground = true)
+@Preview(widthDp = 360, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun Preview_ReferralScreen_NonParticipantAccount() {
+ TangemThemePreview {
+ ReferralScreen(
+ stateHolder = ReferralStateHolder(
+ headerState = HeaderState(onBackClicked = {}),
+ referralInfoState = ReferralInfoState.NonParticipantContent(
+ award = "10 USDT",
+ networkName = "Tron",
+ discount = "10%",
+ url = "",
+ onParticipateClicked = {},
+ participateButtonIcon = R.drawable.ic_tangem_24,
+ accountAward = AccountAward(
+ onAccountClick = {},
+ accountIcon = AccountIconPreviewData.randomAccountIcon(),
+ accountName = stringReference("Main Account"),
+ isBalanceHidden = false,
+ tokenState = TokenItemState.Content(
+ id = UUID.randomUUID().toString(),
+ iconState = CurrencyIconState.TokenIcon(
+ url = null,
+ topBadgeIconResId = R.drawable.img_tron_22,
+ fallbackTint = TangemColorPalette.Black,
+ fallbackBackground = TangemColorPalette.Meadow,
+ isGrayscale = false,
+ shouldShowCustomBadge = false,
+ ),
+ titleState = TokenItemState.TitleState.Content(
+ text = stringReference(value = "Tether"),
+ ),
+ fiatAmountState = FiatAmountState.Content(
+ text = "129,65 $",
+ ),
+ subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "129,65 USDT"),
+ subtitleState = TokenItemState.SubtitleState.TextContent(value = stringReference("USDT")),
+ onItemClick = {},
+ onItemLongClick = {},
+ ),
+ ),
+ ),
+ errorSnackbar = null,
+ analytics = Analytics(
+ onAgreementClicked = {},
+ onCopyClicked = {},
+ onShareClicked = {},
+ ),
+ ),
+ )
+ }
+}
+
@Preview(widthDp = 360, showBackground = true)
@Preview(widthDp = 360, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable