diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt index bc3d85e945..6d24482b1f 100644 --- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt @@ -43,6 +43,7 @@ import com.tangem.features.tangempay.components.TangemPayOnboardingComponent.Par import com.tangem.features.tokendetails.TokenDetailsComponent import com.tangem.features.wallet.WalletEntryComponent import com.tangem.features.walletconnect.components.WalletConnectEntryComponent +import com.tangem.features.yield.supply.api.YieldSupplyActiveComponent import com.tangem.features.yield.supply.api.YieldSupplyPromoComponent import com.tangem.tap.features.details.ui.appcurrency.api.AppCurrencySelectorComponent import com.tangem.tap.features.details.ui.appsettings.api.AppSettingsComponent @@ -116,6 +117,7 @@ internal class ChildFactory @Inject constructor( private val tangemPayOnboardingComponentFactory: TangemPayOnboardingComponent.Factory, private val kycComponentFactory: KycComponent.Factory, private val yieldSupplyPromoComponentFactory: YieldSupplyPromoComponent.Factory, + private val yieldSupplyActiveComponentFactory: YieldSupplyActiveComponent.Factory, private val hotWalletFeatureToggles: HotWalletFeatureToggles, ) { @@ -681,6 +683,16 @@ internal class ChildFactory @Inject constructor( componentFactory = yieldSupplyPromoComponentFactory, ) } + is AppRoute.YieldSupplyActive -> { + createComponentChild( + context = context, + params = YieldSupplyActiveComponent.Params( + userWalletId = route.userWalletId, + cryptoCurrency = route.cryptoCurrency, + ), + componentFactory = yieldSupplyActiveComponentFactory, + ) + } } } } \ No newline at end of file diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt index 92ab4f3da6..ab6a1ccc86 100644 --- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt @@ -446,4 +446,11 @@ sealed class AppRoute(val path: String) : Route { val cryptoCurrency: CryptoCurrency, val apy: String, ) : AppRoute(path = "/yield_supply_promo/${userWalletId.stringValue}/${cryptoCurrency.symbol}") + + @Serializable + data class YieldSupplyActive( + val userWalletId: UserWalletId, + val cryptoCurrency: CryptoCurrency, + val apy: String, + ) : AppRoute(path = "/yield_supply_active/${userWalletId.stringValue}/${cryptoCurrency.symbol}") } \ No newline at end of file diff --git a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultFeeRepository.kt b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultFeeRepository.kt index 07898f241d..5cd4f6c806 100644 --- a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultFeeRepository.kt +++ b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultFeeRepository.kt @@ -16,6 +16,7 @@ import com.tangem.domain.demo.models.DemoConfig import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.transaction.FeeRepository import com.tangem.domain.walletmanager.WalletManagersFacade import java.math.BigDecimal @@ -31,11 +32,11 @@ internal class DefaultFeeRepository( } override suspend fun getEthereumFeeWithoutGas( - userWallet: UserWallet, + userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, ): Fee.Ethereum { val walletManager = walletManagersFacade.getOrCreateWalletManager( - userWalletId = userWallet.walletId, + userWalletId = userWalletId, network = cryptoCurrency.network, ) diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/FeeRepository.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/FeeRepository.kt index f0bfdcf442..a148692971 100644 --- a/domain/transaction/src/main/java/com/tangem/domain/transaction/FeeRepository.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/FeeRepository.kt @@ -7,6 +7,7 @@ import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId interface FeeRepository { @@ -20,5 +21,5 @@ interface FeeRepository { transactionData: TransactionData, ): TransactionFee - suspend fun getEthereumFeeWithoutGas(userWallet: UserWallet, cryptoCurrency: CryptoCurrency): Fee.Ethereum + suspend fun getEthereumFeeWithoutGas(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): Fee.Ethereum } \ No newline at end of file diff --git a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyEstimateEnterFeeUseCase.kt b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyEstimateEnterFeeUseCase.kt index e3b24f7f96..9a9da874ed 100644 --- a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyEstimateEnterFeeUseCase.kt +++ b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyEstimateEnterFeeUseCase.kt @@ -70,7 +70,7 @@ class YieldSupplyEstimateEnterFeeUseCase( if (estimatedFees.estimatedGasList.isEmpty()) return null val fee = feeRepository.getEthereumFeeWithoutGas( - userWallet = userWallet, + userWalletId = userWallet.walletId, cryptoCurrency = cryptoCurrency, ) diff --git a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetCurrentFeeUseCase.kt b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetCurrentFeeUseCase.kt index c9ee2d6a93..460440f4b4 100644 --- a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetCurrentFeeUseCase.kt +++ b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetCurrentFeeUseCase.kt @@ -2,14 +2,14 @@ package com.tangem.domain.yield.supply.usecase import arrow.core.Either import arrow.core.Either.Companion.catch -import com.tangem.domain.yield.supply.fixFee import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.quote.QuoteStatus -import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.quotes.QuotesRepository import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.transaction.FeeRepository import com.tangem.domain.yield.supply.YieldSupplyConst.YIELD_SUPPLY_EVM_CONSTANT_GAS_LIMIT +import com.tangem.domain.yield.supply.fixFee import java.math.BigDecimal import java.math.RoundingMode @@ -23,16 +23,16 @@ class YieldSupplyGetCurrentFeeUseCase( ) { suspend operator fun invoke( - userWallet: UserWallet, + userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus, ): Either = catch { - val feeWithoutGas = feeRepository.getEthereumFeeWithoutGas(userWallet, cryptoCurrencyStatus.currency) + val feeWithoutGas = feeRepository.getEthereumFeeWithoutGas(userWalletId, cryptoCurrencyStatus.currency) val fiatRate = cryptoCurrencyStatus.value.fiatRate ?: error("Fiat rate is missing") require(fiatRate > BigDecimal.ZERO) { "Fiat rate for token must be > 0" } val nativeCryptoCurrency = currenciesRepository.getNetworkCoin( - userWalletId = userWallet.walletId, + userWalletId = userWalletId, networkId = cryptoCurrencyStatus.currency.network.id, derivationPath = cryptoCurrencyStatus.currency.network.derivationPath, ) diff --git a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetMaxFeeUseCase.kt b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetMaxFeeUseCase.kt index ed746b30a4..516452538c 100644 --- a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetMaxFeeUseCase.kt +++ b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetMaxFeeUseCase.kt @@ -6,7 +6,7 @@ import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.currency.yieldSupplyKey import com.tangem.domain.models.quote.QuoteStatus -import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.quotes.QuotesRepository import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.yield.supply.YieldSupplyRepository @@ -32,7 +32,7 @@ class YieldSupplyGetMaxFeeUseCase( ) { suspend operator fun invoke( - userWallet: UserWallet, + userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus, ): Either = catch { val token = cryptoCurrencyStatus.currency as? CryptoCurrency.Token @@ -42,7 +42,7 @@ class YieldSupplyGetMaxFeeUseCase( require(fiatRate > BigDecimal.ZERO) { "Fiat rate for token must be > 0" } val nativeCryptoCurrency = currenciesRepository.getNetworkCoin( - userWalletId = userWallet.walletId, + userWalletId = userWalletId, networkId = cryptoCurrencyStatus.currency.network.id, derivationPath = cryptoCurrencyStatus.currency.network.derivationPath, ) diff --git a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyMinAmountUseCase.kt b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyMinAmountUseCase.kt index 9e6f8b109d..501e6c74f5 100644 --- a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyMinAmountUseCase.kt +++ b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyMinAmountUseCase.kt @@ -2,14 +2,14 @@ package com.tangem.domain.yield.supply.usecase import arrow.core.Either import arrow.core.Either.Companion.catch -import com.tangem.domain.yield.supply.fixFee import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.quote.QuoteStatus -import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.quotes.QuotesRepository import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.transaction.FeeRepository import com.tangem.domain.yield.supply.YieldSupplyConst.YIELD_SUPPLY_EVM_CONSTANT_GAS_LIMIT +import com.tangem.domain.yield.supply.fixFee import java.math.BigDecimal import java.math.RoundingMode @@ -20,16 +20,16 @@ class YieldSupplyMinAmountUseCase( ) { suspend operator fun invoke( - userWallet: UserWallet, + userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus, ): Either = catch { - val feeWithoutGas = feeRepository.getEthereumFeeWithoutGas(userWallet, cryptoCurrencyStatus.currency) + val feeWithoutGas = feeRepository.getEthereumFeeWithoutGas(userWalletId, cryptoCurrencyStatus.currency) val fiatRate = cryptoCurrencyStatus.value.fiatRate ?: error("Fiat rate is missing") require(fiatRate > BigDecimal.ZERO) { "Fiat rate for token must be > 0" } val nativeCryptoCurrency = currenciesRepository.getNetworkCoin( - userWalletId = userWallet.walletId, + userWalletId = userWalletId, networkId = cryptoCurrencyStatus.currency.network.id, derivationPath = cryptoCurrencyStatus.currency.network.derivationPath, ) diff --git a/domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/YieldSupplyEstimateEnterFeeUseCaseTest.kt b/domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/YieldSupplyEstimateEnterFeeUseCaseTest.kt index 38e789a341..94016321e9 100644 --- a/domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/YieldSupplyEstimateEnterFeeUseCaseTest.kt +++ b/domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/YieldSupplyEstimateEnterFeeUseCaseTest.kt @@ -34,7 +34,9 @@ class YieldSupplyEstimateEnterFeeUseCaseTest { private val blockAidGasEstimate: BlockAidGasEstimate = mockk() private val useCase = YieldSupplyEstimateEnterFeeUseCase(feeRepository, feeErrorResolver, blockAidGasEstimate) - private val userWallet: UserWallet = mockk() + private val userWallet: UserWallet = mockk { + every { walletId } returns mockk() + } private val cryptoCurrency: CryptoCurrency = mockk(relaxed = true) { every { decimals } returns 18 } diff --git a/domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/YieldSupplyMinAmountUseCaseTest.kt b/domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/YieldSupplyMinAmountUseCaseTest.kt index c140d8f35e..715c90c6a3 100644 --- a/domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/YieldSupplyMinAmountUseCaseTest.kt +++ b/domain/yield-supply/src/test/java/com/tangem/domain/yield/supply/YieldSupplyMinAmountUseCaseTest.kt @@ -65,7 +65,7 @@ class YieldSupplyMinAmountUseCaseTest { val maxFeePerGas = BigInteger("158320679232") val fee = createEip1559Fee(maxFeePerGas) - coEvery { feeRepository.getEthereumFeeWithoutGas(userWallet, token) } returns fee + coEvery { feeRepository.getEthereumFeeWithoutGas(userWallet.walletId, token) } returns fee coEvery { currenciesRepository.getNetworkCoin( userWalletId = userWallet.walletId, @@ -88,7 +88,7 @@ class YieldSupplyMinAmountUseCaseTest { ), ) val expected = expectedMinAmount(maxFeePerGas, nativeFiatRate, tokenFiatRate, token.decimals) - val result = useCase(userWallet, tokenStatus).getOrNull() + val result = useCase(userWallet.walletId, tokenStatus).getOrNull() Truth.assertThat(result).isEqualTo(expected) } @@ -114,7 +114,7 @@ class YieldSupplyMinAmountUseCaseTest { ), ) val userWallet = createUserWallet() - val result = useCase(userWallet, tokenStatus) + val result = useCase(userWallet.walletId, tokenStatus) Truth.assertThat(result.isLeft()).isTrue() Truth.assertThat(result.leftOrNull()?.message).isEqualTo("Fiat rate is missing") } @@ -145,7 +145,7 @@ class YieldSupplyMinAmountUseCaseTest { val maxFeePerGas = BigInteger("158320679232") val fee = createEip1559Fee(maxFeePerGas) - coEvery { feeRepository.getEthereumFeeWithoutGas(userWallet, token) } returns fee + coEvery { feeRepository.getEthereumFeeWithoutGas(userWallet.walletId, token) } returns fee coEvery { currenciesRepository.getNetworkCoin( userWalletId = userWallet.walletId, @@ -157,7 +157,7 @@ class YieldSupplyMinAmountUseCaseTest { coEvery { quotesRepository.getMultiQuoteSyncOrNull(setOf(nativeCoin.id.rawCurrencyId!!)) } returns null - val result = useCase(userWallet, tokenStatus) + val result = useCase(userWallet.walletId, tokenStatus) Truth.assertThat(result.isLeft()).isTrue() Truth.assertThat(result.leftOrNull()?.message).isEqualTo("Quotes for native coin are unavailable") } diff --git a/features/yield-supply/api/src/main/java/com/tangem/features/yield/supply/api/YieldSupplyActiveComponent.kt b/features/yield-supply/api/src/main/java/com/tangem/features/yield/supply/api/YieldSupplyActiveComponent.kt new file mode 100644 index 0000000000..16b2e0ca1d --- /dev/null +++ b/features/yield-supply/api/src/main/java/com/tangem/features/yield/supply/api/YieldSupplyActiveComponent.kt @@ -0,0 +1,16 @@ +package com.tangem.features.yield.supply.api + +import com.tangem.core.decompose.factory.ComponentFactory +import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.wallet.UserWalletId + +interface YieldSupplyActiveComponent : ComposableContentComponent { + + data class Params( + val userWalletId: UserWalletId, + val cryptoCurrency: CryptoCurrency, + ) + + interface Factory : ComponentFactory +} \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/DefaultYieldSupplyActiveComponent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/DefaultYieldSupplyActiveComponent.kt new file mode 100644 index 0000000000..eee5a64f8e --- /dev/null +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/DefaultYieldSupplyActiveComponent.kt @@ -0,0 +1,140 @@ +package com.tangem.features.yield.supply.impl.active + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.arkivanov.decompose.extensions.compose.subscribeAsState +import com.arkivanov.decompose.router.slot.childSlot +import com.tangem.common.routing.AppRouter +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.context.child +import com.tangem.core.decompose.context.childByContext +import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.core.ui.components.Fade +import com.tangem.core.ui.components.SecondaryButton +import com.tangem.core.ui.decompose.ComposableBottomSheetComponent +import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.res.TangemTheme +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.features.yield.supply.api.YieldSupplyActiveComponent +import com.tangem.features.yield.supply.impl.R +import com.tangem.features.yield.supply.impl.active.model.YieldSupplyActiveModel +import com.tangem.features.yield.supply.impl.active.model.YieldSupplyActiveRoute +import com.tangem.features.yield.supply.impl.active.ui.YieldSupplyActiveContent +import com.tangem.features.yield.supply.impl.active.ui.YieldSupplyActiveTitle +import com.tangem.features.yield.supply.impl.chart.DefaultYieldSupplyChartComponent +import com.tangem.features.yield.supply.impl.subcomponents.approve.YieldSupplyApproveComponent +import com.tangem.features.yield.supply.impl.subcomponents.stopearning.YieldSupplyStopEarningComponent +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +internal class DefaultYieldSupplyActiveComponent @AssistedInject constructor( + @Assisted private val appComponentContext: AppComponentContext, + @Assisted private val params: YieldSupplyActiveComponent.Params, + private val appRouter: AppRouter, +) : YieldSupplyActiveComponent, AppComponentContext by appComponentContext { + + private val model: YieldSupplyActiveModel = getOrCreateModel(params = params) + + private val chartComponent = DefaultYieldSupplyChartComponent( + appComponentContext = child("chartComponent"), + params = DefaultYieldSupplyChartComponent.Params( + cryptoCurrency = model.cryptoCurrencyStatusFlow.value.currency as CryptoCurrency.Token, + ), + ) + + private val bottomSheetSlot = childSlot( + key = "yieldSupplyActiveStack", + source = model.slotNavigation, + serializer = null, + handleBackButton = true, + childFactory = { configuration, factoryContext -> + createChild( + configuration, + childByContext( + componentContext = factoryContext, + ), + ) + }, + ) + + @Composable + override fun Content(modifier: Modifier) { + val state by model.uiState.collectAsStateWithLifecycle() + val isBalanceHidden by model.balanceHiddenFlow.collectAsStateWithLifecycle() + val slotState by bottomSheetSlot.subscribeAsState() + + Column( + modifier = Modifier + .background(TangemTheme.colors.background.secondary) + .fillMaxSize() + .systemBarsPadding(), + ) { + YieldSupplyActiveTitle(onCloseClick = appRouter::pop) + + Box(modifier = Modifier.weight(1f)) { + YieldSupplyActiveContent( + state = state, + isBalanceHidden = isBalanceHidden, + chartComponent = chartComponent, + onReadMoreClick = model::onReadMoreClick, + ) + + Fade( + backgroundColor = TangemTheme.colors.background.tertiary, + modifier = Modifier.align(Alignment.BottomCenter), + ) + } + + SecondaryButton( + text = stringResourceSafe(R.string.yield_module_stop_earning), + onClick = model::onStopEarning, + modifier = Modifier + .fillMaxWidth() + .padding( + start = 16.dp, + end = 16.dp, + bottom = 16.dp, + ), + ) + } + + slotState.child?.instance?.BottomSheet() + } + + private fun createChild( + route: YieldSupplyActiveRoute, + factoryContext: AppComponentContext, + ): ComposableBottomSheetComponent = when (route) { + YieldSupplyActiveRoute.Exit -> YieldSupplyStopEarningComponent( + appComponentContext = factoryContext, + params = YieldSupplyStopEarningComponent.Params( + userWallet = model.userWallet, + cryptoCurrencyStatusFlow = model.cryptoCurrencyStatusFlow, + callback = model, + ), + ) + YieldSupplyActiveRoute.Approve -> YieldSupplyApproveComponent( + appComponentContext = factoryContext, + params = YieldSupplyApproveComponent.Params( + userWallet = model.userWallet, + cryptoCurrencyStatusFlow = model.cryptoCurrencyStatusFlow, + callback = model, + ), + ) + } + + @AssistedFactory + interface Factory : YieldSupplyActiveComponent.Factory { + override fun create( + context: AppComponentContext, + params: YieldSupplyActiveComponent.Params, + ): DefaultYieldSupplyActiveComponent + } +} \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/di/YieldSupplyActiveModule.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/di/YieldSupplyActiveModule.kt new file mode 100644 index 0000000000..3404860b86 --- /dev/null +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/di/YieldSupplyActiveModule.kt @@ -0,0 +1,34 @@ +package com.tangem.features.yield.supply.impl.active.di + +import com.tangem.core.decompose.di.ModelComponent +import com.tangem.core.decompose.model.Model +import com.tangem.features.yield.supply.api.YieldSupplyActiveComponent +import com.tangem.features.yield.supply.impl.active.DefaultYieldSupplyActiveComponent +import com.tangem.features.yield.supply.impl.active.model.YieldSupplyActiveModel +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal interface YieldSupplyActiveBindsModule { + @Binds + @Singleton + fun provideYieldSupplyActiveComponentFactory( + impl: DefaultYieldSupplyActiveComponent.Factory, + ): YieldSupplyActiveComponent.Factory +} + +@Module +@InstallIn(ModelComponent::class) +internal interface YieldSupplyActiveModule { + + @Binds + @IntoMap + @ClassKey(YieldSupplyActiveModel::class) + fun provideYieldSupplyActiveModel(impl: YieldSupplyActiveModel): Model +} \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/entity/YieldSupplyActiveContentUM.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/entity/YieldSupplyActiveContentUM.kt similarity index 90% rename from features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/entity/YieldSupplyActiveContentUM.kt rename to features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/entity/YieldSupplyActiveContentUM.kt index eea128679c..1cdb3d72d8 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/entity/YieldSupplyActiveContentUM.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/entity/YieldSupplyActiveContentUM.kt @@ -1,4 +1,4 @@ -package com.tangem.features.yield.supply.impl.subcomponents.active.entity +package com.tangem.features.yield.supply.impl.active.entity import com.tangem.common.ui.notifications.NotificationUM import com.tangem.core.ui.extensions.TextReference diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/YieldSupplyActiveModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/YieldSupplyActiveModel.kt similarity index 54% rename from features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/YieldSupplyActiveModel.kt rename to features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/YieldSupplyActiveModel.kt index fc9af56dc8..8dbc13d8c2 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/YieldSupplyActiveModel.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/YieldSupplyActiveModel.kt @@ -1,10 +1,15 @@ -package com.tangem.features.yield.supply.impl.subcomponents.active.model +package com.tangem.features.yield.supply.impl.active.model import arrow.core.getOrElse +import com.arkivanov.decompose.router.slot.SlotNavigation +import com.arkivanov.decompose.router.slot.activate +import com.arkivanov.decompose.router.slot.dismiss +import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.core.navigation.url.UrlOpener import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference @@ -14,18 +19,21 @@ import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.yield.supply.usecase.YieldSupplyGetProtocolBalanceUseCase -import com.tangem.domain.yield.supply.usecase.YieldSupplyGetTokenStatusUseCase -import com.tangem.domain.yield.supply.usecase.YieldSupplyMinAmountUseCase -import com.tangem.domain.yield.supply.usecase.YieldSupplyGetCurrentFeeUseCase -import com.tangem.domain.yield.supply.usecase.YieldSupplyGetMaxFeeUseCase +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase +import com.tangem.domain.wallets.usecase.GetUserWalletUseCase +import com.tangem.domain.yield.supply.usecase.* +import com.tangem.features.yield.supply.api.YieldSupplyActiveComponent import com.tangem.features.yield.supply.api.analytics.YieldSupplyAnalytics import com.tangem.features.yield.supply.impl.R -import com.tangem.features.yield.supply.impl.subcomponents.active.YieldSupplyActiveComponent -import com.tangem.features.yield.supply.impl.subcomponents.active.entity.YieldSupplyActiveContentUM -import com.tangem.features.yield.supply.impl.subcomponents.active.model.transformers.YieldSupplyActiveMinAmountTransformer -import com.tangem.features.yield.supply.impl.subcomponents.active.model.transformers.YieldSupplyActiveFeeContentTransformer +import com.tangem.features.yield.supply.impl.active.entity.YieldSupplyActiveContentUM +import com.tangem.features.yield.supply.impl.active.model.transformers.YieldSupplyActiveFeeContentTransformer +import com.tangem.features.yield.supply.impl.active.model.transformers.YieldSupplyActiveMinAmountTransformer +import com.tangem.features.yield.supply.impl.subcomponents.approve.YieldSupplyApproveComponent +import com.tangem.features.yield.supply.impl.subcomponents.stopearning.YieldSupplyStopEarningComponent import com.tangem.utils.StringsSigns.DASH_SIGN +import com.tangem.utils.TangemBlogUrlBuilder import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.transformer.update import kotlinx.collections.immutable.persistentListOf @@ -34,7 +42,7 @@ import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject -@Suppress("LongParameterList") +@Suppress("LongParameterList", "LargeClass") @ModelScoped internal class YieldSupplyActiveModel @Inject constructor( paramsContainer: ParamsContainer, @@ -46,11 +54,31 @@ internal class YieldSupplyActiveModel @Inject constructor( private val yieldSupplyGetCurrentFeeUseCase: YieldSupplyGetCurrentFeeUseCase, private val yieldSupplyGetMaxFeeUseCase: YieldSupplyGetMaxFeeUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, -) : Model() { + private val getUserWalletUseCase: GetUserWalletUseCase, + private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase, + private val urlOpener: UrlOpener, + private val appRouter: AppRouter, +) : Model(), YieldSupplyStopEarningComponent.ModelCallback, + YieldSupplyApproveComponent.ModelCallback { private val params: YieldSupplyActiveComponent.Params = paramsContainer.require() - private val cryptoCurrencyStatusFlow = params.cryptoCurrencyStatusFlow + val slotNavigation = SlotNavigation() + + lateinit var userWallet: UserWallet + + val cryptoCurrencyStatusFlow = MutableStateFlow( + CryptoCurrencyStatus( + value = CryptoCurrencyStatus.Loading, + currency = params.cryptoCurrency, + ), + ) + + val balanceHiddenFlow = MutableStateFlow(false) + val transactionInProgressFlow: StateFlow + field = MutableStateFlow(false) + + private val userWalletId = params.userWalletId private val cryptoCurrency = cryptoCurrencyStatusFlow.value.currency private var appCurrency = AppCurrency.Default @@ -81,12 +109,12 @@ internal class YieldSupplyActiveModel @Inject constructor( blockchain = cryptoCurrency.network.name, ), ) - subscribeOnCurrencyUpdates() + subscribeOnCurrencyStatusUpdates() modelScope.launch(dispatchers.default) { appCurrency = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default } val protocolBalance = yieldSupplyGetProtocolBalanceUseCase( - userWalletId = params.userWallet.walletId, + userWalletId = userWalletId, cryptoCurrency = cryptoCurrency, ).getOrNull() @@ -101,32 +129,90 @@ internal class YieldSupplyActiveModel @Inject constructor( } } - private fun subscribeOnCurrencyUpdates() { - cryptoCurrencyStatusFlow.onEach { cryptoCurrencyStatus -> - val protocolBalance = cryptoCurrencyStatus.value.yieldSupplyStatus?.effectiveProtocolBalance - ?: yieldSupplyGetProtocolBalanceUseCase( - userWalletId = params.userWallet.walletId, - cryptoCurrency = cryptoCurrency, - ).getOrNull() + override fun onDismissClick() { + if (!transactionInProgressFlow.value) { + slotNavigation.dismiss() + } + } - loadApy() - loadMinAmount() - loadFees() + override fun onTransactionProgress(inProgress: Boolean) { + transactionInProgressFlow.update { inProgress } + } - uiState.update { - it.copy( - availableBalance = stringReference( - protocolBalance.format { - crypto( - symbol = AAVEV3_PREFIX + cryptoCurrency.symbol, - decimals = cryptoCurrency.decimals, - ) - }, - ), - ) - } - }.flowOn(dispatchers.default) - .launchIn(modelScope) + override fun onStopEarningTransactionSent() { + transactionInProgressFlow.update { false } + appRouter.pop() + } + + override fun onTransactionSent() { + transactionInProgressFlow.update { false } + appRouter.pop() + } + + fun onApprove() { + slotNavigation.activate(YieldSupplyActiveRoute.Approve) + } + + fun onStopEarning() { + slotNavigation.activate(YieldSupplyActiveRoute.Exit) + } + + fun onReadMoreClick() { + urlOpener.openUrl(TangemBlogUrlBuilder.YIELD_SUPPLY_HOW_IT_WORKS_URL) + } + + private fun subscribeOnCurrencyStatusUpdates() { + modelScope.launch { + getUserWalletUseCase(params.userWalletId).fold( + ifRight = { wallet -> + userWallet = wallet + + getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet( + userWalletId = params.userWalletId, + currencyId = cryptoCurrency.id, + isSingleWalletWithTokens = false, + ).onEach { maybeCryptoCurrency -> + maybeCryptoCurrency.fold( + ifRight = { cryptoCurrencyStatus -> + cryptoCurrencyStatusFlow.update { cryptoCurrencyStatus } + + val protocolBalance = + cryptoCurrencyStatus.value.yieldSupplyStatus?.effectiveProtocolBalance + ?: yieldSupplyGetProtocolBalanceUseCase( + userWalletId = userWalletId, + cryptoCurrency = cryptoCurrency, + ).getOrNull() + + loadApy() + loadMinAmount() + loadFees() + + uiState.update { + it.copy( + availableBalance = stringReference( + protocolBalance.format { + crypto( + symbol = AAVEV3_PREFIX + cryptoCurrency.symbol, + decimals = cryptoCurrency.decimals, + ) + }, + ), + ) + } + }, + ifLeft = { + Timber.w(it.toString()) + }, + ) + }.flowOn(dispatchers.default) + .launchIn(modelScope) + }, + ifLeft = { error -> + Timber.w(error.toString()) + return@launch + }, + ) + } } private fun loadApy() { @@ -152,7 +238,7 @@ internal class YieldSupplyActiveModel @Inject constructor( private fun loadMinAmount() { modelScope.launch(dispatchers.default) { yieldSupplyMinAmountUseCase( - params.userWallet, + userWalletId, cryptoCurrencyStatusFlow.value, ).onRight { minAmount -> uiState.update( @@ -161,7 +247,7 @@ internal class YieldSupplyActiveModel @Inject constructor( appCurrency = appCurrency, minAmount = minAmount, analyticsHandler = analyticsHandler, - onApprove = params.callback::onApprove, + onApprove = ::onApprove, ), ) }.onLeft { @@ -180,12 +266,12 @@ internal class YieldSupplyActiveModel @Inject constructor( val cryptoStatus = cryptoCurrencyStatusFlow.value val currentFee = yieldSupplyGetCurrentFeeUseCase( - userWallet = params.userWallet, + userWalletId = userWalletId, cryptoCurrencyStatus = cryptoStatus, ).getOrNull() val maxFee = yieldSupplyGetMaxFeeUseCase( - userWallet = params.userWallet, + userWalletId = userWalletId, cryptoCurrencyStatus = cryptoStatus, ).getOrNull() diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/YieldSupplyActiveRoute.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/YieldSupplyActiveRoute.kt similarity index 63% rename from features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/YieldSupplyActiveRoute.kt rename to features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/YieldSupplyActiveRoute.kt index 800a76040e..5146e12859 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/YieldSupplyActiveRoute.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/YieldSupplyActiveRoute.kt @@ -1,9 +1,8 @@ -package com.tangem.features.yield.supply.impl.subcomponents.active.model +package com.tangem.features.yield.supply.impl.active.model import com.tangem.core.decompose.navigation.Route internal sealed class YieldSupplyActiveRoute : Route { - data object Info : YieldSupplyActiveRoute() data object Exit : YieldSupplyActiveRoute() data object Approve : YieldSupplyActiveRoute() } \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/transformers/YieldSupplyActiveFeeContentTransformer.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/transformers/YieldSupplyActiveFeeContentTransformer.kt similarity index 94% rename from features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/transformers/YieldSupplyActiveFeeContentTransformer.kt rename to features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/transformers/YieldSupplyActiveFeeContentTransformer.kt index 44e98e521f..d034526cd4 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/transformers/YieldSupplyActiveFeeContentTransformer.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/transformers/YieldSupplyActiveFeeContentTransformer.kt @@ -1,4 +1,4 @@ -package com.tangem.features.yield.supply.impl.subcomponents.active.model.transformers +package com.tangem.features.yield.supply.impl.active.model.transformers import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.ui.extensions.resourceReference @@ -13,7 +13,7 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.yield.supply.models.YieldSupplyMaxFee import com.tangem.features.yield.supply.api.analytics.YieldSupplyAnalytics import com.tangem.features.yield.supply.impl.R -import com.tangem.features.yield.supply.impl.subcomponents.active.entity.YieldSupplyActiveContentUM +import com.tangem.features.yield.supply.impl.active.entity.YieldSupplyActiveContentUM import com.tangem.utils.transformer.Transformer import java.math.BigDecimal diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/transformers/YieldSupplyActiveMinAmountTransformer.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/transformers/YieldSupplyActiveMinAmountTransformer.kt similarity index 96% rename from features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/transformers/YieldSupplyActiveMinAmountTransformer.kt rename to features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/transformers/YieldSupplyActiveMinAmountTransformer.kt index 1bea08e058..c3b1fd4763 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/transformers/YieldSupplyActiveMinAmountTransformer.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/model/transformers/YieldSupplyActiveMinAmountTransformer.kt @@ -1,4 +1,4 @@ -package com.tangem.features.yield.supply.impl.subcomponents.active.model.transformers +package com.tangem.features.yield.supply.impl.active.model.transformers import com.tangem.common.ui.notifications.NotificationUM import com.tangem.core.analytics.api.AnalyticsEventHandler @@ -15,7 +15,7 @@ import com.tangem.domain.models.currency.notSuppliedAmountOrNull import com.tangem.domain.models.currency.shouldShowNotSuppliedInfoIcon import com.tangem.features.yield.supply.api.analytics.YieldSupplyAnalytics import com.tangem.features.yield.supply.impl.R -import com.tangem.features.yield.supply.impl.subcomponents.active.entity.YieldSupplyActiveContentUM +import com.tangem.features.yield.supply.impl.active.entity.YieldSupplyActiveContentUM import com.tangem.utils.transformer.Transformer import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toPersistentList diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/ui/YieldSupplyActiveContent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/ui/YieldSupplyActiveContent.kt similarity index 94% rename from features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/ui/YieldSupplyActiveContent.kt rename to features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/ui/YieldSupplyActiveContent.kt index 70282dca3f..e01e0e702c 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/ui/YieldSupplyActiveContent.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/ui/YieldSupplyActiveContent.kt @@ -1,4 +1,4 @@ -package com.tangem.features.yield.supply.impl.subcomponents.active.ui +package com.tangem.features.yield.supply.impl.active.ui import android.content.res.Configuration import androidx.compose.animation.AnimatedContent @@ -6,7 +6,9 @@ import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.Text @@ -26,6 +28,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import com.tangem.common.ui.notifications.NotificationUM +import com.tangem.core.ui.components.SpacerH import com.tangem.core.ui.components.SpacerH4 import com.tangem.core.ui.components.SpacerH8 import com.tangem.core.ui.components.SpacerWMax @@ -37,7 +40,7 @@ import com.tangem.core.ui.extensions.* import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.features.yield.supply.impl.R -import com.tangem.features.yield.supply.impl.subcomponents.active.entity.YieldSupplyActiveContentUM +import com.tangem.features.yield.supply.impl.active.entity.YieldSupplyActiveContentUM import kotlinx.collections.immutable.persistentListOf @Composable @@ -50,10 +53,12 @@ internal fun YieldSupplyActiveContent( ) { Column( verticalArrangement = Arrangement.spacedBy(14.dp), - modifier = modifier.padding( - vertical = 8.dp, - horizontal = 16.dp, - ), + modifier = modifier + .verticalScroll(rememberScrollState()) + .padding( + vertical = 8.dp, + horizontal = 16.dp, + ), ) { Column( verticalArrangement = Arrangement.spacedBy(4.dp), @@ -90,6 +95,10 @@ internal fun YieldSupplyActiveContent( onReadMoreClick = onReadMoreClick, ) + YieldSupplyActiveTopUp( + state = state, + ) + AnimatedVisibility(state.feeDescription != null) { Text( modifier = Modifier.padding(horizontal = 12.dp), @@ -107,6 +116,8 @@ internal fun YieldSupplyActiveContent( color = TangemTheme.colors.text.tertiary, ) } + + SpacerH(16.dp) } } @@ -198,20 +209,28 @@ private fun YieldSupplyActiveMyFunds( thickness = 0.5.dp, color = TangemTheme.colors.stroke.primary, ) - InfoRow( - title = resourceReference(R.string.yield_module_earn_sheet_transfers_title), - info = resourceReference(R.string.yield_module_transfer_mode_automatic), - isBalanceHidden = false, - ) - HorizontalDivider( - thickness = 0.5.dp, - color = TangemTheme.colors.stroke.primary, - ) InfoRow( title = resourceReference(R.string.yield_module_earn_sheet_available_title), info = state.availableBalance, isBalanceHidden = isBalanceHidden, ) + } +} + +@Composable +private fun YieldSupplyActiveTopUp(state: YieldSupplyActiveContentUM) { + Column( + modifier = Modifier + .clip(RoundedCornerShape(16.dp)) + .background(TangemTheme.colors.background.action) + .fillMaxWidth() + .padding(horizontal = 12.dp), + ) { + InfoRow( + title = resourceReference(R.string.yield_module_earn_sheet_transfers_title), + info = resourceReference(R.string.yield_module_transfer_mode_automatic), + isBalanceHidden = false, + ) HorizontalDivider( thickness = 0.5.dp, color = TangemTheme.colors.stroke.primary, diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/ui/YieldSupplyActiveTitle.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/ui/YieldSupplyActiveTitle.kt similarity index 62% rename from features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/ui/YieldSupplyActiveTitle.kt rename to features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/ui/YieldSupplyActiveTitle.kt index f965c213f4..df0f79c6cb 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/ui/YieldSupplyActiveTitle.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/active/ui/YieldSupplyActiveTitle.kt @@ -1,24 +1,40 @@ -package com.tangem.features.yield.supply.impl.subcomponents.active.ui +package com.tangem.features.yield.supply.impl.active.ui import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Icon import androidx.compose.material3.Text +import androidx.compose.material3.ripple import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource import androidx.compose.ui.unit.dp -import com.tangem.core.ui.components.buttons.small.TangemIconButton import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme import com.tangem.features.yield.supply.impl.R @Composable internal fun YieldSupplyActiveTitle(onCloseClick: () -> Unit) { - Box(modifier = Modifier.fillMaxWidth()) { + Row(modifier = Modifier.fillMaxWidth()) { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.ic_close_24), + contentDescription = null, + modifier = Modifier + .padding(16.dp) + .clickable( + indication = ripple(false), + interactionSource = remember { MutableInteractionSource() }, + onClick = onCloseClick, + ), + ) Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.align(Alignment.Center), + modifier = Modifier.padding(vertical = 8.dp), ) { Text( text = stringResourceSafe(R.string.yield_module_earn_sheet_title), @@ -30,25 +46,17 @@ internal fun YieldSupplyActiveTitle(onCloseClick: () -> Unit) { horizontalArrangement = Arrangement.spacedBy(4.dp), verticalAlignment = Alignment.CenterVertically, ) { + Text( + text = stringResourceSafe(R.string.yield_module_status_active), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + ) Box( modifier = Modifier .size(8.dp) .background(TangemTheme.colors.icon.accent, CircleShape), ) - Text( - text = stringResourceSafe(R.string.yield_module_status_active), - style = TangemTheme.typography.caption1, - color = TangemTheme.colors.text.tertiary, - modifier = Modifier, - ) } } - TangemIconButton( - iconRes = R.drawable.ic_close_24, - onClick = onCloseClick, - modifier = Modifier - .padding(16.dp) - .align(Alignment.CenterEnd), - ) } } \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/DefaultYieldSupplyComponent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/DefaultYieldSupplyComponent.kt index abb21ee496..2473062b59 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/DefaultYieldSupplyComponent.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/DefaultYieldSupplyComponent.kt @@ -4,18 +4,11 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle -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.features.yield.supply.api.YieldSupplyComponent import com.tangem.features.yield.supply.impl.main.model.YieldSupplyModel import com.tangem.features.yield.supply.impl.main.ui.YieldSupplyBlockContent -import com.tangem.features.yield.supply.impl.subcomponents.active.YieldSupplyActiveEntryComponent import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -27,33 +20,11 @@ internal class DefaultYieldSupplyComponent @AssistedInject constructor( private val model: YieldSupplyModel = getOrCreateModel(params = params) - private val bottomSheetSlot = childSlot( - source = model.bottomSheetNavigation, - serializer = null, - handleBackButton = false, - childFactory = { _, context -> bottomSheetChild(context) }, - ) - @Composable override fun Content(modifier: Modifier) { val yieldSupplyUM by model.uiState.collectAsStateWithLifecycle() - val bottomSheet by bottomSheetSlot.subscribeAsState() YieldSupplyBlockContent(yieldSupplyUM = yieldSupplyUM, modifier = modifier) - - bottomSheet.child?.instance?.BottomSheet() - } - - private fun bottomSheetChild(componentContext: ComponentContext): ComposableBottomSheetComponent { - return YieldSupplyActiveEntryComponent( - appComponentContext = childByContext(componentContext), - params = YieldSupplyActiveEntryComponent.Params( - userWallet = model.userWallet, - cryptoCurrencyStatusFlow = model.cryptoCurrencyStatusFlow, - isBalanceHiddenFlow = model.isBalanceHiddenFlow, - onDismiss = model.bottomSheetNavigation::dismiss, - ), - ) } @AssistedFactory diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/model/YieldSupplyModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/model/YieldSupplyModel.kt index cb5c7464d9..650ba7d090 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/model/YieldSupplyModel.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/model/YieldSupplyModel.kt @@ -1,8 +1,7 @@ package com.tangem.features.yield.supply.impl.main.model +import com.tangem.common.routing.AppRoute import android.os.SystemClock -import com.arkivanov.decompose.router.slot.SlotNavigation -import com.arkivanov.decompose.router.slot.activate import com.tangem.common.routing.AppRoute.YieldSupplyPromo import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.api.AnalyticsEventHandler @@ -13,7 +12,6 @@ import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.combinedReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference -import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.models.StatusSource import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus @@ -60,7 +58,6 @@ internal class YieldSupplyModel @Inject constructor( private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase, private val singleNetworkStatusFetcher: SingleNetworkStatusFetcher, @DelayedWork private val coroutineScope: CoroutineScope, - private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, private val yieldSupplyGetTokenStatusUseCase: YieldSupplyGetTokenStatusUseCase, private val yieldSupplyIsAvailableUseCase: YieldSupplyIsAvailableUseCase, private val yieldSupplyActivateUseCase: YieldSupplyActivateUseCase, @@ -74,23 +71,9 @@ internal class YieldSupplyModel @Inject constructor( val uiState: StateFlow field = MutableStateFlow(YieldSupplyUM.Initial) - val bottomSheetNavigation: SlotNavigation = SlotNavigation() - - private val handleNavigation = params.handleNavigation private val cryptoCurrency = params.cryptoCurrency var userWallet: UserWallet by Delegates.notNull() - val cryptoCurrencyStatusFlow: StateFlow - field = MutableStateFlow( - CryptoCurrencyStatus( - currency = params.cryptoCurrency, - value = CryptoCurrencyStatus.Loading, - ), - ) - - val isBalanceHiddenFlow: StateFlow - field = MutableStateFlow(false) - private var lastYieldSupplyStatus: YieldSupplyStatus? = null private val fetchCurrencyJobHolder = JobHolder() @@ -98,32 +81,6 @@ internal class YieldSupplyModel @Inject constructor( init { checkIfYieldSupplyIsAvailable() - - val protocolStatus = yieldSupplyRepository.getTokenProtocolStatus( - userWalletId = params.userWalletId, - cryptoCurrency = cryptoCurrency, - ) - if (handleNavigation != null && protocolStatus == null) { - if (handleNavigation) { - modelScope.launch { - delay(timeMillis = 1000) - bottomSheetNavigation.activate(Unit) - } - } else { - uiState - .filterIsInstance() - .take(1) - .onEach { state -> - appRouter.push( - YieldSupplyPromo( - userWalletId = params.userWalletId, - cryptoCurrency = params.cryptoCurrency, - apy = state.apy, - ), - ) - }.launchIn(modelScope) - } - } } private fun checkIfYieldSupplyIsAvailable() { @@ -131,7 +88,6 @@ internal class YieldSupplyModel @Inject constructor( val isAvailable = yieldSupplyIsAvailableUseCase(params.userWalletId, params.cryptoCurrency) if (isAvailable) { subscribeOnCurrencyStatusUpdates() - subscribeOnBalanceHidden() } } } @@ -149,7 +105,6 @@ internal class YieldSupplyModel @Inject constructor( ).onEach { maybeCryptoCurrency -> maybeCryptoCurrency.fold( ifRight = { cryptoCurrencyStatus -> - cryptoCurrencyStatusFlow.update { cryptoCurrencyStatus } onCryptoCurrencyStatusUpdated(cryptoCurrencyStatus) }, ifLeft = { @@ -185,8 +140,7 @@ internal class YieldSupplyModel @Inject constructor( } override fun onStartEarningClick() { - val yieldSupplyUM = uiState.value - val apy = when (yieldSupplyUM) { + val apy = when (val yieldSupplyUM = uiState.value) { is YieldSupplyUM.Available -> yieldSupplyUM.apy is YieldSupplyUM.Content -> yieldSupplyUM.apy else -> "" @@ -201,18 +155,18 @@ internal class YieldSupplyModel @Inject constructor( } override fun onActiveClick() { - bottomSheetNavigation.activate(Unit) - } - - private fun subscribeOnBalanceHidden() { - getBalanceHidingSettingsUseCase() - .conflate() - .distinctUntilChanged() - .onEach { - isBalanceHiddenFlow.value = it.isBalanceHidden - } - .flowOn(dispatchers.default) - .launchIn(modelScope) + val apy = when (val yieldSupplyUM = uiState.value) { + is YieldSupplyUM.Available -> yieldSupplyUM.apy + is YieldSupplyUM.Content -> yieldSupplyUM.apy + else -> "" + } + appRouter.push( + AppRoute.YieldSupplyActive( + userWalletId = params.userWalletId, + cryptoCurrency = params.cryptoCurrency, + apy = apy, + ), + ) } @Suppress("MaximumLineLength") @@ -379,7 +333,8 @@ internal class YieldSupplyModel @Inject constructor( private fun computeAndApplyShowInfoIcon(cryptoCurrencyStatus: CryptoCurrencyStatus) { modelScope.launch(dispatchers.default) { val isShowInfoIcon = if (cryptoCurrencyStatus.hasNotSuppliedAmount()) { - val minAmount = yieldSupplyMinAmountUseCase(userWallet, cryptoCurrencyStatus).getOrNull() + val minAmount = yieldSupplyMinAmountUseCase(userWalletId = userWallet.walletId, cryptoCurrencyStatus) + .getOrNull() if (minAmount != null) { cryptoCurrencyStatus.shouldShowNotSuppliedInfoIcon(minAmount) } else { diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/YieldSupplyActiveComponent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/YieldSupplyActiveComponent.kt deleted file mode 100644 index 18d339bb5c..0000000000 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/YieldSupplyActiveComponent.kt +++ /dev/null @@ -1,82 +0,0 @@ -package com.tangem.features.yield.supply.impl.subcomponents.active - -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.tangem.core.decompose.context.AppComponentContext -import com.tangem.core.decompose.context.child -import com.tangem.core.decompose.model.getOrCreateModel -import com.tangem.core.ui.components.SecondaryButton -import com.tangem.core.ui.decompose.ComposableModularContentComponent -import com.tangem.core.ui.extensions.stringResourceSafe -import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.models.currency.CryptoCurrencyStatus -import com.tangem.domain.models.wallet.UserWallet -import com.tangem.features.yield.supply.impl.subcomponents.active.model.YieldSupplyActiveModel -import com.tangem.features.yield.supply.impl.subcomponents.active.ui.YieldSupplyActiveContent -import com.tangem.features.yield.supply.impl.subcomponents.active.ui.YieldSupplyActiveTitle -import com.tangem.features.yield.supply.impl.R -import com.tangem.features.yield.supply.impl.chart.DefaultYieldSupplyChartComponent -import kotlinx.coroutines.flow.StateFlow - -internal class YieldSupplyActiveComponent( - appComponentContext: AppComponentContext, - private val params: Params, -) : ComposableModularContentComponent, AppComponentContext by appComponentContext { - - private val model: YieldSupplyActiveModel = getOrCreateModel(params = params) - private val chartComponent = DefaultYieldSupplyChartComponent( - appComponentContext = child("chartComponent"), - params = DefaultYieldSupplyChartComponent.Params( - cryptoCurrency = params.cryptoCurrencyStatusFlow.value.currency as CryptoCurrency.Token, - ), - ) - - @Composable - override fun Title() { - YieldSupplyActiveTitle(onCloseClick = params.callback::onBackClick) - } - - @Composable - override fun Content(modifier: Modifier) { - val state by model.uiState.collectAsStateWithLifecycle() - val isBalanceHidden by params.isBalanceHiddenFlow.collectAsStateWithLifecycle() - - YieldSupplyActiveContent( - state = state, - isBalanceHidden = isBalanceHidden, - chartComponent = chartComponent, - onReadMoreClick = params.callback::onReadMoreClick, - modifier = Modifier, - ) - } - - @Composable - override fun Footer() { - SecondaryButton( - text = stringResourceSafe(R.string.yield_module_disable_button), - onClick = params.callback::onStopEarning, - modifier = Modifier - .fillMaxWidth() - .padding(16.dp), - ) - } - - data class Params( - val userWallet: UserWallet, - val cryptoCurrencyStatusFlow: StateFlow, - val isBalanceHiddenFlow: StateFlow, - val callback: ModelCallback, - ) - - interface ModelCallback { - fun onBackClick() - fun onStopEarning() - fun onApprove() - fun onReadMoreClick() - } -} \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/YieldSupplyActiveEntryComponent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/YieldSupplyActiveEntryComponent.kt deleted file mode 100644 index 89dcded7bf..0000000000 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/YieldSupplyActiveEntryComponent.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.tangem.features.yield.supply.impl.subcomponents.active - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.arkivanov.decompose.extensions.compose.subscribeAsState -import com.arkivanov.decompose.router.stack.StackNavigation -import com.arkivanov.decompose.router.stack.childStack -import com.arkivanov.decompose.router.stack.pop -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.decompose.navigation.inner.InnerRouter -import com.tangem.core.ui.decompose.ComposableBottomSheetComponent -import com.tangem.core.ui.decompose.ComposableModularContentComponent -import com.tangem.domain.models.currency.CryptoCurrencyStatus -import com.tangem.domain.models.wallet.UserWallet -import com.tangem.features.yield.supply.impl.subcomponents.active.model.YieldSupplyActiveEntryModel -import com.tangem.features.yield.supply.impl.subcomponents.active.model.YieldSupplyActiveRoute -import com.tangem.features.yield.supply.impl.subcomponents.active.ui.YieldSupplyActiveEntryBottomSheet -import com.tangem.features.yield.supply.impl.subcomponents.approve.YieldSupplyApproveComponent -import com.tangem.features.yield.supply.impl.subcomponents.stopearning.YieldSupplyStopEarningComponent -import kotlinx.coroutines.flow.StateFlow - -internal class YieldSupplyActiveEntryComponent( - private val appComponentContext: AppComponentContext, - private val params: Params, -) : ComposableBottomSheetComponent, AppComponentContext by appComponentContext { - - private val stackNavigation = StackNavigation() - - private val innerRouter = InnerRouter( - stackNavigation = stackNavigation, - popCallback = { onChildBack() }, - ) - - private val model: YieldSupplyActiveEntryModel = getOrCreateModel(params = params, router = innerRouter) - - private val innerStack = childStack( - key = "yieldSupplyActiveStack", - source = stackNavigation, - serializer = null, - initialConfiguration = YieldSupplyActiveRoute.Info, - handleBackButton = true, - childFactory = { configuration, factoryContext -> - createChild( - configuration, - childByContext( - componentContext = factoryContext, - router = innerRouter, - ), - ) - }, - ) - - override fun dismiss() { - params.onDismiss() - } - - @Composable - override fun BottomSheet() { - val stackState by innerStack.subscribeAsState() - val isTransactionInProgress by model.isTransactionInProgressFlow.collectAsStateWithLifecycle() - YieldSupplyActiveEntryBottomSheet( - stackState = stackState, - dismissOnClickOutside = { !isTransactionInProgress }, - onDismiss = ::dismiss, - ) - } - - private fun createChild( - route: YieldSupplyActiveRoute, - factoryContext: AppComponentContext, - ): ComposableModularContentComponent = when (route) { - YieldSupplyActiveRoute.Info -> YieldSupplyActiveComponent( - appComponentContext = factoryContext, - params = YieldSupplyActiveComponent.Params( - userWallet = params.userWallet, - cryptoCurrencyStatusFlow = params.cryptoCurrencyStatusFlow, - isBalanceHiddenFlow = params.isBalanceHiddenFlow, - callback = model, - ), - ) - YieldSupplyActiveRoute.Exit -> YieldSupplyStopEarningComponent( - appComponentContext = factoryContext, - params = YieldSupplyStopEarningComponent.Params( - userWallet = params.userWallet, - cryptoCurrencyStatusFlow = params.cryptoCurrencyStatusFlow, - callback = model, - ), - ) - YieldSupplyActiveRoute.Approve -> YieldSupplyApproveComponent( - appComponentContext = factoryContext, - params = YieldSupplyApproveComponent.Params( - userWallet = params.userWallet, - cryptoCurrencyStatusFlow = params.cryptoCurrencyStatusFlow, - callback = model, - ), - ) - } - - private fun onChildBack() { - if (innerStack.value.backStack.isEmpty()) { - dismiss() - } else { - stackNavigation.pop() - } - } - - data class Params( - val userWallet: UserWallet, - val cryptoCurrencyStatusFlow: StateFlow, - val isBalanceHiddenFlow: StateFlow, - val onDismiss: () -> Unit, - ) -} \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/di/YieldSupplyActiveModule.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/di/YieldSupplyActiveModule.kt deleted file mode 100644 index 4e062d0d10..0000000000 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/di/YieldSupplyActiveModule.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.tangem.features.yield.supply.impl.subcomponents.active.di - -import com.tangem.core.decompose.di.ModelComponent -import com.tangem.core.decompose.model.Model -import com.tangem.features.yield.supply.impl.subcomponents.active.model.YieldSupplyActiveEntryModel -import com.tangem.features.yield.supply.impl.subcomponents.active.model.YieldSupplyActiveModel -import dagger.Binds -import dagger.Module -import dagger.hilt.InstallIn -import dagger.multibindings.ClassKey -import dagger.multibindings.IntoMap - -@Module -@InstallIn(ModelComponent::class) -internal interface YieldSupplyActiveModule { - - @Binds - @IntoMap - @ClassKey(YieldSupplyActiveModel::class) - fun provideYieldSupplyActiveModel(impl: YieldSupplyActiveModel): Model - - @Binds - @IntoMap - @ClassKey(YieldSupplyActiveEntryModel::class) - fun provideYieldSupplyActiveEntryModel(impl: YieldSupplyActiveEntryModel): Model -} \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/YieldSupplyActiveEntryModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/YieldSupplyActiveEntryModel.kt deleted file mode 100644 index 9f7ca52491..0000000000 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/model/YieldSupplyActiveEntryModel.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.tangem.features.yield.supply.impl.subcomponents.active.model - -import com.tangem.core.decompose.di.ModelScoped -import com.tangem.core.decompose.model.Model -import com.tangem.core.decompose.model.ParamsContainer -import com.tangem.core.decompose.navigation.Router -import com.tangem.core.navigation.url.UrlOpener -import com.tangem.features.yield.supply.impl.subcomponents.active.YieldSupplyActiveComponent -import com.tangem.features.yield.supply.impl.subcomponents.active.YieldSupplyActiveEntryComponent -import com.tangem.features.yield.supply.impl.subcomponents.approve.YieldSupplyApproveComponent -import com.tangem.features.yield.supply.impl.subcomponents.stopearning.YieldSupplyStopEarningComponent -import com.tangem.utils.TangemBlogUrlBuilder -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.update -import javax.inject.Inject - -@ModelScoped -internal class YieldSupplyActiveEntryModel @Inject constructor( - override val dispatchers: CoroutineDispatcherProvider, - paramsContainer: ParamsContainer, - private val router: Router, - private val urlOpener: UrlOpener, -) : Model(), YieldSupplyActiveComponent.ModelCallback, - YieldSupplyStopEarningComponent.ModelCallback, - YieldSupplyApproveComponent.ModelCallback { - - private val params = paramsContainer.require() - - val isTransactionInProgressFlow: StateFlow - field = MutableStateFlow(false) - - override fun onStopEarning() { - router.push(YieldSupplyActiveRoute.Exit) - } - - override fun onBackClick() { - if (!isTransactionInProgressFlow.value) { - router.pop() - } - } - - override fun onTransactionProgress(inProgress: Boolean) { - isTransactionInProgressFlow.update { inProgress } - } - - override fun onTransactionSent() { - isTransactionInProgressFlow.update { false } - params.onDismiss() - } - - override fun onApprove() { - router.push(YieldSupplyActiveRoute.Approve) - } - - override fun onReadMoreClick() { - urlOpener.openUrl(TangemBlogUrlBuilder.YIELD_SUPPLY_HOW_IT_WORKS_URL) - } -} \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/ui/YieldSupplyActiveEntryBottomSheet.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/ui/YieldSupplyActiveEntryBottomSheet.kt deleted file mode 100644 index e4f47efa5e..0000000000 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/ui/YieldSupplyActiveEntryBottomSheet.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.tangem.features.yield.supply.impl.subcomponents.active.ui - -import androidx.compose.animation.AnimatedContent -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import com.arkivanov.decompose.router.stack.ChildStack -import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig -import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent -import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetWithFooter -import com.tangem.core.ui.decompose.ComposableModularContentComponent -import com.tangem.core.ui.res.TangemTheme -import com.tangem.features.yield.supply.impl.subcomponents.active.model.YieldSupplyActiveRoute - -@Composable -internal fun YieldSupplyActiveEntryBottomSheet( - stackState: ChildStack, - dismissOnClickOutside: () -> Boolean, - onDismiss: () -> Unit, -) { - TangemModalBottomSheetWithFooter( - config = TangemBottomSheetConfig( - isShown = true, - onDismissRequest = onDismiss, - dismissOnClickOutside = dismissOnClickOutside, - content = TangemBottomSheetConfigContent.Empty, - ), - containerColor = TangemTheme.colors.background.tertiary, - title = { state -> - AnimatedContent( - stackState.active.instance, - ) { currentState -> - currentState.Title() - } - }, - footer = { state -> - AnimatedContent( - stackState.active.instance, - ) { currentState -> - currentState.Footer() - } - }, - content = { state -> - AnimatedContent( - stackState.active.instance, - ) { currentState -> - currentState.Content(modifier = Modifier) - } - }, - ) -} \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/YieldSupplyApproveComponent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/YieldSupplyApproveComponent.kt index c712301a01..9e641d0513 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/YieldSupplyApproveComponent.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/YieldSupplyApproveComponent.kt @@ -19,8 +19,11 @@ import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.child import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.components.PrimaryButtonIconEnd +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle -import com.tangem.core.ui.decompose.ComposableModularContentComponent +import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetWithFooter +import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme import com.tangem.domain.models.currency.CryptoCurrencyStatus @@ -35,7 +38,7 @@ import kotlinx.coroutines.flow.StateFlow internal class YieldSupplyApproveComponent( private val appComponentContext: AppComponentContext, private val params: Params, -) : ComposableModularContentComponent, AppComponentContext by appComponentContext { +) : ComposableBottomSheetComponent, AppComponentContext by appComponentContext { private val model: YieldSupplyApproveModel = getOrCreateModel(params = params) @@ -49,52 +52,60 @@ internal class YieldSupplyApproveComponent( ), ) - @Composable - override fun Title() { - TangemModalBottomSheetTitle( - startIconRes = R.drawable.ic_back_24, - onStartClick = params.callback::onBackClick, - ) + override fun dismiss() { + params.callback.onDismissClick() } @Composable - override fun Content(modifier: Modifier) { + override fun BottomSheet() { val state by model.uiState.collectAsStateWithLifecycle() - YieldSupplyActionContent( - yieldSupplyActionUM = state, - onFooterClick = model::onReadMoreClick, - yieldSupplyNotificationsComponent = yieldSupplyNotificationsComponent, - modifier = modifier, - ) { - Box( - modifier = Modifier - .size(56.dp) - .background(TangemTheme.colors.icon.accent.copy(0.1f), CircleShape), - ) { - Icon( - imageVector = ImageVector.vectorResource(R.drawable.ic_check_circle_24), - contentDescription = null, - tint = TangemTheme.colors.icon.accent, - modifier = Modifier - .padding(12.dp) - .size(32.dp), + + TangemModalBottomSheetWithFooter( + config = TangemBottomSheetConfig( + isShown = true, + onDismissRequest = params.callback::onDismissClick, + content = TangemBottomSheetConfigContent.Empty, + ), + containerColor = TangemTheme.colors.background.tertiary, + title = { + TangemModalBottomSheetTitle( + endIconRes = R.drawable.ic_close_24, + onEndClick = params.callback::onDismissClick, ) - } - } - } - - @Composable - override fun Footer() { - val state by model.uiState.collectAsStateWithLifecycle() - - PrimaryButtonIconEnd( - text = stringResourceSafe(R.string.common_confirm), - onClick = model::onClick, - iconResId = walletInterationIcon(params.userWallet), - enabled = state.isPrimaryButtonEnabled, - modifier = Modifier - .fillMaxWidth() - .padding(16.dp), + }, + footer = { + PrimaryButtonIconEnd( + text = stringResourceSafe(R.string.common_confirm), + onClick = model::onClick, + iconResId = walletInterationIcon(params.userWallet), + enabled = state.isPrimaryButtonEnabled, + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + ) + }, + content = { + YieldSupplyActionContent( + yieldSupplyActionUM = state, + onFooterClick = model::onReadMoreClick, + yieldSupplyNotificationsComponent = yieldSupplyNotificationsComponent, + ) { + Box( + modifier = Modifier + .size(56.dp) + .background(TangemTheme.colors.icon.accent.copy(0.1f), CircleShape), + ) { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.ic_check_circle_24), + contentDescription = null, + tint = TangemTheme.colors.icon.accent, + modifier = Modifier + .padding(12.dp) + .size(32.dp), + ) + } + } + }, ) } @@ -105,7 +116,7 @@ internal class YieldSupplyApproveComponent( ) interface ModelCallback { - fun onBackClick() + fun onDismissClick() fun onTransactionProgress(inProgress: Boolean) fun onTransactionSent() } diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt index baa31bd4fd..dd17066a2d 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt @@ -144,7 +144,7 @@ internal class YieldSupplyApproveModel @Inject constructor( ) yieldSupplyAlertFactory.getSendTransactionErrorState( error = error, - popBack = params.callback::onBackClick, + popBack = params.callback::onDismissClick, onFailedTxEmailClick = { errorMessage -> modelScope.launch(dispatchers.default) { yieldSupplyAlertFactory.onFailedTxEmailClick( diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningModel.kt index bc56f86994..f9d09e6fac 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningModel.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningModel.kt @@ -70,6 +70,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor( private val params: YieldSupplyStartEarningComponent.Params = paramsContainer.require() private val cryptoCurrency = params.cryptoCurrency + private val userWalletId = params.userWalletId private var minAmount: BigDecimal by Delegates.notNull() var userWallet: UserWallet by Delegates.notNull() @@ -126,8 +127,8 @@ internal class YieldSupplyStartEarningModel @Inject constructor( } } - private suspend fun calculateMinAmount(userWallet: UserWallet, cryptoCurrencyStatus: CryptoCurrencyStatus) { - yieldSupplyMinAmountUseCase(userWallet, cryptoCurrencyStatus).onRight { + private suspend fun calculateMinAmount(cryptoCurrencyStatus: CryptoCurrencyStatus) { + yieldSupplyMinAmountUseCase(userWalletId, cryptoCurrencyStatus).onRight { minAmount = it }.onLeft { minAmount = BigDecimal.ZERO @@ -141,11 +142,11 @@ internal class YieldSupplyStartEarningModel @Inject constructor( it.copy(yieldSupplyFeeUM = YieldSupplyFeeUM.Loading) } - val maxFee = yieldSupplyGetMaxFeeUseCase(userWallet, cryptoCurrencyStatus).getOrNull() ?: return - val estimatedFee = yieldSupplyGetCurrentFeeUseCase(userWallet, cryptoCurrencyStatus).getOrNull() ?: return + val maxFee = yieldSupplyGetMaxFeeUseCase(userWalletId, cryptoCurrencyStatus).getOrNull() ?: return + val estimatedFee = yieldSupplyGetCurrentFeeUseCase(userWalletId, cryptoCurrencyStatus).getOrNull() ?: return val transactionListData = yieldSupplyStartEarningUseCase( - userWalletId = userWallet.walletId, + userWalletId = userWalletId, cryptoCurrencyStatus = cryptoCurrencyStatus, maxNetworkFee = maxFee.tokenMaxFee, ).getOrNull() @@ -255,7 +256,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor( private suspend fun onStartEarningTransactionSuccess(yieldSupplyFeeUM: YieldSupplyFeeUM.Content) { yieldSupplyRepository.saveTokenProtocolStatus( - userWallet.walletId, + userWalletId, cryptoCurrency, YieldSupplyEnterStatus.Enter, ) @@ -282,7 +283,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor( val address = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value if (address != null) { yieldSupplyActivateUseCase( - userWalletId = userWallet.walletId, + userWalletId = userWalletId, cryptoCurrency = cryptoCurrency, address = address, ) @@ -295,7 +296,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor( private fun subscribeOnCurrencyStatusUpdates() { modelScope.launch { - getUserWalletUseCase(params.userWalletId).fold( + getUserWalletUseCase(userWalletId).fold( ifRight = { wallet -> userWallet = wallet getCurrenciesStatusUpdates() @@ -325,7 +326,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor( private fun getCurrenciesStatusUpdates() { getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet( - userWalletId = params.userWalletId, + userWalletId = userWalletId, currencyId = cryptoCurrency.id, isSingleWalletWithTokens = false, ).onEach { maybeCryptoCurrency -> @@ -334,7 +335,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor( onDataLoaded( currencyStatus = cryptoCurrencyStatus, feeCurrencyStatus = getFeePaidCryptoCurrencyStatusSyncUseCase( - userWalletId = params.userWalletId, + userWalletId = userWalletId, cryptoCurrencyStatus = cryptoCurrencyStatus, ).getOrNull() ?: cryptoCurrencyStatus, ) @@ -352,7 +353,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor( feeCryptoCurrencyStatusFlow.update { feeCurrencyStatus } modelScope.launch { - calculateMinAmount(userWallet, currencyStatus) + calculateMinAmount(currencyStatus) onLoadFee() } } diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/YieldSupplyStopEarningComponent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/YieldSupplyStopEarningComponent.kt index e74a248197..d67dc087bb 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/YieldSupplyStopEarningComponent.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/YieldSupplyStopEarningComponent.kt @@ -9,6 +9,7 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Icon import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.vectorResource @@ -19,8 +20,11 @@ import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.child import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.components.PrimaryButtonIconEnd +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle -import com.tangem.core.ui.decompose.ComposableModularContentComponent +import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetWithFooter +import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme import com.tangem.domain.models.currency.CryptoCurrencyStatus @@ -31,10 +35,11 @@ import com.tangem.features.yield.supply.impl.subcomponents.notifications.YieldSu import com.tangem.features.yield.supply.impl.subcomponents.stopearning.model.YieldSupplyStopEarningModel import kotlinx.coroutines.flow.StateFlow +@Suppress("MagicNumber") internal class YieldSupplyStopEarningComponent( private val appComponentContext: AppComponentContext, private val params: Params, -) : ComposableModularContentComponent, AppComponentContext by appComponentContext { +) : ComposableBottomSheetComponent, AppComponentContext by appComponentContext { private val model: YieldSupplyStopEarningModel = getOrCreateModel(params = params) @@ -48,54 +53,64 @@ internal class YieldSupplyStopEarningComponent( ), ) - @Composable - override fun Title() { - TangemModalBottomSheetTitle( - startIconRes = R.drawable.ic_back_24, - onStartClick = params.callback::onBackClick, - ) + override fun dismiss() { + params.callback.onDismissClick() } - @Suppress("MagicNumber") @Composable - override fun Content(modifier: Modifier) { + override fun BottomSheet() { val state by model.uiState.collectAsStateWithLifecycle() - YieldSupplyActionContent( - yieldSupplyActionUM = state, - onFooterClick = model::onReadMoreClick, - yieldSupplyNotificationsComponent = yieldSupplyNotificationsComponent, - modifier = modifier, - ) { - Box( - modifier = Modifier - .size(56.dp) - .background(TangemTheme.colors.icon.attention.copy(0.1f), CircleShape), - ) { - Icon( - imageVector = ImageVector.vectorResource(R.drawable.ic_alert_triangle_20), - contentDescription = null, - tint = TangemTheme.colors.icon.attention, - modifier = Modifier - .padding(12.dp) - .size(32.dp), - ) - } + val config = remember { + TangemBottomSheetConfig( + isShown = true, + onDismissRequest = params.callback::onDismissClick, + content = TangemBottomSheetConfigContent.Empty, + ) } - } - @Composable - override fun Footer() { - val state by model.uiState.collectAsStateWithLifecycle() - - PrimaryButtonIconEnd( - text = stringResourceSafe(R.string.common_confirm), - onClick = model::onClick, - iconResId = walletInterationIcon(params.userWallet), - enabled = state.isPrimaryButtonEnabled, - showProgress = state.isTransactionSending, - modifier = Modifier - .fillMaxWidth() - .padding(16.dp), + TangemModalBottomSheetWithFooter( + config = config, + containerColor = TangemTheme.colors.background.tertiary, + title = { + TangemModalBottomSheetTitle( + endIconRes = R.drawable.ic_close_24, + onEndClick = params.callback::onDismissClick, + ) + }, + footer = { + PrimaryButtonIconEnd( + text = stringResourceSafe(R.string.common_confirm), + onClick = model::onClick, + iconResId = walletInterationIcon(params.userWallet), + enabled = state.isPrimaryButtonEnabled, + showProgress = state.isTransactionSending, + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + ) + }, + content = { + YieldSupplyActionContent( + yieldSupplyActionUM = state, + onFooterClick = model::onReadMoreClick, + yieldSupplyNotificationsComponent = yieldSupplyNotificationsComponent, + ) { + Box( + modifier = Modifier + .size(56.dp) + .background(TangemTheme.colors.icon.attention.copy(0.1f), CircleShape), + ) { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.ic_alert_triangle_20), + contentDescription = null, + tint = TangemTheme.colors.icon.attention, + modifier = Modifier + .padding(12.dp) + .size(32.dp), + ) + } + } + }, ) } @@ -106,8 +121,8 @@ internal class YieldSupplyStopEarningComponent( ) interface ModelCallback { - fun onBackClick() + fun onDismissClick() fun onTransactionProgress(inProgress: Boolean) - fun onTransactionSent() + fun onStopEarningTransactionSent() } } \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/model/YieldSupplyStopEarningModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/model/YieldSupplyStopEarningModel.kt index 13daef75cc..f327d6c82a 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/model/YieldSupplyStopEarningModel.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/model/YieldSupplyStopEarningModel.kt @@ -154,7 +154,7 @@ internal class YieldSupplyStopEarningModel @Inject constructor( ) yieldSupplyAlertFactory.getSendTransactionErrorState( error = error, - popBack = params.callback::onBackClick, + popBack = params.callback::onDismissClick, onFailedTxEmailClick = { errorMessage -> modelScope.launch(dispatchers.default) { yieldSupplyAlertFactory.onFailedTxEmailClick( @@ -203,7 +203,7 @@ internal class YieldSupplyStopEarningModel @Inject constructor( } modelScope.launch { - params.callback.onTransactionSent() + params.callback.onStopEarningTransactionSent() } }