diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt index 726ee06efc..960857e2eb 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt @@ -24,6 +24,7 @@ import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.CurrencyChecksRepository import com.tangem.domain.tokens.repository.PolkadotAccountHealthCheckRepository import com.tangem.domain.tokens.repository.TokenReceiveWarningsViewedRepository +import com.tangem.domain.tokens.repository.YieldSupplyWarningsViewedRepository import com.tangem.domain.tokens.wallet.WalletBalanceFetcher import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.tap.domain.tokens.DefaultTokensFeatureToggles @@ -455,4 +456,21 @@ internal object TokensDomainModule { ): SaveViewedTokenReceiveWarningUseCase { return SaveViewedTokenReceiveWarningUseCase(tokenReceiveWarningsViewedRepository) } + + @Provides + @Singleton + fun provideNeedShowYieldSupplyDepositedWarningUseCase( + yieldSupplyWarningsViewedRepository: YieldSupplyWarningsViewedRepository, + dispatchers: CoroutineDispatcherProvider, + ): NeedShowYieldSupplyDepositedWarningUseCase { + return NeedShowYieldSupplyDepositedWarningUseCase(yieldSupplyWarningsViewedRepository, dispatchers) + } + + @Provides + @Singleton + fun provideSaveViewedYieldSupplyWarningUseCase( + yieldSupplyWarningsViewedRepository: YieldSupplyWarningsViewedRepository, + ): SaveViewedYieldSupplyWarningUseCase { + return SaveViewedYieldSupplyWarningUseCase(yieldSupplyWarningsViewedRepository) + } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt index 63fbdfebf8..90230d2f23 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt @@ -129,6 +129,8 @@ object PreferencesKeys { val WALLETS_NFT_ENABLED_STATES_KEY by lazy { stringPreferencesKey(name = "walletsNftEnabledStates") } + val YIELD_SUPPLY_WARNINGS_STATES_KEY by lazy { stringPreferencesKey(name = "yieldSupplyWarningsStates") } + // region Notifications val NOTIFICATIONS_APPLICATION_ID_KEY by lazy { stringPreferencesKey(name = "notificationsApplicationId") } diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt index fe5dd5cc34..23f10b2f4d 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt @@ -9,6 +9,7 @@ import com.tangem.data.tokens.repository.DefaultCurrenciesRepository import com.tangem.data.tokens.repository.DefaultCurrencyChecksRepository import com.tangem.data.tokens.repository.DefaultPolkadotAccountHealthCheckRepository import com.tangem.data.tokens.repository.DefaultTokenReceiveWarningsViewedRepository +import com.tangem.data.tokens.repository.DefaultYieldSupplyWarningsViewedRepository import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader import com.tangem.datasource.local.preferences.AppPreferencesStore @@ -19,6 +20,7 @@ import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.CurrencyChecksRepository import com.tangem.domain.tokens.repository.PolkadotAccountHealthCheckRepository import com.tangem.domain.tokens.repository.TokenReceiveWarningsViewedRepository +import com.tangem.domain.tokens.repository.YieldSupplyWarningsViewedRepository import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module @@ -96,4 +98,16 @@ internal object TokensDataModule { tokenReceiveWarningActionStore = tokenReceiveWarningActionStore, ) } + + @Provides + @Singleton + fun provideDefaultYieldSupplyWarningsViewedRepository( + appPreferencesStore: AppPreferencesStore, + dispatchers: CoroutineDispatcherProvider, + ): YieldSupplyWarningsViewedRepository { + return DefaultYieldSupplyWarningsViewedRepository( + appPreferencesStore = appPreferencesStore, + dispatchers = dispatchers, + ) + } } \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultYieldSupplyWarningsViewedRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultYieldSupplyWarningsViewedRepository.kt new file mode 100644 index 0000000000..193bbb03c6 --- /dev/null +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultYieldSupplyWarningsViewedRepository.kt @@ -0,0 +1,36 @@ +package com.tangem.data.tokens.repository + +import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.datasource.local.preferences.PreferencesKeys +import com.tangem.datasource.local.preferences.utils.getObjectSet +import com.tangem.domain.tokens.repository.YieldSupplyWarningsViewedRepository +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.withContext + +internal class DefaultYieldSupplyWarningsViewedRepository( + private val appPreferencesStore: AppPreferencesStore, + private val dispatchers: CoroutineDispatcherProvider, +) : YieldSupplyWarningsViewedRepository { + + override suspend fun getViewedWarnings(): Set = withContext(dispatchers.io) { + appPreferencesStore.getObjectSet(PreferencesKeys.YIELD_SUPPLY_WARNINGS_STATES_KEY).firstOrNull() + ?: emptySet() + } + + override suspend fun view(symbol: String) = withContext(dispatchers.io) { + appPreferencesStore.editData { mutablePreferences -> + val stored = mutablePreferences.getObjectSet( + PreferencesKeys.YIELD_SUPPLY_WARNINGS_STATES_KEY, + ) ?: mutableSetOf() + + val updated = stored + symbol + + mutablePreferences.setObjectSet( + key = PreferencesKeys.YIELD_SUPPLY_WARNINGS_STATES_KEY, + value = updated, + ) + } + return@withContext + } +} \ No newline at end of file diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/details/TokenAction.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/details/TokenAction.kt new file mode 100644 index 0000000000..5c134f0308 --- /dev/null +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/details/TokenAction.kt @@ -0,0 +1,7 @@ +package com.tangem.domain.tokens.model.details + +enum class TokenAction { + Receive, + Send, + Swap, +} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/NeedShowYieldSupplyDepositedWarningUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/NeedShowYieldSupplyDepositedWarningUseCase.kt new file mode 100644 index 0000000000..417e84e987 --- /dev/null +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/NeedShowYieldSupplyDepositedWarningUseCase.kt @@ -0,0 +1,19 @@ +package com.tangem.domain.tokens + +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.tokens.repository.YieldSupplyWarningsViewedRepository +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.withContext + +class NeedShowYieldSupplyDepositedWarningUseCase( + private val yieldSupplyWarningsViewedRepository: YieldSupplyWarningsViewedRepository, + private val dispatchers: CoroutineDispatcherProvider, +) { + + suspend operator fun invoke(cryptoCurrencyStatus: CryptoCurrencyStatus?): Boolean = withContext(dispatchers.io) { + val hasActiveLending = cryptoCurrencyStatus?.value?.yieldSupplyStatus?.isActive == true + if (!hasActiveLending) return@withContext false + val showedWarnings = yieldSupplyWarningsViewedRepository.getViewedWarnings() + return@withContext !showedWarnings.contains(cryptoCurrencyStatus?.currency?.name) + } +} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/SaveViewedYieldSupplyWarningUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/SaveViewedYieldSupplyWarningUseCase.kt new file mode 100644 index 0000000000..8175e13b51 --- /dev/null +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/SaveViewedYieldSupplyWarningUseCase.kt @@ -0,0 +1,11 @@ +package com.tangem.domain.tokens + +import com.tangem.domain.tokens.repository.YieldSupplyWarningsViewedRepository + +class SaveViewedYieldSupplyWarningUseCase( + private val yieldSupplyWarningsViewedRepository: YieldSupplyWarningsViewedRepository, +) { + suspend operator fun invoke(symbol: String) { + yieldSupplyWarningsViewedRepository.view(symbol) + } +} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/YieldSupplyWarningsViewedRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/YieldSupplyWarningsViewedRepository.kt new file mode 100644 index 0000000000..d85720dad6 --- /dev/null +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/YieldSupplyWarningsViewedRepository.kt @@ -0,0 +1,8 @@ +package com.tangem.domain.tokens.repository + +interface YieldSupplyWarningsViewedRepository { + + suspend fun getViewedWarnings(): Set + + suspend fun view(symbol: String) +} \ No newline at end of file diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/NeedShowYieldSupplyDepositedWarningUseCaseTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/NeedShowYieldSupplyDepositedWarningUseCaseTest.kt new file mode 100644 index 0000000000..1873ddeed3 --- /dev/null +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/NeedShowYieldSupplyDepositedWarningUseCaseTest.kt @@ -0,0 +1,107 @@ +package com.tangem.domain.tokens + +import com.google.common.truth.Truth.assertThat +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.network.NetworkAddress +import com.tangem.domain.models.serialization.SerializedBigDecimal +import com.tangem.domain.models.yield.supply.YieldSupplyStatus +import com.tangem.domain.tokens.repository.YieldSupplyWarningsViewedRepository +import com.tangem.domain.tokens.mock.MockTokens +import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.impl.annotations.RelaxedMockK +import io.mockk.junit5.MockKExtension +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@OptIn(ExperimentalCoroutinesApi::class) +@ExtendWith(MockKExtension::class) +class NeedShowYieldSupplyDepositedWarningUseCaseTest { + + @RelaxedMockK + private lateinit var repository: YieldSupplyWarningsViewedRepository + + private lateinit var dispatchers: TestingCoroutineDispatcherProvider + + @BeforeEach + fun setup() { + dispatchers = TestingCoroutineDispatcherProvider() + } + + @Test + fun `GIVEN null status WHEN invoke THEN returns false`() = runTest { + val useCase = NeedShowYieldSupplyDepositedWarningUseCase(repository, dispatchers) + + val result = useCase.invoke(null) + + assertThat(result).isFalse() + coVerify(exactly = 0) { repository.getViewedWarnings() } + } + + @Test + fun `GIVEN inactive lending WHEN invoke THEN returns false`() = runTest { + val useCase = NeedShowYieldSupplyDepositedWarningUseCase(repository, dispatchers) + val status = createStatus(isActive = false) + + val result = useCase.invoke(status) + + assertThat(result).isFalse() + coVerify(exactly = 0) { repository.getViewedWarnings() } + } + + @Test + fun `GIVEN active lending and not viewed WHEN invoke THEN returns true`() = runTest { + val useCase = NeedShowYieldSupplyDepositedWarningUseCase(repository, dispatchers) + val status = createStatus(isActive = true) + coEvery { repository.getViewedWarnings() } returns emptySet() + + val result = useCase.invoke(status) + + assertThat(result).isTrue() + coVerify(exactly = 1) { repository.getViewedWarnings() } + } + + @Test + fun `GIVEN active lending and already viewed WHEN invoke THEN returns false`() = runTest { + val useCase = NeedShowYieldSupplyDepositedWarningUseCase(repository, dispatchers) + val status = createStatus(isActive = true) + coEvery { repository.getViewedWarnings() } returns setOf(status.currency.name) + + val result = useCase.invoke(status) + + assertThat(result).isFalse() + coVerify(exactly = 1) { repository.getViewedWarnings() } + } + + private fun createStatus(isActive: Boolean): CryptoCurrencyStatus { + val currency = MockTokens.token1 + val yieldSupplyStatus = YieldSupplyStatus( + isActive = isActive, + isInitialized = true, + isAllowedToSpend = true, + ) + val value = CryptoCurrencyStatus.NoQuote( + amount = SerializedBigDecimal.ZERO, + yieldBalance = null, + yieldSupplyStatus = yieldSupplyStatus, + hasCurrentNetworkTransactions = false, + pendingTransactions = emptySet(), + networkAddress = NetworkAddress.Single( + defaultAddress = NetworkAddress.Address( + value = "address", + type = NetworkAddress.Address.Type.Primary, + ), + ), + sources = CryptoCurrencyStatus.Sources(), + ) + + return CryptoCurrencyStatus( + currency = currency, + value = value, + ) + } +} \ No newline at end of file diff --git a/features/tokendetails/impl/build.gradle.kts b/features/tokendetails/impl/build.gradle.kts index 3b363faddf..4945e2727a 100644 --- a/features/tokendetails/impl/build.gradle.kts +++ b/features/tokendetails/impl/build.gradle.kts @@ -1,5 +1,6 @@ plugins { alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.serialization) alias(deps.plugins.kotlin.android) alias(deps.plugins.kotlin.kapt) alias(deps.plugins.hilt.android) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/DefaultTokenDetailsComponent.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/DefaultTokenDetailsComponent.kt index 6c5befecc6..5de293633a 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/DefaultTokenDetailsComponent.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/DefaultTokenDetailsComponent.kt @@ -15,15 +15,16 @@ import com.tangem.core.decompose.context.childByContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.components.NavigationBar3ButtonsScrim import com.tangem.core.ui.decompose.ComposableBottomSheetComponent -import com.tangem.domain.models.TokenReceiveConfig import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsModel +import com.tangem.feature.tokendetails.presentation.tokendetails.route.TokenDetailsBottomSheetConfig import com.tangem.feature.tokendetails.presentation.tokendetails.ui.TokenDetailsScreen import com.tangem.features.markets.token.block.TokenMarketBlockComponent import com.tangem.features.tokendetails.TokenDetailsComponent import com.tangem.features.tokenreceive.TokenReceiveComponent import com.tangem.features.txhistory.component.TxHistoryComponent import com.tangem.features.yield.supply.api.YieldSupplyComponent +import com.tangem.features.yield.supply.api.YieldSupplyDepositedWarningComponent import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -35,6 +36,7 @@ internal class DefaultTokenDetailsComponent @AssistedInject constructor( tokenMarketBlockComponentFactory: TokenMarketBlockComponent.Factory, txHistoryComponentFactory: TxHistoryComponent.Factory, private val tokenReceiveComponentFactory: TokenReceiveComponent.Factory, + private val yieldSupplyWarningComponentFactory: YieldSupplyDepositedWarningComponent.Factory, yieldSupplyComponentFactory: YieldSupplyComponent.Factory, ) : TokenDetailsComponent, AppComponentContext by appComponentContext { @@ -50,7 +52,7 @@ internal class DefaultTokenDetailsComponent @AssistedInject constructor( private val bottomSheetSlot = childSlot( source = model.bottomSheetNavigation, - serializer = TokenReceiveConfig.serializer(), + serializer = TokenDetailsBottomSheetConfig.serializer(), handleBackButton = false, childFactory = ::bottomSheetChild, ) @@ -98,15 +100,26 @@ internal class DefaultTokenDetailsComponent @AssistedInject constructor( } private fun bottomSheetChild( - config: TokenReceiveConfig, + route: TokenDetailsBottomSheetConfig, componentContext: ComponentContext, - ): ComposableBottomSheetComponent = tokenReceiveComponentFactory.create( - context = childByContext(componentContext), - params = TokenReceiveComponent.Params( - config = config, - onDismiss = model.bottomSheetNavigation::dismiss, - ), - ) + ): ComposableBottomSheetComponent = when (route) { + is TokenDetailsBottomSheetConfig.Receive -> tokenReceiveComponentFactory.create( + context = childByContext(componentContext), + params = TokenReceiveComponent.Params( + config = route.tokenReceiveConfig, + onDismiss = model.bottomSheetNavigation::dismiss, + ), + ) + is TokenDetailsBottomSheetConfig.YieldSupplyWarning -> yieldSupplyWarningComponentFactory.create( + context = childByContext(componentContext), + params = YieldSupplyDepositedWarningComponent.Params( + cryptoCurrency = route.cryptoCurrency, + onDismiss = model.bottomSheetNavigation::dismiss, + modelCallback = model, + tokenAction = route.tokenAction, + ), + ) + } @AssistedFactory interface Factory : TokenDetailsComponent.Factory { diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt index 12d907cfef..c1c4ef7ec8 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt @@ -5,6 +5,7 @@ import arrow.core.getOrElse import arrow.core.merge import com.arkivanov.decompose.router.slot.SlotNavigation import com.arkivanov.decompose.router.slot.activate +import com.arkivanov.decompose.router.slot.dismiss import com.tangem.blockchain.common.address.AddressType import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRouter @@ -58,6 +59,7 @@ import com.tangem.domain.tokens.model.TokenActionsState import com.tangem.domain.tokens.model.analytics.* import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.Companion.toReasonAnalyticsText import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.DetailsScreenOpened.TokenBalance +import com.tangem.domain.tokens.model.details.TokenAction import com.tangem.domain.transaction.error.AssociateAssetError import com.tangem.domain.transaction.error.IncompleteTransactionError import com.tangem.domain.transaction.error.OpenTrustlineError @@ -72,6 +74,7 @@ import com.tangem.feature.tokendetails.deeplink.TokenDetailsDeepLinkActionListen import com.tangem.feature.tokendetails.presentation.router.InnerTokenDetailsRouter import com.tangem.feature.tokendetails.presentation.tokendetails.analytics.TokenDetailsCurrencyStatusAnalyticsSender import com.tangem.feature.tokendetails.presentation.tokendetails.analytics.TokenDetailsNotificationsAnalyticsSender +import com.tangem.feature.tokendetails.presentation.tokendetails.route.TokenDetailsBottomSheetConfig import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenBalanceSegmentedButtonConfig import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsStateFactory @@ -81,6 +84,7 @@ import com.tangem.features.tokendetails.TokenDetailsComponent import com.tangem.features.tokendetails.impl.R import com.tangem.features.tokenreceive.TokenReceiveFeatureToggle import com.tangem.features.txhistory.entity.TxHistoryContentUpdateEmitter +import com.tangem.features.yield.supply.api.YieldSupplyDepositedWarningComponent import com.tangem.features.yield.supply.api.YieldSupplyFeatureToggles import com.tangem.utils.Provider import com.tangem.utils.coroutines.* @@ -139,7 +143,10 @@ internal class TokenDetailsModel @Inject constructor( private val getViewedTokenReceiveWarningUseCase: GetViewedTokenReceiveWarningUseCase, private val getEnsNameUseCase: GetEnsNameUseCase, private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles, -) : Model(), TokenDetailsClickIntents { + private val saveViewedYieldSupplyWarningUseCase: SaveViewedYieldSupplyWarningUseCase, + private val saveViewedTokenReceiveWarningUseCase: SaveViewedTokenReceiveWarningUseCase, + private val needShowYieldSupplyDepositedWarningUseCase: NeedShowYieldSupplyDepositedWarningUseCase, +) : Model(), TokenDetailsClickIntents, YieldSupplyDepositedWarningComponent.ModelCallback { private val params = paramsContainer.require() private val userWalletId: UserWalletId = params.userWalletId @@ -162,7 +169,7 @@ internal class TokenDetailsModel @Inject constructor( /** Transaction id to check for status */ private val waitForFirstExpressStatusEmmit = MutableStateFlow(false) - val bottomSheetNavigation: SlotNavigation = SlotNavigation() + val bottomSheetNavigation: SlotNavigation = SlotNavigation() private val stateFactory = TokenDetailsStateFactory( currentStateProvider = Provider { uiState.value }, @@ -527,8 +534,18 @@ internal class TokenDetailsModel @Inject constructor( if (handleUnavailabilityReason(unavailabilityReason = unavailabilityReason)) { return } - - sendCurrency() + modelScope.launch { + if (needShowYieldSupplyDepositedWarningUseCase(cryptoCurrencyStatus)) { + bottomSheetNavigation.activate( + configuration = TokenDetailsBottomSheetConfig.YieldSupplyWarning( + cryptoCurrency = cryptoCurrency, + tokenAction = TokenAction.Send, + ), + ) + } else { + sendCurrency() + } + } } private fun sendCurrency() { @@ -563,26 +580,15 @@ internal class TokenDetailsModel @Inject constructor( } modelScope.launch { - if (tokenReceiveFeatureToggle.isNewTokenReceiveEnabled) { + if (needShowYieldSupplyWarning()) { bottomSheetNavigation.activate( - configuration = configureReceiveAddresses(addresses = networkAddress), + configuration = TokenDetailsBottomSheetConfig.YieldSupplyWarning( + cryptoCurrency = cryptoCurrency, + tokenAction = TokenAction.Receive, + ), ) } else { - analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ReceiveScreenOpened(cryptoCurrency.symbol)) - internalUiState.value = stateFactory.getStateWithReceiveBottomSheet( - currency = cryptoCurrency, - networkAddress = networkAddress, - onCopyClick = { - analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ButtonCopyAddress(cryptoCurrency.symbol)) - clipboardManager.setText(text = it, isSensitive = true) - }, - onShareClick = { - analyticsEventsHandler.send( - TokenReceiveAnalyticsEvent.ButtonShareAddress(cryptoCurrency.symbol), - ) - shareManager.shareText(text = it) - }, - ) + navigateToReceive() } } } @@ -656,13 +662,24 @@ internal class TokenDetailsModel @Inject constructor( return } - appRouter.push( - AppRoute.Swap( - currencyFrom = cryptoCurrency, - userWalletId = userWalletId, - screenSource = AnalyticsParam.ScreensSources.Token.value, - ), - ) + modelScope.launch { + if (needShowYieldSupplyDepositedWarningUseCase(cryptoCurrencyStatus)) { + bottomSheetNavigation.activate( + configuration = TokenDetailsBottomSheetConfig.YieldSupplyWarning( + cryptoCurrency = cryptoCurrency, + tokenAction = TokenAction.Swap, + ), + ) + } else { + appRouter.push( + AppRoute.Swap( + currencyFrom = cryptoCurrency, + userWalletId = userWalletId, + screenSource = AnalyticsParam.ScreensSources.Token.value, + ), + ) + } + } } override fun onDismissDialog() { @@ -1040,7 +1057,7 @@ internal class TokenDetailsModel @Inject constructor( .launchIn(modelScope) } - private suspend fun configureReceiveAddresses(addresses: NetworkAddress): TokenReceiveConfig { + private suspend fun configureReceiveAddresses(addresses: NetworkAddress): TokenDetailsBottomSheetConfig { val ensName = getEnsNameUseCase.invoke( userWalletId = userWalletId, network = cryptoCurrency.network, @@ -1069,12 +1086,14 @@ internal class TokenDetailsModel @Inject constructor( } } - return TokenReceiveConfig( - shouldShowWarning = cryptoCurrency.name !in getViewedTokenReceiveWarningUseCase(), - cryptoCurrency = cryptoCurrency, - userWalletId = userWalletId, - showMemoDisclaimer = cryptoCurrency.network.transactionExtrasType != Network.TransactionExtrasType.NONE, - receiveAddress = receiveAddresses, + return TokenDetailsBottomSheetConfig.Receive( + TokenReceiveConfig( + shouldShowWarning = cryptoCurrency.name !in getViewedTokenReceiveWarningUseCase(), + cryptoCurrency = cryptoCurrency, + userWalletId = userWalletId, + showMemoDisclaimer = cryptoCurrency.network.transactionExtrasType != Network.TransactionExtrasType.NONE, + receiveAddress = receiveAddresses, + ), ) } @@ -1115,6 +1134,59 @@ internal class TokenDetailsModel @Inject constructor( isBalanceLoadedEventSent = true } + private suspend fun needShowYieldSupplyWarning(): Boolean { + return yieldSupplyFeatureToggles.isYieldSupplyFeatureEnabled && + needShowYieldSupplyDepositedWarningUseCase(cryptoCurrencyStatus) + } + + override fun onYieldSupplyWarningAcknowledged(tokenAction: TokenAction) { + bottomSheetNavigation.dismiss() + modelScope.launch { + saveViewedYieldSupplyWarningUseCase(cryptoCurrency.name) + if (tokenAction == TokenAction.Receive) { + saveViewedTokenReceiveWarningUseCase(cryptoCurrency.name) + } + when (tokenAction) { + TokenAction.Receive -> navigateToReceive() + TokenAction.Send -> sendCurrency() + TokenAction.Swap -> appRouter.push( + AppRoute.Swap( + currencyFrom = cryptoCurrency, + userWalletId = userWalletId, + screenSource = AnalyticsParam.ScreensSources.Token.value, + ), + ) + } + } + } + + private fun navigateToReceive() { + val networkAddress = cryptoCurrencyStatus?.value?.networkAddress ?: return + if (tokenReceiveFeatureToggle.isNewTokenReceiveEnabled) { + modelScope.launch { + bottomSheetNavigation.activate( + configuration = configureReceiveAddresses(addresses = networkAddress), + ) + } + } else { + analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ReceiveScreenOpened(cryptoCurrency.symbol)) + internalUiState.value = stateFactory.getStateWithReceiveBottomSheet( + currency = cryptoCurrency, + networkAddress = networkAddress, + onCopyClick = { + analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ButtonCopyAddress(cryptoCurrency.symbol)) + clipboardManager.setText(text = it, isSensitive = true) + }, + onShareClick = { + analyticsEventsHandler.send( + TokenReceiveAnalyticsEvent.ButtonShareAddress(cryptoCurrency.symbol), + ) + shareManager.shareText(text = it) + }, + ) + } + } + private companion object { const val EXPRESS_STATUS_UPDATE_DELAY = 10_000L } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/route/TokenDetailsBottomSheetConfig.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/route/TokenDetailsBottomSheetConfig.kt new file mode 100644 index 0000000000..dfba415f3b --- /dev/null +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/route/TokenDetailsBottomSheetConfig.kt @@ -0,0 +1,20 @@ +package com.tangem.feature.tokendetails.presentation.tokendetails.route + +import com.tangem.core.decompose.navigation.Route +import com.tangem.domain.models.TokenReceiveConfig +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.tokens.model.details.TokenAction +import kotlinx.serialization.Serializable + +@Serializable +sealed class TokenDetailsBottomSheetConfig : Route { + + @Serializable + data class Receive(val tokenReceiveConfig: TokenReceiveConfig) : TokenDetailsBottomSheetConfig() + + @Serializable + data class YieldSupplyWarning( + val cryptoCurrency: CryptoCurrency, + val tokenAction: TokenAction, + ) : TokenDetailsBottomSheetConfig() +} \ No newline at end of file diff --git a/features/yield-supply/api/src/main/java/com/tangem/features/yield/supply/api/YieldSupplyDepositedWarningComponent.kt b/features/yield-supply/api/src/main/java/com/tangem/features/yield/supply/api/YieldSupplyDepositedWarningComponent.kt index de39187872..086fd34e5d 100644 --- a/features/yield-supply/api/src/main/java/com/tangem/features/yield/supply/api/YieldSupplyDepositedWarningComponent.kt +++ b/features/yield-supply/api/src/main/java/com/tangem/features/yield/supply/api/YieldSupplyDepositedWarningComponent.kt @@ -4,17 +4,19 @@ import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.tokens.model.details.TokenAction interface YieldSupplyDepositedWarningComponent : ComposableBottomSheetComponent { data class Params( val cryptoCurrency: CryptoCurrency, + val tokenAction: TokenAction, val modelCallback: ModelCallback, val onDismiss: () -> Unit, ) interface ModelCallback { - fun onYieldSupplyWarningAcknowledged() + fun onYieldSupplyWarningAcknowledged(tokenAction: TokenAction) } interface Factory : ComponentFactory { diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/warning/model/YieldSupplyDepositedWarningModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/warning/model/YieldSupplyDepositedWarningModel.kt index e6a12d759b..aa994f08e2 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/warning/model/YieldSupplyDepositedWarningModel.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/warning/model/YieldSupplyDepositedWarningModel.kt @@ -28,7 +28,9 @@ internal class YieldSupplyDepositedWarningModel @Inject constructor( field = MutableStateFlow( YieldSupplyDepositedWarningUM( iconState = iconStateConverter.convert(params.cryptoCurrency), - onWarningAcknowledged = params.modelCallback::onYieldSupplyWarningAcknowledged, + onWarningAcknowledged = { + params.modelCallback.onYieldSupplyWarningAcknowledged(params.tokenAction) + }, network = params.cryptoCurrency.name, ), ) diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/warning/ui/YieldSupplyDepositedWarningContent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/warning/ui/YieldSupplyDepositedWarningContent.kt index 4a07dbf93a..fde7716c00 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/warning/ui/YieldSupplyDepositedWarningContent.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/warning/ui/YieldSupplyDepositedWarningContent.kt @@ -40,7 +40,7 @@ internal fun YieldSupplyDepositedWarningContent(warningUM: YieldSupplyDepositedW onDismissRequest = onDismiss, content = TangemBottomSheetConfigContent.Empty, ), - containerColor = TangemTheme.colors.background.primary, + containerColor = TangemTheme.colors.background.tertiary, onBack = null, title = { TangemModalBottomSheetTitle( @@ -57,7 +57,7 @@ internal fun YieldSupplyDepositedWarningContent(warningUM: YieldSupplyDepositedW .padding(horizontal = 16.dp) .fillMaxWidth(), text = stringResourceSafe(CoreUiR.string.balance_hidden_got_it_button), - onClick = onDismiss, + onClick = warningUM.onWarningAcknowledged, ) }, ) @@ -68,7 +68,6 @@ private fun Content(warningUM: YieldSupplyDepositedWarningUM) { Column( modifier = Modifier .fillMaxWidth() - .background(color = TangemTheme.colors.background.primary) .padding( start = 16.dp, end = 16.dp,