Updated on 2026-08-14
This commit is contained in:
parent
596cbb40cb
commit
eb6d4806e6
16 changed files with 291 additions and 710 deletions
|
|
@ -76,6 +76,7 @@ interface AddToPortfolioManager : AddToPortfolioManagerInternal {
|
|||
companion object {
|
||||
val DefaultMarket = Settings(shouldSkipTokenActionsScreen = false)
|
||||
val ChooseToken = Settings(shouldSkipTokenActionsScreen = true)
|
||||
val Earn = Settings(shouldSkipTokenActionsScreen = true)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
package com.tangem.features.commonfeatures.api.addtoportfolio
|
||||
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.domain.markets.TokenMarketInfo
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
interface AddToPortfolioPreselectedDataComponent : ComposableBottomSheetComponent {
|
||||
|
||||
/**
|
||||
* @param tokenToAdd token and preselected network (no network selector).
|
||||
* @param callback callbacks for add-to-portfolio flow.
|
||||
*/
|
||||
data class Params(
|
||||
val tokenToAdd: TokenToAdd,
|
||||
val callback: Callback,
|
||||
val analyticsParams: AnalyticsParams,
|
||||
)
|
||||
|
||||
data class AnalyticsParams(val source: String)
|
||||
|
||||
interface Callback {
|
||||
fun onDismiss()
|
||||
fun onSuccess(addedToken: CryptoCurrency, walletId: UserWalletId)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class TokenToAdd(
|
||||
val network: TokenMarketInfo.Network,
|
||||
val id: CryptoCurrency.RawID,
|
||||
val name: String,
|
||||
val symbol: String,
|
||||
)
|
||||
|
||||
interface Factory : ComponentFactory<Params, AddToPortfolioPreselectedDataComponent>
|
||||
}
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
package com.tangem.features.commonfeatures.impl.addtoportfolio
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.arkivanov.decompose.extensions.compose.subscribeAsState
|
||||
import com.arkivanov.decompose.router.stack.backStack
|
||||
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.child
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioSelectorComponent
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioPreselectedDataComponent
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddToPortfolioPreselectedDataModel
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddToPortfolioRoutes
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
internal class DefaultAddToPortfolioPreselectedDataComponent @AssistedInject constructor(
|
||||
@Assisted context: AppComponentContext,
|
||||
@Assisted private val params: AddToPortfolioPreselectedDataComponent.Params,
|
||||
portfolioSelectorComponentFactory: PortfolioSelectorComponent.Factory,
|
||||
addTokenComponentFactory: AddTokenComponent.Factory,
|
||||
) : AppComponentContext by context, AddToPortfolioPreselectedDataComponent {
|
||||
|
||||
private val model: AddToPortfolioPreselectedDataModel = getOrCreateModel(params)
|
||||
|
||||
private val portfolioSelectorComponent: PortfolioSelectorComponent = portfolioSelectorComponentFactory.create(
|
||||
context = child("portfolioSelectorComponent"),
|
||||
params = PortfolioSelectorComponent.Params(
|
||||
portfolioFetcher = model.portfolioFetcher,
|
||||
controller = model.portfolioSelectorController,
|
||||
),
|
||||
)
|
||||
|
||||
private val addTokenComponent: AddTokenComponent = addTokenComponentFactory.create(
|
||||
context = child("addTokenComponent"),
|
||||
params = AddTokenComponent.Params(
|
||||
eventBuilder = model.eventBuilder,
|
||||
callbacks = model,
|
||||
selectedPortfolio = model.selectedPortfolio,
|
||||
selectedNetwork = model.selectedNetwork,
|
||||
),
|
||||
)
|
||||
|
||||
private val childStack = childStack(
|
||||
key = "addToPortfolioFromEarnStack",
|
||||
handleBackButton = true,
|
||||
source = model.navigation,
|
||||
serializer = AddToPortfolioRoutes.serializer(),
|
||||
initialStack = { model.currentStack },
|
||||
childFactory = { configuration, _ ->
|
||||
contentChild(configuration)
|
||||
},
|
||||
)
|
||||
|
||||
private fun onBack() {
|
||||
if (childStack.backStack.isNotEmpty()) model.navigation.pop() else dismiss()
|
||||
}
|
||||
|
||||
override fun dismiss() {
|
||||
params.callback.onDismiss()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun BottomSheet() {
|
||||
AddToPortfolioBottomSheet(
|
||||
childStack = childStack.subscribeAsState(),
|
||||
onBack = ::onBack,
|
||||
onDismiss = ::dismiss,
|
||||
)
|
||||
}
|
||||
|
||||
private fun contentChild(config: AddToPortfolioRoutes): ComposableContentComponent = when (config) {
|
||||
AddToPortfolioRoutes.AddToken -> addTokenComponent
|
||||
AddToPortfolioRoutes.PortfolioSelector -> portfolioSelectorComponent
|
||||
AddToPortfolioRoutes.TokenActions -> ComposableContentComponent.EMPTY
|
||||
AddToPortfolioRoutes.Empty -> ComposableContentComponent.EMPTY
|
||||
is AddToPortfolioRoutes.NetworkSelector -> ComposableContentComponent.EMPTY
|
||||
AddToPortfolioRoutes.UserPortfolio -> ComposableContentComponent.EMPTY
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : AddToPortfolioPreselectedDataComponent.Factory {
|
||||
override fun create(
|
||||
context: AppComponentContext,
|
||||
params: AddToPortfolioPreselectedDataComponent.Params,
|
||||
): DefaultAddToPortfolioPreselectedDataComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -2,9 +2,7 @@ package com.tangem.features.commonfeatures.impl.addtoportfolio.di
|
|||
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioComponent
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioPreselectedDataComponent
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.DefaultAddToPortfolioComponent
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.DefaultAddToPortfolioPreselectedDataComponent
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.ui.DefaultAddToPortfolioManager
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio.DefaultUserPortfolioComponent
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.userportfolio.UserPortfolioComponent
|
||||
|
|
@ -23,11 +21,6 @@ internal interface AddToPortfolioComponentModule {
|
|||
@Binds
|
||||
fun bindAddToPortfolioManagerFactory(factory: DefaultAddToPortfolioManager.Factory): AddToPortfolioManager.Factory
|
||||
|
||||
@Binds
|
||||
fun bindAddToPortfolioPreselectedDataComponent(
|
||||
factory: DefaultAddToPortfolioPreselectedDataComponent.Factory,
|
||||
): AddToPortfolioPreselectedDataComponent.Factory
|
||||
|
||||
@Binds
|
||||
fun bindUserPortfolioComponentFactory(
|
||||
factory: DefaultUserPortfolioComponent.Factory,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.tangem.features.commonfeatures.impl.addtoportfolio.di
|
|||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddToPortfolioModel
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddToPortfolioPreselectedDataModel
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddTokenModel
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.ChooseNetworkModel
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.model.TokenActionsModel
|
||||
|
|
@ -28,11 +27,6 @@ internal interface AddToPortfolioModelModule {
|
|||
@ClassKey(AddToPortfolioModel::class)
|
||||
fun addToPortfolioModel(model: AddToPortfolioModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(AddToPortfolioPreselectedDataModel::class)
|
||||
fun addToPortfolioPreselectedDataModel(model: AddToPortfolioPreselectedDataModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(TokenActionsModel::class)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import com.tangem.core.decompose.ui.UiMessageSender
|
|||
import com.tangem.core.ui.DesignFeatureToggles
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.ToastMessage
|
||||
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
|
||||
import com.tangem.domain.account.status.usecase.GetCryptoCurrencyActionsUseCaseV2
|
||||
import com.tangem.domain.markets.GetTokenMarketCryptoCurrency
|
||||
import com.tangem.domain.markets.RawMarketToken
|
||||
|
|
@ -60,6 +61,7 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
|
||||
private val selectionResolver: AddToPortfolioInitialSelectionResolver,
|
||||
private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase,
|
||||
userPortfolioStateControllerFactory: UserPortfolioStateController.Factory,
|
||||
) : Model(),
|
||||
ChooseNetworkComponent.Callbacks by callbackDelegate,
|
||||
|
|
@ -95,19 +97,13 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
onTokenSelected = { result -> addToPortfolioManager.onAddedTokenClick(result) },
|
||||
)
|
||||
|
||||
val featureData: Flow<AddToPortfolioManager.State> = combineFeatureData()
|
||||
|
||||
private val globalSelectedWallet: UserWallet?
|
||||
get() = getSelectedWalletSyncUseCase().getOrNull()
|
||||
.takeIf { it?.isMultiCurrency == true }
|
||||
|
||||
init {
|
||||
navigation.subscribe { currentStack = it.transformer.invoke(currentStack) }
|
||||
if (designFeatureToggles.isRedesignEnabled) {
|
||||
startRedesignAddToPortfolioFlow()
|
||||
} else {
|
||||
startLegacyAddToPortfolioFlow()
|
||||
}
|
||||
startRedesignAddToPortfolioFlow()
|
||||
}
|
||||
|
||||
private fun <T> replayMutableSharedFlow() = MutableSharedFlow<T>(
|
||||
|
|
@ -115,132 +111,43 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
onBufferOverflow = BufferOverflow.DROP_OLDEST,
|
||||
)
|
||||
|
||||
@Suppress("LongMethod")
|
||||
private fun startLegacyAddToPortfolioFlow() {
|
||||
channelFlow<Unit> {
|
||||
fun finishSuccessFlow(result: AddToPortfolioManager.Result) {
|
||||
addToPortfolioManager.onSuccessAdded(result)
|
||||
channel.close()
|
||||
}
|
||||
|
||||
val featureDataFlow: StateFlow<AvailableToAddData> = featureData
|
||||
.filterIsInstance<AddToPortfolioManager.State.Ready>()
|
||||
.map { it.availableToAddData }
|
||||
.distinctUntilChanged()
|
||||
.stateIn(this)
|
||||
val isAccountMode = portfolioSelectorController.isAccountModeSync()
|
||||
|
||||
// use snapshot data, looks like we don’t need to remap at runtime
|
||||
val data = featureDataFlow.value
|
||||
|
||||
// init data flows, emits on user/code selection, updates state holder
|
||||
val firstSelectedPortfolio = setupPortfolioFlow(data)
|
||||
.onEach { selectedPortfolio.emit(it) }
|
||||
val firstSelectedNetwork = setupNetworkFlow(firstSelectedPortfolio)
|
||||
.onEach { selectedNetwork.emit(it) }
|
||||
|
||||
val isSinglePortfolio = data.isSinglePortfolio
|
||||
if (isSinglePortfolio) {
|
||||
val accountId = data.availableToAddWallets.values.first()
|
||||
.availableToAddAccounts.values.first()
|
||||
.account.account.accountId
|
||||
// force select a portfolio, triggers [selectedPortfolio]
|
||||
portfolioSelectorController.selectAccount(accountId)
|
||||
} else {
|
||||
logAccountSelector(isAccountMode)
|
||||
navigation.replaceAll(AddToPortfolioRoutes.PortfolioSelector)
|
||||
}
|
||||
|
||||
val firstPartOfNavigation: Job = firstSelectedPortfolio
|
||||
.onEach { portfolio ->
|
||||
val isSingleAvailableNetwork = portfolio.account.isSingleNetwork
|
||||
when {
|
||||
// force select a network, triggers [selectedNetwork]
|
||||
isSingleAvailableNetwork -> {
|
||||
val singleNetwork = portfolio.account.availableToAddNetworks.first()
|
||||
callbackDelegate.onNetworkSelected(singleNetwork)
|
||||
}
|
||||
// it's important to control root screen, UI depends on it(close/arrow icon)
|
||||
isSinglePortfolio -> navigation.replaceAll(routeToNetworkSelector(portfolio))
|
||||
else -> navigation.pushNew(routeToNetworkSelector(portfolio))
|
||||
}
|
||||
}
|
||||
.launchIn(this)
|
||||
|
||||
// main flow that combine all require data
|
||||
val allRequireForAdd = combine(
|
||||
flow = firstSelectedNetwork,
|
||||
flow2 = firstSelectedPortfolio,
|
||||
transform = { a, b -> a to b },
|
||||
)
|
||||
|
||||
// suspend until all required data is selected
|
||||
val firstPair = allRequireForAdd.first()
|
||||
// line of navigation to AddToken screen is finished; cancel the job, select a new root screen
|
||||
firstPartOfNavigation.cancel()
|
||||
|
||||
val selectedNetworkName = firstPair.first.cryptoCurrency.network.name
|
||||
analyticsEventHandler.send(event = eventBuilder.popupToConfirm(selectedNetworkName))
|
||||
navigation.replaceAll(AddToPortfolioRoutes.AddToken)
|
||||
|
||||
var middleNavigationJob: Job? = null
|
||||
// handle actions from AddToken screen
|
||||
callbackDelegate.onChangeNetworkClick.receiveAsFlow()
|
||||
.onEach {
|
||||
middleNavigationJob?.cancel()
|
||||
middleNavigationJob = changeNetworkNavigationFlow()
|
||||
.launchIn(this)
|
||||
val route = routeToNetworkSelector(selectedPortfolio.first())
|
||||
navigation.pushNew(route)
|
||||
}
|
||||
.launchIn(this)
|
||||
// handle actions from AddToken screen
|
||||
callbackDelegate.onChangePortfolioClick.receiveAsFlow()
|
||||
.onEach {
|
||||
middleNavigationJob?.cancel()
|
||||
middleNavigationJob = changePortfolioNavigationFlow(data).launchIn(this)
|
||||
logAccountSelector(isAccountMode)
|
||||
navigation.pushNew(AddToPortfolioRoutes.PortfolioSelector)
|
||||
}
|
||||
.launchIn(this)
|
||||
|
||||
// suspend until token is added
|
||||
val addedToken = callbackDelegate.onTokenAdded.receiveAsFlow().first()
|
||||
middleNavigationJob?.cancel()
|
||||
val selectedPortfolio = selectedPortfolio.first()
|
||||
analyticsEventHandler.send(eventBuilder.tokenAdded(addedToken.currency.network.name))
|
||||
if (!selectedPortfolio.account.account.account.isMainAccount) {
|
||||
analyticsEventHandler.send(eventBuilder.addToNotMainAccount())
|
||||
}
|
||||
val result = AddToPortfolioManager.Result(
|
||||
wallet = selectedPortfolio.userWallet,
|
||||
account = selectedPortfolio.account.account,
|
||||
addedCurrency = addedToken,
|
||||
)
|
||||
|
||||
messageSender.send(ToastMessage(message = resourceReference(R.string.markets_token_added)))
|
||||
|
||||
if (addToPortfolioManager.settings.shouldSkipTokenActionsScreen) {
|
||||
finishSuccessFlow(result)
|
||||
} else {
|
||||
setupTokenActionsFlow(selectedPortfolio, addedToken)
|
||||
.onEach { cryptoCurrencyData ->
|
||||
tokenActionsData.emit(cryptoCurrencyData)
|
||||
navigation.replaceAll(AddToPortfolioRoutes.TokenActions)
|
||||
}
|
||||
.onEmpty { finishSuccessFlow(result) }
|
||||
.launchIn(this)
|
||||
}
|
||||
|
||||
callbackDelegate.onLaterClick.receiveAsFlow().first()
|
||||
analyticsEventHandler.send(eventBuilder.getTokenLater())
|
||||
finishSuccessFlow(result)
|
||||
private fun lineNavigationFlowToAddTokenScreen(
|
||||
isAccountMode: Boolean,
|
||||
data: AvailableToAddData,
|
||||
firstSelectedPortfolioFlow: Flow<SelectedPortfolio>,
|
||||
): Flow<SelectedPortfolio> {
|
||||
val isSinglePortfolio = data.isSinglePortfolio
|
||||
if (isSinglePortfolio) {
|
||||
val accountId = data.availableToAddWallets.values.first()
|
||||
.availableToAddAccounts.values.first()
|
||||
.account.account.accountId
|
||||
// force select a portfolio, triggers [selectedPortfolio]
|
||||
portfolioSelectorController.selectAccount(accountId)
|
||||
} else {
|
||||
logAccountSelector(isAccountMode)
|
||||
navigation.replaceAll(AddToPortfolioRoutes.PortfolioSelector)
|
||||
}
|
||||
.catch { throwable ->
|
||||
TangemLogger.e("Error", throwable)
|
||||
addToPortfolioManager.onDismiss()
|
||||
|
||||
return firstSelectedPortfolioFlow.onEach { portfolio ->
|
||||
val isAvailableToAdd = portfolio.account.isAvailableToAdd
|
||||
val isSingleAvailableNetwork = portfolio.account.isSingleNetwork
|
||||
when {
|
||||
// force select an added network, not allowed to add
|
||||
// but its call AddToPortfolioManager.onAddedTokenClick callback
|
||||
!isAvailableToAdd -> {
|
||||
val singleNetwork = portfolio.account.addedMarketNetworks.first()
|
||||
callbackDelegate.onNetworkSelected(singleNetwork)
|
||||
}
|
||||
// force select a network, triggers [selectedNetwork]
|
||||
isSingleAvailableNetwork -> {
|
||||
val singleNetwork = portfolio.account.availableToAddNetworks.first()
|
||||
callbackDelegate.onNetworkSelected(singleNetwork)
|
||||
}
|
||||
// it's important to control root screen, UI depends on it(close/arrow icon)
|
||||
isSinglePortfolio -> navigation.replaceAll(routeToNetworkSelector(portfolio))
|
||||
else -> navigation.pushNew(routeToNetworkSelector(portfolio))
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
|
|
@ -250,6 +157,10 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
addToPortfolioManager.onSuccessAdded(result)
|
||||
channel.close()
|
||||
}
|
||||
fun finishOnAddedTokenClick(result: AddToPortfolioManager.Result) {
|
||||
addToPortfolioManager.onAddedTokenClick(result)
|
||||
channel.close()
|
||||
}
|
||||
|
||||
fun finishDismissFlow() {
|
||||
addToPortfolioManager.onDismiss()
|
||||
|
|
@ -259,45 +170,85 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
val tokenMarketParams = paramsSnapshot.token
|
||||
|
||||
val launchMode = paramsSnapshot.launchMode
|
||||
val initialData = featureData
|
||||
val isAccountMode = portfolioSelectorController.isAccountModeSync()
|
||||
val initialData: AvailableToAddData = addToPortfolioManager.state
|
||||
.filterIsInstance<AddToPortfolioManager.State.Ready>()
|
||||
.map { it.availableToAddData }
|
||||
.first()
|
||||
|
||||
if (launchMode is AddToPortfolioManager.LaunchMode.ViaUserPortfolio &&
|
||||
setupPortfolioSelector(initialData, launchMode)
|
||||
|
||||
val shouldShowUserPortfolio = designFeatureToggles.isRedesignEnabled &&
|
||||
launchMode is AddToPortfolioManager.LaunchMode.ViaUserPortfolio &&
|
||||
initialData.hasAnyAddedCurrency(tokenMarketParams.id)
|
||||
) {
|
||||
|
||||
if (shouldShowUserPortfolio) {
|
||||
// suspend, must prepare UM before navigate to UserPortfolio
|
||||
userPortfolioStateController.updateAndWaitNotNullState(initialData, tokenMarketParams.id)
|
||||
userPortfolioStateController.updateAndWaitNotNullState(
|
||||
allAvailableData = initialData,
|
||||
rawCurrencyId = tokenMarketParams.id,
|
||||
)
|
||||
navigation.replaceAll(AddToPortfolioRoutes.UserPortfolio)
|
||||
callbackDelegate.onContinueFromUserPortfolio.receiveAsFlow().first()
|
||||
}
|
||||
val selection = selectionResolver.resolve(
|
||||
availableToAddData = initialData,
|
||||
orderedNetworks = paramsSnapshot.networks,
|
||||
selectedWallet = globalSelectedWallet,
|
||||
tokenParams = tokenMarketParams,
|
||||
) ?: run {
|
||||
finishDismissFlow()
|
||||
return@channelFlow
|
||||
|
||||
val initialSelection: AddToPortfolioInitialSelectionResolver.InitialSelection? = when (launchMode) {
|
||||
AddToPortfolioManager.LaunchMode.Preselected -> null
|
||||
is AddToPortfolioManager.LaunchMode.ViaUserPortfolio,
|
||||
AddToPortfolioManager.LaunchMode.DirectAdd,
|
||||
-> if (designFeatureToggles.isRedesignEnabled) {
|
||||
getInitialSelection(initialData)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
val isAccountMode = portfolioSelectorController.isAccountModeSync()
|
||||
portfolioSelectorController.selectAccount(selection.account.account.accountId)
|
||||
val firstSelectedPortfolioFlow: Flow<SelectedPortfolio> =
|
||||
setupPortfolioFlow(initialData).onEach { selectedPortfolio.emit(it) }
|
||||
val firstSelectedNetworkFlow: Flow<SelectedNetwork> =
|
||||
setupNetworkFlow(firstSelectedPortfolioFlow).onEach { selectedNetwork.emit(it) }
|
||||
|
||||
val firstSelectedPortfolio = SelectedPortfolio(
|
||||
isAccountMode = isAccountMode,
|
||||
userWallet = selection.userWallet,
|
||||
account = selection.account,
|
||||
isAvailableMorePortfolio = !initialData.isSinglePortfolio,
|
||||
// main flow that combine all require data
|
||||
val allRequireForAdd = combine(
|
||||
flow = firstSelectedNetworkFlow,
|
||||
flow2 = firstSelectedPortfolioFlow,
|
||||
transform = { a, b -> a to b },
|
||||
)
|
||||
val firstSelectedNetwork = selection.toSelectedNetwork() ?: run {
|
||||
finishDismissFlow()
|
||||
return@channelFlow
|
||||
|
||||
var firstPartOfNavigation: Job? = null
|
||||
|
||||
if (initialSelection != null) {
|
||||
launch {
|
||||
portfolioSelectorController.selectAccount(initialSelection.account.account.accountId)
|
||||
callbackDelegate.onNetworkSelected.send(initialSelection.network)
|
||||
}
|
||||
} else {
|
||||
firstPartOfNavigation = lineNavigationFlowToAddTokenScreen(
|
||||
isAccountMode = isAccountMode,
|
||||
data = initialData,
|
||||
firstSelectedPortfolioFlow = firstSelectedPortfolioFlow,
|
||||
).launchIn(this)
|
||||
}
|
||||
|
||||
selectedPortfolio.emit(firstSelectedPortfolio)
|
||||
selectedNetwork.emit(firstSelectedNetwork)
|
||||
// suspend until all required data is selected
|
||||
val (firstSelectedNetwork, firstSelectedPortfolio) = allRequireForAdd.first()
|
||||
// line navigation to AddToken screen is finished; cancel the job if exists
|
||||
firstPartOfNavigation?.cancel()
|
||||
|
||||
val alreadyAddedToken = getAccountCurrencyStatusUseCase.invokeSync(
|
||||
userWalletId = firstSelectedPortfolio.userWallet.walletId,
|
||||
currency = firstSelectedNetwork.cryptoCurrency,
|
||||
).getOrNull()
|
||||
|
||||
if (alreadyAddedToken != null) {
|
||||
val result = AddToPortfolioManager.Result(
|
||||
wallet = firstSelectedPortfolio.userWallet,
|
||||
account = firstSelectedPortfolio.account.account,
|
||||
addedCurrency = alreadyAddedToken.status,
|
||||
)
|
||||
finishOnAddedTokenClick(result)
|
||||
return@channelFlow
|
||||
}
|
||||
|
||||
val selectedNetworkName = firstSelectedNetwork.cryptoCurrency.network.name
|
||||
analyticsEventHandler.send(event = eventBuilder.popupToConfirm(selectedNetworkName))
|
||||
|
|
@ -317,11 +268,15 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
callbackDelegate.onChangePortfolioClick.receiveAsFlow()
|
||||
.onEach {
|
||||
middleNavigationJob?.cancel()
|
||||
middleNavigationJob = changePortfolioNavigationNewFlow(
|
||||
data = initialData,
|
||||
orderedNetworks = paramsSnapshot.networks,
|
||||
tokenParams = tokenMarketParams,
|
||||
).launchIn(this)
|
||||
middleNavigationJob = if (designFeatureToggles.isRedesignEnabled) {
|
||||
changePortfolioNavigationNewFlow(
|
||||
data = initialData,
|
||||
orderedNetworks = paramsSnapshot.networks,
|
||||
tokenParams = tokenMarketParams,
|
||||
)
|
||||
} else {
|
||||
changePortfolioNavigationFlow(initialData)
|
||||
}.launchIn(this)
|
||||
logAccountSelector(isAccountMode)
|
||||
navigation.pushNew(AddToPortfolioRoutes.PortfolioSelector)
|
||||
}
|
||||
|
|
@ -362,6 +317,17 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private suspend fun getInitialSelection(
|
||||
initialData: AvailableToAddData,
|
||||
): AddToPortfolioInitialSelectionResolver.InitialSelection? {
|
||||
return selectionResolver.resolve(
|
||||
availableToAddData = initialData,
|
||||
orderedNetworks = paramsSnapshot.networks,
|
||||
selectedWallet = globalSelectedWallet,
|
||||
tokenParams = paramsSnapshot.token,
|
||||
)
|
||||
}
|
||||
|
||||
private fun logAccountSelector(isAccountMode: Boolean) {
|
||||
if (isAccountMode) {
|
||||
analyticsEventHandler.send(eventBuilder.popupToChooseAccount())
|
||||
|
|
@ -523,19 +489,21 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
return AddToPortfolioRoutes.NetworkSelector(selectedPortfolio = portfolio)
|
||||
}
|
||||
|
||||
private fun combineFeatureData() = addToPortfolioManager.state.onEach { state ->
|
||||
when (state) {
|
||||
is AddToPortfolioManager.State.Ready ->
|
||||
portfolioSelectorController.isEnabled.value = isEnabled@{ userWallet, accountStatus ->
|
||||
val availableWallet = state.availableToAddData.availableToAddWallets[userWallet.walletId]
|
||||
private fun setupPortfolioSelector(data: AvailableToAddData, launchMode: AddToPortfolioManager.LaunchMode) {
|
||||
portfolioSelectorController.isEnabled.value = isEnabled@{ userWallet, accountStatus ->
|
||||
when (launchMode) {
|
||||
AddToPortfolioManager.LaunchMode.Preselected -> return@isEnabled true
|
||||
AddToPortfolioManager.LaunchMode.DirectAdd,
|
||||
is AddToPortfolioManager.LaunchMode.ViaUserPortfolio,
|
||||
-> {
|
||||
val availableWallet = data.availableToAddWallets[userWallet.walletId]
|
||||
?: return@isEnabled false
|
||||
val isAvailableAccount =
|
||||
availableWallet.availableToAddAccounts[accountStatus.account.accountId]
|
||||
?.isAvailableToAdd == true
|
||||
val isAvailableAccount = availableWallet
|
||||
.availableToAddAccounts[accountStatus.account.accountId]
|
||||
?.isAvailableToAdd == true
|
||||
return@isEnabled isAvailableAccount
|
||||
}
|
||||
AddToPortfolioManager.State.Loading,
|
||||
-> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,300 +0,0 @@
|
|||
package com.tangem.features.commonfeatures.impl.addtoportfolio.model
|
||||
|
||||
import com.arkivanov.decompose.router.stack.StackNavigation
|
||||
import com.arkivanov.decompose.router.stack.replaceAll
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
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.ui.UiMessageSender
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.ToastMessage
|
||||
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
|
||||
import com.tangem.domain.markets.GetTokenMarketCryptoCurrency
|
||||
import com.tangem.domain.markets.RawMarketToken
|
||||
import com.tangem.domain.markets.TokenMarketInfo
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.account.filterCryptoPortfolio
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioFetcher
|
||||
import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioSelectorController
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.AddTokenComponent
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.*
|
||||
import com.tangem.features.commonfeatures.impl.R
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.analytics.EarnAnalyticsEvent
|
||||
import com.tangem.features.commonfeatures.impl.addtoportfolio.analytics.PortfolioAnalyticsEvent
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.*
|
||||
import javax.inject.Inject
|
||||
import kotlin.collections.mapNotNull
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class AddToPortfolioPreselectedDataModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
portfolioFetcherFactory: PortfolioFetcher.Factory,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
val portfolioSelectorController: PortfolioSelectorController,
|
||||
private val callbackDelegate: AddToPortfolioFromEarnCallbackDelegate,
|
||||
private val messageSender: UiMessageSender,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val getTokenMarketCryptoCurrency: GetTokenMarketCryptoCurrency,
|
||||
private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase,
|
||||
) : Model(), AddTokenComponent.Callbacks by callbackDelegate {
|
||||
|
||||
private val params = paramsContainer.require<AddToPortfolioPreselectedDataComponent.Params>()
|
||||
|
||||
val portfolioFetcher: PortfolioFetcher = portfolioFetcherFactory.create(
|
||||
mode = PortfolioFetcher.Mode.All(isOnlyMultiCurrency = true),
|
||||
scope = modelScope,
|
||||
)
|
||||
|
||||
val navigation = StackNavigation<AddToPortfolioRoutes>()
|
||||
var currentStack = listOf<AddToPortfolioRoutes>(AddToPortfolioRoutes.Empty)
|
||||
|
||||
private val _selectedNetwork = MutableStateFlow<SelectedNetwork?>(null)
|
||||
val selectedNetwork: Flow<SelectedNetwork> = _selectedNetwork.asStateFlow().filterNotNull()
|
||||
|
||||
private val _selectedPortfolio = MutableStateFlow<SelectedPortfolio?>(null)
|
||||
val selectedPortfolio: Flow<SelectedPortfolio> = _selectedPortfolio.asStateFlow().filterNotNull()
|
||||
val eventBuilder = PortfolioAnalyticsEvent.EventBuilder(
|
||||
tokenSymbol = params.tokenToAdd.symbol,
|
||||
source = AnalyticsParam.ScreensSources.Markets.value,
|
||||
category = AnalyticsParam.ScreensSources.Markets.value,
|
||||
)
|
||||
|
||||
init {
|
||||
navigation.subscribe { currentStack = it.transformer.invoke(currentStack) }
|
||||
startAddToPortfolioFlow()
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
private fun startAddToPortfolioFlow() {
|
||||
channelFlow<Unit> {
|
||||
fun finishSuccessFlow(currency: CryptoCurrency, userWalletId: UserWalletId) {
|
||||
params.callback.onSuccess(addedToken = currency, walletId = userWalletId)
|
||||
channel.close()
|
||||
}
|
||||
|
||||
val data = createAvailableToAddDataForPreselectedNetwork(params.tokenToAdd.network)
|
||||
?: return@channelFlow
|
||||
|
||||
val isAccountMode = portfolioSelectorController.isAccountModeSync()
|
||||
val firstSelectedPortfolio = setupPortfolioFlow(data)
|
||||
.onEach { _selectedPortfolio.value = it }
|
||||
|
||||
val firstSelectedNetwork = firstSelectedPortfolio
|
||||
.map { portfolio -> createSelectedNetwork(network = params.tokenToAdd.network, portfolio = portfolio) }
|
||||
.filterNotNull()
|
||||
.onEach { _selectedNetwork.value = it }
|
||||
|
||||
val isSinglePortfolio = data.isSinglePortfolio
|
||||
if (isSinglePortfolio) {
|
||||
val accountId = data.availableToAddWallets.values.first()
|
||||
.availableToAddAccounts.values.first()
|
||||
.account.account.accountId
|
||||
// force select a portfolio, triggers [selectedPortfolio]
|
||||
portfolioSelectorController.selectAccount(accountId)
|
||||
} else {
|
||||
logAccountSelector(isAccountMode)
|
||||
navigation.replaceAll(AddToPortfolioRoutes.PortfolioSelector)
|
||||
}
|
||||
|
||||
// main flow that combine all require data
|
||||
val allRequireForAdd = combine(
|
||||
flow = firstSelectedNetwork,
|
||||
flow2 = firstSelectedPortfolio,
|
||||
transform = { a, b -> a to b },
|
||||
)
|
||||
|
||||
// suspend until all required data is selected
|
||||
val (selectedNetworkValue, selectedPortfolioValue) = allRequireForAdd.first()
|
||||
|
||||
val isTokenAlreadyAdded = getAccountCurrencyStatusUseCase.invokeSync(
|
||||
userWalletId = selectedPortfolioValue.userWallet.walletId,
|
||||
currency = selectedNetworkValue.cryptoCurrency,
|
||||
).isSome()
|
||||
|
||||
if (isTokenAlreadyAdded) {
|
||||
finishSuccessFlow(
|
||||
currency = selectedNetworkValue.cryptoCurrency,
|
||||
userWalletId = selectedPortfolioValue.userWallet.walletId,
|
||||
)
|
||||
return@channelFlow
|
||||
}
|
||||
|
||||
sendAddTokenOpenedAnalytics(selectedNetworkValue.cryptoCurrency)
|
||||
navigation.replaceAll(AddToPortfolioRoutes.AddToken)
|
||||
val addedToken = callbackDelegate.onTokenAdded.receiveAsFlow().first()
|
||||
messageSender.send(ToastMessage(message = resourceReference(R.string.markets_token_added)))
|
||||
sendSuccessAddedAnalytics(addedToken.currency)
|
||||
finishSuccessFlow(addedToken.currency, selectedPortfolioValue.userWallet.walletId)
|
||||
}
|
||||
.catch { throwable ->
|
||||
TangemLogger.e("Error", throwable)
|
||||
params.callback.onDismiss()
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun logAccountSelector(isAccountMode: Boolean) {
|
||||
if (isAccountMode) {
|
||||
analyticsEventHandler.send(eventBuilder.popupToChooseAccount())
|
||||
}
|
||||
}
|
||||
|
||||
private fun minimalTokenMarketParams() = with(params.tokenToAdd) {
|
||||
RawMarketToken(
|
||||
id = id,
|
||||
name = name,
|
||||
symbol = symbol,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates [com.tangem.features.commonfeatures.api.AvailableToAddData] for preselected network when token is already added in all networks.
|
||||
* This allows user to select wallet/account and do smth after it with selected info.
|
||||
*/
|
||||
private suspend fun createAvailableToAddDataForPreselectedNetwork(
|
||||
preSelectedNetwork: TokenMarketInfo.Network,
|
||||
): AvailableToAddData? {
|
||||
val portfolioData = portfolioFetcher.data.firstOrNull() ?: return null
|
||||
|
||||
val availableToAddInWallets = portfolioData.balances.mapNotNull { (walletId, balance) ->
|
||||
val wallet = balance.userWallet
|
||||
val accounts = balance.accountsBalance.accountStatuses.filterCryptoPortfolio()
|
||||
|
||||
val availableToAddAccounts = accounts.mapNotNull { accountStatus ->
|
||||
val accountIndex = when (accountStatus) {
|
||||
is AccountStatus.CryptoPortfolio -> accountStatus.account.derivationIndex
|
||||
}
|
||||
|
||||
val cryptoCurrency = getTokenMarketCryptoCurrency(
|
||||
userWalletId = walletId,
|
||||
tokenMarketParams = minimalTokenMarketParams(),
|
||||
network = preSelectedNetwork,
|
||||
accountIndex = accountIndex,
|
||||
) ?: return@mapNotNull null
|
||||
|
||||
val addedNetworks = getAccountCurrencyStatusUseCase.invokeSync(
|
||||
userWalletId = walletId,
|
||||
currency = cryptoCurrency,
|
||||
).fold(
|
||||
ifEmpty = { emptySet() },
|
||||
ifSome = { setOf(it.status.currency.network) },
|
||||
)
|
||||
|
||||
AvailableToAddAccount(
|
||||
account = accountStatus,
|
||||
availableNetworks = setOf(preSelectedNetwork),
|
||||
addedNetworks = addedNetworks,
|
||||
)
|
||||
}.associateBy { it.account.account.accountId }
|
||||
|
||||
if (availableToAddAccounts.isEmpty()) return@mapNotNull null
|
||||
|
||||
walletId to AvailableToAddWallet(
|
||||
userWallet = wallet,
|
||||
accounts = accounts,
|
||||
availableNetworks = setOf(preSelectedNetwork),
|
||||
availableToAddAccounts = availableToAddAccounts,
|
||||
)
|
||||
}.toMap()
|
||||
|
||||
if (availableToAddInWallets.isEmpty()) return null
|
||||
|
||||
return AvailableToAddData(availableToAddWallets = availableToAddInWallets)
|
||||
}
|
||||
|
||||
private suspend fun createCryptoCurrency(
|
||||
userWallet: UserWallet,
|
||||
network: TokenMarketInfo.Network,
|
||||
account: AvailableToAddAccount,
|
||||
): CryptoCurrency? {
|
||||
val accountIndex = when (val accountStatus = account.account) {
|
||||
is AccountStatus.CryptoPortfolio -> accountStatus.account.derivationIndex
|
||||
is AccountStatus.Payment -> return null
|
||||
}
|
||||
return getTokenMarketCryptoCurrency(
|
||||
userWalletId = userWallet.walletId,
|
||||
tokenMarketParams = minimalTokenMarketParams(),
|
||||
network = network,
|
||||
accountIndex = accountIndex,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun createSelectedNetwork(
|
||||
network: TokenMarketInfo.Network,
|
||||
portfolio: SelectedPortfolio,
|
||||
): SelectedNetwork? {
|
||||
val cryptoCurrency = createCryptoCurrency(
|
||||
userWallet = portfolio.userWallet,
|
||||
network = network,
|
||||
account = portfolio.account,
|
||||
) ?: return null
|
||||
|
||||
return SelectedNetwork(
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
selectedNetwork = network,
|
||||
isAvailableMoreNetwork = false,
|
||||
)
|
||||
}
|
||||
|
||||
private fun setupPortfolioFlow(data: AvailableToAddData): Flow<SelectedPortfolio> = combine(
|
||||
flow = portfolioSelectorController.isAccountMode,
|
||||
flow2 = portfolioSelectorController.selectedAccount,
|
||||
transform = { isAccountMode, selectedAccountId ->
|
||||
selectedAccountId ?: return@combine null
|
||||
val availableToAddWallets =
|
||||
data.availableToAddWallets[selectedAccountId.userWalletId] ?: return@combine null
|
||||
val availableToAddAccount =
|
||||
availableToAddWallets.availableToAddAccounts[selectedAccountId] ?: return@combine null
|
||||
if (!isAccountMode) analyticsEventHandler.send(eventBuilder.addToPortfolioWalletChanged())
|
||||
SelectedPortfolio(
|
||||
isAccountMode = isAccountMode,
|
||||
userWallet = availableToAddWallets.userWallet,
|
||||
account = availableToAddAccount,
|
||||
isAvailableMorePortfolio = false,
|
||||
)
|
||||
},
|
||||
)
|
||||
.filterNotNull()
|
||||
|
||||
private fun sendSuccessAddedAnalytics(cryptoCurrency: CryptoCurrency) {
|
||||
analyticsEventHandler.send(
|
||||
EarnAnalyticsEvent.TokenAdded(
|
||||
tokenSymbol = cryptoCurrency.symbol,
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun sendAddTokenOpenedAnalytics(cryptoCurrency: CryptoCurrency) {
|
||||
analyticsEventHandler.send(
|
||||
EarnAnalyticsEvent.AddTokenScreenOpened(
|
||||
tokenSymbol = cryptoCurrency.symbol,
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
source = params.analyticsParams.source,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ModelScoped
|
||||
internal class AddToPortfolioFromEarnCallbackDelegate @Inject constructor() :
|
||||
AddTokenComponent.Callbacks {
|
||||
val onTokenAdded = Channel<CryptoCurrencyStatus>()
|
||||
|
||||
override fun onChangeNetworkClick() = Unit
|
||||
|
||||
override fun onChangePortfolioClick() = Unit
|
||||
|
||||
override fun onTokenAdded(status: CryptoCurrencyStatus) {
|
||||
onTokenAdded.trySend(status)
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,6 @@ import com.tangem.features.feed.components.feed.DefaultFeedComponent
|
|||
import com.tangem.features.feed.components.feed.DefaultFeedComponent.FeedParams
|
||||
import com.tangem.features.feed.components.market.details.DefaultMarketsTokenDetailsComponent
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioComponent
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioPreselectedDataComponent
|
||||
import com.tangem.features.feed.components.market.details.portfolio.api.MarketsPortfolioComponent
|
||||
import com.tangem.features.feed.components.market.details.portfolioblock.PortfolioBlockComponent
|
||||
import com.tangem.features.feed.components.market.list.DefaultMarketsTokenListComponent
|
||||
|
|
@ -31,7 +30,6 @@ internal class FeedEntryChildFactory @Inject constructor(
|
|||
private val portfolioComponentFactory: MarketsPortfolioComponent.Factory,
|
||||
private val portfolioBlockComponentFactory: PortfolioBlockComponent.Factory,
|
||||
private val addToPortfolioComponentFactory: AddToPortfolioComponent.Factory,
|
||||
private val addToPortfolioPreselectedDataComponent: AddToPortfolioPreselectedDataComponent.Factory,
|
||||
private val promoBannersBlockComponentFactory: PromoBannersBlockComponent.Factory,
|
||||
private val newPromoBannersFeatureToggles: NewPromoBannersFeatureToggles,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
|
|
@ -132,7 +130,7 @@ internal class FeedEntryChildFactory @Inject constructor(
|
|||
DefaultFeedComponent(
|
||||
appComponentContext = appComponentContext,
|
||||
params = FeedParams(feedClickIntents = feedEntryClickIntents),
|
||||
addToPortfolioComponentFactory = addToPortfolioPreselectedDataComponent,
|
||||
addToPortfolioComponentFactory = addToPortfolioComponentFactory,
|
||||
promoBannersBlockComponentFactory = promoBannersBlockComponentFactory,
|
||||
newPromoBannersFeatureToggles = newPromoBannersFeatureToggles,
|
||||
)
|
||||
|
|
@ -144,7 +142,7 @@ internal class FeedEntryChildFactory @Inject constructor(
|
|||
onBackClick = onBackClicked,
|
||||
onSearchClicked = feedEntryClickIntents::openSearch,
|
||||
),
|
||||
addToPortfolioComponentFactory = addToPortfolioPreselectedDataComponent,
|
||||
addToPortfolioComponentFactory = addToPortfolioComponentFactory,
|
||||
)
|
||||
}
|
||||
is Child.Search -> DefaultSearchComponent(
|
||||
|
|
|
|||
|
|
@ -33,9 +33,10 @@ 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.features.feed.components.feed.FeedBottomSheetRoute
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioPreselectedDataComponent
|
||||
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
|
||||
|
|
@ -43,7 +44,7 @@ import dev.chrisbanes.haze.HazeProgressive
|
|||
internal class DefaultEarnComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
private val params: Params,
|
||||
private val addToPortfolioComponentFactory: AddToPortfolioPreselectedDataComponent.Factory,
|
||||
private val addToPortfolioComponentFactory: AddToPortfolioComponent.Factory,
|
||||
) : ComposableModularBottomSheetContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val earnModel = getOrCreateModel<EarnModel, Params>(params = params)
|
||||
|
|
@ -129,10 +130,14 @@ internal class DefaultEarnComponent(
|
|||
is FeedBottomSheetRoute.AddToPortfolio -> {
|
||||
addToPortfolioComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
params = AddToPortfolioPreselectedDataComponent.Params(
|
||||
tokenToAdd = config.tokenToAdd,
|
||||
callback = earnModel.addToPortfolioCallback,
|
||||
analyticsParams = AddToPortfolioPreselectedDataComponent.AnalyticsParams(config.source),
|
||||
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}")
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ 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.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.child
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
|
|
@ -20,8 +19,7 @@ import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
|
|||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.core.ui.decompose.ComposableModularBottomSheetContentComponent
|
||||
import com.tangem.core.ui.decompose.EmptyComposableBottomSheetComponent
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioPreselectedDataComponent
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioPreselectedDataComponent.Params
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioComponent
|
||||
import com.tangem.features.feed.model.feed.FeedComponentModel
|
||||
import com.tangem.features.feed.model.feed.FeedModelClickIntents
|
||||
import com.tangem.features.feed.ui.feed.FeedList
|
||||
|
|
@ -32,7 +30,7 @@ import com.tangem.features.promobanners.api.PromoBannersBlockComponent
|
|||
internal class DefaultFeedComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
private val params: FeedParams,
|
||||
private val addToPortfolioComponentFactory: AddToPortfolioPreselectedDataComponent.Factory,
|
||||
private val addToPortfolioComponentFactory: AddToPortfolioComponent.Factory,
|
||||
private val promoBannersBlockComponentFactory: PromoBannersBlockComponent.Factory,
|
||||
private val newPromoBannersFeatureToggles: NewPromoBannersFeatureToggles,
|
||||
) : ComposableModularBottomSheetContentComponent, AppComponentContext by appComponentContext {
|
||||
|
|
@ -102,12 +100,8 @@ internal class DefaultFeedComponent(
|
|||
is FeedBottomSheetRoute.AddToPortfolio -> {
|
||||
addToPortfolioComponentFactory.create(
|
||||
context = childByContext(componentContext),
|
||||
params = Params(
|
||||
tokenToAdd = config.tokenToAdd,
|
||||
callback = feedComponentModel.addToPortfolioCallback,
|
||||
analyticsParams = AddToPortfolioPreselectedDataComponent.AnalyticsParams(
|
||||
AnalyticsParam.ScreensSources.Markets.value,
|
||||
),
|
||||
params = AddToPortfolioComponent.Params(
|
||||
addToPortfolioManager = feedComponentModel.addToPortfolioManager,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,10 @@ package com.tangem.features.feed.components.feed
|
|||
|
||||
import com.tangem.features.feed.components.earn.EarnNetworkFilterComponent
|
||||
import com.tangem.features.feed.components.earn.EarnTypeFilterComponent
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioPreselectedDataComponent
|
||||
|
||||
internal sealed interface FeedBottomSheetRoute {
|
||||
|
||||
data class AddToPortfolio(
|
||||
val tokenToAdd: AddToPortfolioPreselectedDataComponent.TokenToAdd,
|
||||
val source: String,
|
||||
) : FeedBottomSheetRoute
|
||||
|
||||
|
|
|
|||
|
|
@ -17,17 +17,18 @@ import com.tangem.domain.earn.model.EarnFilter
|
|||
import com.tangem.domain.earn.model.EarnFilterNetwork
|
||||
import com.tangem.domain.earn.model.EarnFilterType
|
||||
import com.tangem.domain.earn.usecase.*
|
||||
import com.tangem.domain.markets.RawMarketToken
|
||||
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.domain.models.wallet.UserWalletId
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
|
||||
import com.tangem.features.feed.components.earn.DefaultEarnComponent
|
||||
import com.tangem.features.feed.components.earn.EarnNetworkFilterComponent
|
||||
import com.tangem.features.feed.components.earn.EarnTypeFilterComponent
|
||||
import com.tangem.features.feed.components.feed.FeedBottomSheetRoute
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioPreselectedDataComponent
|
||||
import com.tangem.features.feed.model.earn.analytics.EarnAnalyticsEvent
|
||||
import com.tangem.features.feed.model.earn.analytics.EarnSource
|
||||
import com.tangem.features.feed.model.earn.filters.state.EarnFilterNetworkConverter
|
||||
import com.tangem.features.feed.model.earn.filters.state.EarnFilterNetworkUMConverter
|
||||
import com.tangem.features.feed.model.earn.filters.state.EarnFilterTypeConverter
|
||||
|
|
@ -48,7 +49,7 @@ import javax.inject.Inject
|
|||
|
||||
@Stable
|
||||
@ModelScoped
|
||||
@Suppress("LongParameterList")
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
internal class EarnModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
@ -62,6 +63,7 @@ internal class EarnModel @Inject constructor(
|
|||
private val appRouter: AppRouter,
|
||||
private val stateController: EarnStateController,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val addToPortfolioManagerFactory: AddToPortfolioManager.Factory,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<DefaultEarnComponent.Params>()
|
||||
|
|
@ -82,21 +84,24 @@ internal class EarnModel @Inject constructor(
|
|||
dispatchers = dispatchers,
|
||||
)
|
||||
|
||||
val bottomSheetNavigation: SlotNavigation<FeedBottomSheetRoute> = SlotNavigation()
|
||||
|
||||
val addToPortfolioCallback = object : AddToPortfolioPreselectedDataComponent.Callback {
|
||||
override fun onDismiss() = bottomSheetNavigation.dismiss()
|
||||
override fun onSuccess(addedToken: CryptoCurrency, walletId: UserWalletId) {
|
||||
bottomSheetNavigation.dismiss()
|
||||
appRouter.push(
|
||||
AppRoute.CurrencyDetails(
|
||||
userWalletId = walletId,
|
||||
currency = addedToken,
|
||||
),
|
||||
)
|
||||
}
|
||||
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()
|
||||
|
||||
val state: StateFlow<EarnUM>
|
||||
get() = stateController.uiState
|
||||
|
||||
|
|
@ -107,6 +112,39 @@ 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) {
|
||||
appRouter.push(
|
||||
AppRoute.CurrencyDetails(
|
||||
userWalletId = result.wallet.walletId,
|
||||
currency = result.addedCurrency.currency,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun subscribeOnBatchFlow() {
|
||||
|
|
@ -260,30 +298,36 @@ internal class EarnModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun onEarnTokenClick(earnTokenWithCurrency: EarnTokenWithCurrency, source: String) {
|
||||
private fun onEarnTokenClick(earnTokenWithCurrency: EarnTokenWithCurrency, source: EarnSource) {
|
||||
analyticsEventHandler.send(
|
||||
EarnAnalyticsEvent.OpportunitySelected(
|
||||
tokenSymbol = earnTokenWithCurrency.earnToken.tokenSymbol,
|
||||
blockchain = earnTokenWithCurrency.cryptoCurrency.network.name,
|
||||
source = source,
|
||||
source = source.value,
|
||||
),
|
||||
)
|
||||
bottomSheetNavigation.activate(
|
||||
FeedBottomSheetRoute.AddToPortfolio(
|
||||
tokenToAdd = AddToPortfolioPreselectedDataComponent.TokenToAdd(
|
||||
network = TokenMarketInfo.Network(
|
||||
networkId = earnTokenWithCurrency.earnToken.networkId,
|
||||
isExchangeable = false,
|
||||
contractAddress = earnTokenWithCurrency.earnToken.tokenAddress,
|
||||
decimalCount = earnTokenWithCurrency.earnToken.decimalCount,
|
||||
),
|
||||
id = CryptoCurrency.RawID(earnTokenWithCurrency.earnToken.tokenId),
|
||||
name = earnTokenWithCurrency.earnToken.tokenName,
|
||||
symbol = earnTokenWithCurrency.earnToken.tokenSymbol,
|
||||
),
|
||||
source = source,
|
||||
),
|
||||
val token = RawMarketToken(
|
||||
id = CryptoCurrency.RawID(earnTokenWithCurrency.earnToken.tokenId),
|
||||
name = earnTokenWithCurrency.earnToken.tokenName,
|
||||
symbol = earnTokenWithCurrency.earnToken.tokenSymbol,
|
||||
)
|
||||
val network = TokenMarketInfo.Network(
|
||||
networkId = earnTokenWithCurrency.earnToken.networkId,
|
||||
isExchangeable = false,
|
||||
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))
|
||||
}
|
||||
}
|
||||
bottomSheetNavigation.activate(FeedBottomSheetRoute.AddToPortfolio(source.value))
|
||||
}
|
||||
|
||||
private fun onTypeFilterOptionSelected(type: EarnFilterType) {
|
||||
|
|
|
|||
|
|
@ -62,9 +62,10 @@ internal sealed class EarnAnalyticsEvent(
|
|||
)
|
||||
}
|
||||
|
||||
internal const val BEST_OPPORTUNITIES_SOURCE = "Best Opportunity"
|
||||
internal const val MOSTLY_USED_SOURCE = "Mostly Used"
|
||||
|
||||
internal enum class EarnSource(val value: String) {
|
||||
BEST_OPPORTUNITIES_SOURCE("Best Opportunity"),
|
||||
MOSTLY_USED_SOURCE("Mostly Used"),
|
||||
}
|
||||
internal enum class FilterNetworkAnalytic(val value: String) {
|
||||
ALL_NETWORKS("All Networks"),
|
||||
MY_NETWORKS("My Networks"),
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.features.feed.model.earn.state.transformers
|
|||
import com.tangem.domain.models.earn.EarnTokenWithCurrency
|
||||
import com.tangem.domain.models.earn.EarnTopToken
|
||||
import com.tangem.features.feed.model.converter.EarnTokenWithCurrencyToListItemUMConverter
|
||||
import com.tangem.features.feed.model.earn.analytics.MOSTLY_USED_SOURCE
|
||||
import com.tangem.features.feed.model.earn.analytics.EarnSource
|
||||
import com.tangem.features.feed.ui.earn.state.EarnListUM
|
||||
import com.tangem.features.feed.ui.earn.state.EarnUM
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
|
@ -11,12 +11,12 @@ import java.math.BigDecimal
|
|||
|
||||
internal class UpdateMostlyUsedStateTransformer(
|
||||
private val earnResult: EarnTopToken?,
|
||||
private val onItemClick: (EarnTokenWithCurrency, source: String) -> Unit,
|
||||
private val onItemClick: (EarnTokenWithCurrency, source: EarnSource) -> Unit,
|
||||
private val onRetryClick: () -> Unit,
|
||||
) : EarnUMTransformer {
|
||||
|
||||
private val converter = EarnTokenWithCurrencyToListItemUMConverter(
|
||||
onItemClick = { token -> onItemClick(token, MOSTLY_USED_SOURCE) },
|
||||
onItemClick = { token -> onItemClick(token, EarnSource.MOSTLY_USED_SOURCE) },
|
||||
)
|
||||
|
||||
override fun transform(prevState: EarnUM): EarnUM {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import com.tangem.domain.earn.model.EarnTokensListConfig
|
|||
import com.tangem.domain.earn.usecase.GetEarnTokensBatchFlowUseCase
|
||||
import com.tangem.domain.models.earn.EarnTokenWithCurrency
|
||||
import com.tangem.features.feed.model.converter.EarnTokenWithCurrencyToListItemUMConverter
|
||||
import com.tangem.features.feed.model.earn.analytics.BEST_OPPORTUNITIES_SOURCE
|
||||
import com.tangem.features.feed.model.earn.analytics.EarnSource
|
||||
import com.tangem.features.feed.ui.earn.state.EarnListItemUM
|
||||
import com.tangem.pagination.BatchAction
|
||||
import com.tangem.pagination.PaginationStatus
|
||||
|
|
@ -21,13 +21,13 @@ import kotlinx.coroutines.launch
|
|||
internal class EarnListBatchFlowManager(
|
||||
getEarnTokensBatchFlowUseCase: GetEarnTokensBatchFlowUseCase,
|
||||
private val configProvider: Provider<EarnTokensListConfig>,
|
||||
private val onItemClick: (EarnTokenWithCurrency, source: String) -> Unit,
|
||||
private val onItemClick: (EarnTokenWithCurrency, source: EarnSource) -> Unit,
|
||||
private val modelScope: CoroutineScope,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
private val actionsFlow = MutableSharedFlow<BatchAction<Int, EarnTokensListConfig, Nothing>>()
|
||||
private val converter = EarnTokenWithCurrencyToListItemUMConverter(
|
||||
onItemClick = { onItemClick(it, BEST_OPPORTUNITIES_SOURCE) },
|
||||
onItemClick = { onItemClick(it, EarnSource.BEST_OPPORTUNITIES_SOURCE) },
|
||||
)
|
||||
|
||||
private val batchFlow = getEarnTokensBatchFlowUseCase(
|
||||
|
|
|
|||
|
|
@ -20,18 +20,14 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.earn.usecase.FetchTopEarnTokensUseCase
|
||||
import com.tangem.domain.earn.usecase.GetTopEarnTokensUseCase
|
||||
import com.tangem.domain.markets.GetTopFiveMarketTokenUseCase
|
||||
import com.tangem.domain.markets.TokenMarketInfo
|
||||
import com.tangem.domain.markets.TokenMarketListConfig
|
||||
import com.tangem.domain.markets.toSerializableParam
|
||||
import com.tangem.domain.markets.*
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.earn.EarnTokenWithCurrency
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.news.usecase.FetchTrendingNewsUseCase
|
||||
import com.tangem.domain.news.usecase.ManageTrendingNewsUseCase
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
|
||||
import com.tangem.features.feed.components.feed.DefaultFeedComponent
|
||||
import com.tangem.features.feed.components.feed.FeedBottomSheetRoute
|
||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioPreselectedDataComponent
|
||||
import com.tangem.features.feed.impl.R
|
||||
import com.tangem.features.feed.model.earn.analytics.EarnAnalyticsEvent
|
||||
import com.tangem.features.feed.model.feed.analytics.FeedAnalyticsEvent
|
||||
|
|
@ -66,6 +62,7 @@ internal class FeedComponentModel @Inject constructor(
|
|||
private val getTopEarnTokensUseCase: GetTopEarnTokensUseCase,
|
||||
private val appRouter: AppRouter,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
addToPortfolioManagerFactory: AddToPortfolioManager.Factory,
|
||||
getTopFiveMarketTokenUseCase: GetTopFiveMarketTokenUseCase,
|
||||
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
paramsContainer: ParamsContainer,
|
||||
|
|
@ -90,21 +87,16 @@ internal class FeedComponentModel @Inject constructor(
|
|||
dispatchers = dispatchers,
|
||||
)
|
||||
|
||||
val bottomSheetNavigation: SlotNavigation<FeedBottomSheetRoute> = SlotNavigation()
|
||||
|
||||
val addToPortfolioCallback = object : AddToPortfolioPreselectedDataComponent.Callback {
|
||||
override fun onDismiss() = bottomSheetNavigation.dismiss()
|
||||
override fun onSuccess(addedToken: CryptoCurrency, walletId: UserWalletId) {
|
||||
bottomSheetNavigation.dismiss()
|
||||
appRouter.push(
|
||||
AppRoute.CurrencyDetails(
|
||||
userWalletId = walletId,
|
||||
currency = addedToken,
|
||||
),
|
||||
)
|
||||
}
|
||||
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()
|
||||
|
||||
val state: StateFlow<FeedListUM>
|
||||
get() = stateController.uiState
|
||||
|
||||
|
|
@ -118,6 +110,27 @@ 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) {
|
||||
appRouter.push(
|
||||
AppRoute.CurrencyDetails(
|
||||
userWalletId = result.wallet.walletId,
|
||||
currency = result.addedCurrency.currency,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun subscribeOnDataState() {
|
||||
|
|
@ -407,22 +420,23 @@ internal class FeedComponentModel @Inject constructor(
|
|||
source = AnalyticsParam.ScreensSources.Markets.value,
|
||||
),
|
||||
)
|
||||
bottomSheetNavigation.activate(
|
||||
FeedBottomSheetRoute.AddToPortfolio(
|
||||
tokenToAdd = AddToPortfolioPreselectedDataComponent.TokenToAdd(
|
||||
network = TokenMarketInfo.Network(
|
||||
networkId = earnTokenWithCurrency.earnToken.networkId,
|
||||
isExchangeable = false,
|
||||
contractAddress = earnTokenWithCurrency.earnToken.tokenAddress,
|
||||
decimalCount = earnTokenWithCurrency.earnToken.decimalCount,
|
||||
),
|
||||
id = CryptoCurrency.RawID(earnTokenWithCurrency.earnToken.tokenId),
|
||||
name = earnTokenWithCurrency.earnToken.tokenName,
|
||||
symbol = earnTokenWithCurrency.earnToken.tokenSymbol,
|
||||
),
|
||||
source = AnalyticsParam.ScreensSources.Markets.value,
|
||||
),
|
||||
val token = RawMarketToken(
|
||||
id = CryptoCurrency.RawID(earnTokenWithCurrency.earnToken.tokenId),
|
||||
name = earnTokenWithCurrency.earnToken.tokenName,
|
||||
symbol = earnTokenWithCurrency.earnToken.tokenSymbol,
|
||||
)
|
||||
val network = TokenMarketInfo.Network(
|
||||
networkId = earnTokenWithCurrency.earnToken.networkId,
|
||||
isExchangeable = false,
|
||||
contractAddress = earnTokenWithCurrency.earnToken.tokenAddress,
|
||||
decimalCount = earnTokenWithCurrency.earnToken.decimalCount,
|
||||
)
|
||||
val route = FeedBottomSheetRoute.AddToPortfolio(AnalyticsParam.ScreensSources.Markets.value)
|
||||
addToPortfolioManager.apply {
|
||||
setTokenParams(token)
|
||||
setTokenNetworks(listOf(network))
|
||||
}
|
||||
bottomSheetNavigation.activate(route)
|
||||
}
|
||||
|
||||
private fun handleEarnPageOpenClicked() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue