Updated on 2026-08-14

This commit is contained in:
Tangem 2026-05-20 14:02:58 +02:00
parent c7269fbadb
commit 305758f4be
5 changed files with 102 additions and 97 deletions

View file

@ -33,15 +33,14 @@ import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.LocalMainBottomSheetColor
import com.tangem.core.ui.res.LocalRedesignEnabled
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioComponent
import com.tangem.domain.models.earn.PreselectedEarnType
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioComponent
import com.tangem.features.feed.components.feed.FeedBottomSheetRoute
import kotlinx.serialization.Serializable
import com.tangem.features.feed.model.earn.EarnModel
import com.tangem.features.feed.model.earn.analytics.EarnSource
import com.tangem.features.feed.ui.components.FeedSearchBar
import com.tangem.features.feed.ui.earn.EarnContent
import dev.chrisbanes.haze.HazeProgressive
import kotlinx.serialization.Serializable
internal class DefaultEarnComponent(
appComponentContext: AppComponentContext,
@ -130,17 +129,12 @@ internal class DefaultEarnComponent(
componentContext: ComponentContext,
): ComposableBottomSheetComponent = when (config) {
is FeedBottomSheetRoute.AddToPortfolio -> {
val manager = checkNotNull(earnModel.currentAddToPortfolioManager) {
"currentAddToPortfolioManager must be set before activating AddToPortfolio slot"
}
addToPortfolioComponentFactory.create(
context = childByContext(componentContext),
params = AddToPortfolioComponent.Params(
addToPortfolioManager = when {
config.source == EarnSource.BEST_OPPORTUNITIES_SOURCE.value ->
earnModel.addBestOpportunitiesPortfolioManager
config.source == EarnSource.MOSTLY_USED_SOURCE.value ->
earnModel.addMostlyUsedPortfolioManager
else -> error("Unknown source: ${config.source}")
},
),
params = AddToPortfolioComponent.Params(addToPortfolioManager = manager),
)
}
is FeedBottomSheetRoute.NetworkFilter -> EarnNetworkFilterComponent(

View file

@ -98,11 +98,12 @@ internal class DefaultFeedComponent(
componentContext: ComponentContext,
): ComposableBottomSheetComponent = when (config) {
is FeedBottomSheetRoute.AddToPortfolio -> {
val manager = checkNotNull(feedComponentModel.currentAddToPortfolioManager) {
"currentAddToPortfolioManager must be set before activating AddToPortfolio slot"
}
addToPortfolioComponentFactory.create(
context = childByContext(componentContext),
params = AddToPortfolioComponent.Params(
addToPortfolioManager = feedComponentModel.addToPortfolioManager,
),
params = AddToPortfolioComponent.Params(addToPortfolioManager = manager),
)
}
is FeedBottomSheetRoute.NetworkFilter -> EmptyComposableBottomSheetComponent

View file

@ -22,8 +22,9 @@ import com.tangem.domain.markets.TokenMarketInfo
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.earn.EarnNetworks
import com.tangem.domain.models.earn.EarnTokenWithCurrency
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
import com.tangem.domain.models.earn.PreselectedEarnType
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager.AnalyticsParams.Companion.CategoryEarn
import com.tangem.features.feed.components.earn.DefaultEarnComponent
import com.tangem.features.feed.components.earn.EarnNetworkFilterComponent
import com.tangem.features.feed.components.earn.EarnTypeFilterComponent
@ -44,8 +45,8 @@ import com.tangem.features.feed.ui.earn.state.EarnFilterTypeUM
import com.tangem.features.feed.ui.earn.state.EarnUM
import com.tangem.utils.Provider
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject
@Stable
@ -85,24 +86,13 @@ internal class EarnModel @Inject constructor(
dispatchers = dispatchers,
)
val addBestOpportunitiesPortfolioManager: AddToPortfolioManager = addToPortfolioManagerFactory.create(
scope = modelScope,
settings = AddToPortfolioManager.Settings.Earn,
analyticsParams = AddToPortfolioManager.AnalyticsParams(source = EarnSource.BEST_OPPORTUNITIES_SOURCE.value),
).apply {
updateLaunchMode(AddToPortfolioManager.LaunchMode.Preselected)
}
val addMostlyUsedPortfolioManager: AddToPortfolioManager = addToPortfolioManagerFactory.create(
scope = modelScope,
settings = AddToPortfolioManager.Settings.Earn,
analyticsParams = AddToPortfolioManager.AnalyticsParams(source = EarnSource.MOSTLY_USED_SOURCE.value),
).apply {
updateLaunchMode(AddToPortfolioManager.LaunchMode.Preselected)
}
val bottomSheetNavigation: SlotNavigation<FeedBottomSheetRoute> = SlotNavigation()
var currentAddToPortfolioManager: AddToPortfolioManager? = null
private set
private var currentAddToPortfolioManagerScope: CoroutineScope? = null
val state: StateFlow<EarnUM>
get() = stateController.uiState
@ -114,30 +104,6 @@ internal class EarnModel @Inject constructor(
subscribeOnNetworks()
subscribeOnBatchFlow()
subscribeToMostlyUsed()
addBestOpportunitiesPortfolioManager.onDismiss.receiveAsFlow()
.onEach { bottomSheetNavigation.dismiss() }
.launchIn(modelScope)
addBestOpportunitiesPortfolioManager.onSuccessAdded.receiveAsFlow()
.onEach { bottomSheetNavigation.dismiss() }
.onEach(::openCurrencyDetails)
.launchIn(modelScope)
addBestOpportunitiesPortfolioManager.onAddedTokenClick.receiveAsFlow()
.onEach { bottomSheetNavigation.dismiss() }
.onEach(::openCurrencyDetails)
.launchIn(modelScope)
addMostlyUsedPortfolioManager.onDismiss.receiveAsFlow()
.onEach { bottomSheetNavigation.dismiss() }
.launchIn(modelScope)
addMostlyUsedPortfolioManager.onSuccessAdded.receiveAsFlow()
.onEach { bottomSheetNavigation.dismiss() }
.onEach(::openCurrencyDetails)
.launchIn(modelScope)
addMostlyUsedPortfolioManager.onAddedTokenClick.receiveAsFlow()
.onEach { bottomSheetNavigation.dismiss() }
.onEach(::openCurrencyDetails)
.launchIn(modelScope)
}
private fun openCurrencyDetails(result: AddToPortfolioManager.Result) {
@ -354,17 +320,44 @@ internal class EarnModel @Inject constructor(
contractAddress = earnTokenWithCurrency.earnToken.tokenAddress,
decimalCount = earnTokenWithCurrency.earnToken.decimalCount,
)
when (source) {
EarnSource.BEST_OPPORTUNITIES_SOURCE -> addBestOpportunitiesPortfolioManager.apply {
setTokenParams(token)
setTokenNetworks(listOf(network))
}
EarnSource.MOSTLY_USED_SOURCE -> addMostlyUsedPortfolioManager.apply {
setTokenParams(token)
setTokenNetworks(listOf(network))
}
val manager = createAddToPortfolioManager(source = source).apply {
setTokenParams(token)
setTokenNetworks(listOf(network))
}
bottomSheetNavigation.activate(FeedBottomSheetRoute.AddToPortfolio(source.value))
currentAddToPortfolioManager = manager
// Drop the slot through null so the same-source repeat click still recreates the child.
bottomSheetNavigation.dismiss()
bottomSheetNavigation.activate(FeedBottomSheetRoute.AddToPortfolio(source = source.value))
}
private fun createAddToPortfolioManager(source: EarnSource): AddToPortfolioManager {
currentAddToPortfolioManagerScope?.cancel()
val managerScope = CoroutineScope(
modelScope.coroutineContext + SupervisorJob(modelScope.coroutineContext.job),
)
currentAddToPortfolioManagerScope = managerScope
val manager = addToPortfolioManagerFactory.create(
scope = managerScope,
settings = AddToPortfolioManager.Settings.Earn,
analyticsParams = AddToPortfolioManager.AnalyticsParams(source = source.value, category = CategoryEarn),
).apply {
updateLaunchMode(AddToPortfolioManager.LaunchMode.Preselected)
}
manager.onDismiss.receiveAsFlow()
.onEach { bottomSheetNavigation.dismiss() }
.launchIn(managerScope)
manager.onSuccessAdded.receiveAsFlow()
.onEach { bottomSheetNavigation.dismiss() }
.onEach(::openCurrencyDetails)
.launchIn(managerScope)
manager.onAddedTokenClick.receiveAsFlow()
.onEach { bottomSheetNavigation.dismiss() }
.onEach(::openCurrencyDetails)
.launchIn(managerScope)
return manager
}
private fun onTypeFilterOptionSelected(type: EarnFilterType) {

View file

@ -41,10 +41,8 @@ import com.tangem.utils.Provider
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentHashMap
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import org.joda.time.DateTime
import org.joda.time.DateTimeZone
import javax.inject.Inject
@ -62,7 +60,7 @@ internal class FeedComponentModel @Inject constructor(
private val getTopEarnTokensUseCase: GetTopEarnTokensUseCase,
private val appRouter: AppRouter,
private val designFeatureToggles: DesignFeatureToggles,
addToPortfolioManagerFactory: AddToPortfolioManager.Factory,
private val addToPortfolioManagerFactory: AddToPortfolioManager.Factory,
getTopFiveMarketTokenUseCase: GetTopFiveMarketTokenUseCase,
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
paramsContainer: ParamsContainer,
@ -87,16 +85,13 @@ internal class FeedComponentModel @Inject constructor(
dispatchers = dispatchers,
)
val addToPortfolioManager: AddToPortfolioManager = addToPortfolioManagerFactory.create(
scope = modelScope,
settings = AddToPortfolioManager.Settings.Earn,
analyticsParams = AddToPortfolioManager.AnalyticsParams(source = AnalyticsParam.ScreensSources.Markets.value),
).apply {
updateLaunchMode(AddToPortfolioManager.LaunchMode.Preselected)
}
val bottomSheetNavigation: SlotNavigation<FeedBottomSheetRoute> = SlotNavigation()
var currentAddToPortfolioManager: AddToPortfolioManager? = null
private set
private var currentAddToPortfolioManagerScope: CoroutineScope? = null
val state: StateFlow<FeedListUM>
get() = stateController.uiState
@ -110,18 +105,6 @@ internal class FeedComponentModel @Inject constructor(
fetchCharts()
subscribeOnCurrencyUpdate()
subscribeOnDataState()
addToPortfolioManager.onDismiss.receiveAsFlow()
.onEach { bottomSheetNavigation.dismiss() }
.launchIn(modelScope)
addToPortfolioManager.onSuccessAdded.receiveAsFlow()
.onEach { bottomSheetNavigation.dismiss() }
.onEach(::openCurrencyDetails)
.launchIn(modelScope)
addToPortfolioManager.onAddedTokenClick.receiveAsFlow()
.onEach { bottomSheetNavigation.dismiss() }
.onEach(::openCurrencyDetails)
.launchIn(modelScope)
}
private fun openCurrencyDetails(result: AddToPortfolioManager.Result) {
@ -431,12 +414,46 @@ internal class FeedComponentModel @Inject constructor(
contractAddress = earnTokenWithCurrency.earnToken.tokenAddress,
decimalCount = earnTokenWithCurrency.earnToken.decimalCount,
)
val route = FeedBottomSheetRoute.AddToPortfolio(AnalyticsParam.ScreensSources.Markets.value)
addToPortfolioManager.apply {
val manager = createAddToPortfolioManager().apply {
setTokenParams(token)
setTokenNetworks(listOf(network))
}
bottomSheetNavigation.activate(route)
currentAddToPortfolioManager = manager
// Drop the slot through null so the same-source repeat click still recreates the child.
bottomSheetNavigation.dismiss()
bottomSheetNavigation.activate(FeedBottomSheetRoute.AddToPortfolio(AnalyticsParam.ScreensSources.Markets.value))
}
private fun createAddToPortfolioManager(): AddToPortfolioManager {
currentAddToPortfolioManagerScope?.cancel()
val managerScope = CoroutineScope(
modelScope.coroutineContext + SupervisorJob(modelScope.coroutineContext.job),
)
currentAddToPortfolioManagerScope = managerScope
val manager = addToPortfolioManagerFactory.create(
scope = managerScope,
settings = AddToPortfolioManager.Settings.Earn,
analyticsParams = AddToPortfolioManager.AnalyticsParams(
source = AnalyticsParam.ScreensSources.Markets.value,
),
).apply {
updateLaunchMode(AddToPortfolioManager.LaunchMode.Preselected)
}
manager.onDismiss.receiveAsFlow()
.onEach { bottomSheetNavigation.dismiss() }
.launchIn(managerScope)
manager.onSuccessAdded.receiveAsFlow()
.onEach { bottomSheetNavigation.dismiss() }
.onEach(::openCurrencyDetails)
.launchIn(managerScope)
manager.onAddedTokenClick.receiveAsFlow()
.onEach { bottomSheetNavigation.dismiss() }
.onEach(::openCurrencyDetails)
.launchIn(managerScope)
return manager
}
private fun handleEarnPageOpenClicked() {