Updated on 2026-08-14

This commit is contained in:
Tangem 2025-02-25 09:46:39 +03:00
commit ec15925bce
225 changed files with 5402 additions and 3982 deletions

View file

@ -0,0 +1,78 @@
package com.tangem.feature.tokendetails
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.arkivanov.essenty.lifecycle.subscribe
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.deeplink.DeepLinksRegistry
import com.tangem.core.deeplink.global.BuyCurrencyDeepLink
import com.tangem.core.deeplink.utils.registerDeepLinks
import com.tangem.core.ui.components.NavigationBar3ButtonsScrim
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsModel
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.TokenDetailsScreen
import com.tangem.features.markets.token.block.TokenMarketBlockComponent
import com.tangem.features.tokendetails.TokenDetailsComponent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
internal class DefaultTokenDetailsComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted params: TokenDetailsComponent.Params,
tokenMarketBlockComponentFactory: TokenMarketBlockComponent.Factory,
deepLinksRegistry: DeepLinksRegistry,
) : TokenDetailsComponent, AppComponentContext by appComponentContext {
private val model: TokenDetailsModel = getOrCreateModel(params)
init {
lifecycle.subscribe(
onPause = model::onPause,
onResume = model::onResume,
)
registerDeepLinks(
registry = deepLinksRegistry,
BuyCurrencyDeepLink(
onReceive = model::onBuyCurrencyDeepLink,
),
)
}
private val tokenMarketBlockComponent = params.currency.toTokenMarketParam()?.let { tokenMarketParams ->
tokenMarketBlockComponentFactory.create(
appComponentContext = child("tokenMarketBlockComponent"),
params = tokenMarketParams,
)
}
@Composable
override fun Content(modifier: Modifier) {
val state by model.uiState.collectAsStateWithLifecycle()
NavigationBar3ButtonsScrim()
TokenDetailsScreen(
state = state,
tokenMarketBlockComponent = tokenMarketBlockComponent,
)
}
private fun CryptoCurrency.toTokenMarketParam(): TokenMarketBlockComponent.Params? {
id.rawCurrencyId ?: return null // token price is not available
return TokenMarketBlockComponent.Params(cryptoCurrency = this)
}
@AssistedFactory
interface Factory : TokenDetailsComponent.Factory {
override fun create(
context: AppComponentContext,
params: TokenDetailsComponent.Params,
): DefaultTokenDetailsComponent
}
}

View file

@ -0,0 +1,38 @@
package com.tangem.feature.tokendetails.di
import com.tangem.core.decompose.di.ComponentScoped
import com.tangem.core.decompose.di.DecomposeComponent
import com.tangem.core.decompose.model.Model
import com.tangem.feature.tokendetails.DefaultTokenDetailsComponent
import com.tangem.feature.tokendetails.presentation.router.DefaultTokenDetailsRouter
import com.tangem.feature.tokendetails.presentation.router.InnerTokenDetailsRouter
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsModel
import com.tangem.features.tokendetails.TokenDetailsComponent
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
@Module
@InstallIn(SingletonComponent::class)
internal interface TokenDetailsModule {
@Binds
fun bindComponentFactory(factory: DefaultTokenDetailsComponent.Factory): TokenDetailsComponent.Factory
@Binds
@IntoMap
@ClassKey(TokenDetailsModel::class)
fun bindModel(model: TokenDetailsModel): Model
}
@Module
@InstallIn(DecomposeComponent::class)
internal interface StakingComponentModule {
@Binds
@ComponentScoped
fun bindRouter(impl: DefaultTokenDetailsRouter): InnerTokenDetailsRouter
}

View file

@ -1,27 +0,0 @@
package com.tangem.feature.tokendetails.di
import com.tangem.common.routing.AppRouter
import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.feature.tokendetails.presentation.router.DefaultTokenDetailsRouter
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityComponent
import dagger.hilt.android.scopes.ActivityScoped
@Module
@InstallIn(ActivityComponent::class)
internal object TokenDetailsRouterModule {
@Provides
@ActivityScoped
fun provideTokenDetailsRouter(
appRouter: AppRouter,
urlOpener: UrlOpener,
shareManager: ShareManager,
): TokenDetailsRouter {
return DefaultTokenDetailsRouter(appRouter, urlOpener, shareManager)
}
}

View file

@ -1,105 +0,0 @@
package com.tangem.feature.tokendetails.presentation
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.fragment.app.viewModels
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.arkivanov.decompose.defaultComponentContext
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.common.routing.bundle.unbundle
import com.tangem.common.routing.utils.asRouter
import com.tangem.core.decompose.context.DefaultAppComponentContext
import com.tangem.core.decompose.di.DecomposeComponent
import com.tangem.core.decompose.di.GlobalUiMessageSender
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.UiDependencies
import com.tangem.core.ui.components.NavigationBar3ButtonsScrim
import com.tangem.core.ui.screen.ComposeFragment
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.feature.tokendetails.presentation.router.InnerTokenDetailsRouter
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.TokenDetailsScreen
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsViewModel
import com.tangem.features.markets.token.block.TokenMarketBlockComponent
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@AndroidEntryPoint
internal class TokenDetailsFragment : ComposeFragment() {
@Inject
override lateinit var uiDependencies: UiDependencies
@Inject
@GlobalUiMessageSender
internal lateinit var messageSender: UiMessageSender
@Inject
internal lateinit var tokenDetailsRouter: TokenDetailsRouter
@Inject
internal lateinit var coroutineDispatcherProvider: CoroutineDispatcherProvider
@Inject
internal lateinit var componentBuilder: DecomposeComponent.Builder
@Inject
internal lateinit var tokenMarketBlockComponentFactory: TokenMarketBlockComponent.Factory
@Inject
internal lateinit var appRouter: AppRouter
private val viewModel by viewModels<TokenDetailsViewModel>()
private var tokenMarketBlockComponent: TokenMarketBlockComponent? = null
private val internalTokenDetailsRouter: InnerTokenDetailsRouter
get() = requireNotNull(tokenDetailsRouter as? InnerTokenDetailsRouter) {
"internalTokenDetailsRouter should be instance of InnerTokenDetailsRouter"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.router = internalTokenDetailsRouter
lifecycle.addObserver(viewModel)
val cryptoCurrency: CryptoCurrency = arguments
?.getBundle(AppRoute.CurrencyDetails.CRYPTO_CURRENCY_KEY)
?.unbundle(CryptoCurrency.serializer())
?: error("Token Details screen can't be opened without `CryptoCurrency`")
val param = cryptoCurrency.toParam() ?: return
val appContext = DefaultAppComponentContext(
componentContext = defaultComponentContext(requireActivity().onBackPressedDispatcher),
messageSender = messageSender,
dispatchers = coroutineDispatcherProvider,
hiltComponentBuilder = componentBuilder,
replaceRouter = appRouter.asRouter(),
)
tokenMarketBlockComponent = tokenMarketBlockComponentFactory.create(
appComponentContext = appContext,
params = param,
)
}
private fun CryptoCurrency.toParam(): TokenMarketBlockComponent.Params? {
id.rawCurrencyId ?: return null // token price is not available
return TokenMarketBlockComponent.Params(cryptoCurrency = this)
}
@Composable
override fun ScreenContent(modifier: Modifier) {
NavigationBar3ButtonsScrim()
TokenDetailsScreen(
state = viewModel.uiState.collectAsStateWithLifecycle().value,
tokenMarketBlockComponent = tokenMarketBlockComponent,
)
}
}

View file

@ -1,23 +1,22 @@
package com.tangem.feature.tokendetails.presentation.router
import androidx.fragment.app.Fragment
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.core.decompose.di.ComponentScoped
import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.tokendetails.presentation.TokenDetailsFragment
import javax.inject.Inject
internal class DefaultTokenDetailsRouter(
@ComponentScoped
internal class DefaultTokenDetailsRouter @Inject constructor(
private val router: AppRouter,
private val urlOpener: UrlOpener,
private val shareManager: ShareManager,
) : InnerTokenDetailsRouter {
override fun getEntryFragment(): Fragment = TokenDetailsFragment()
override fun popBackStack() {
router.pop()
}

View file

@ -3,9 +3,8 @@ package com.tangem.feature.tokendetails.presentation.router
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
internal interface InnerTokenDetailsRouter : TokenDetailsRouter {
internal interface InnerTokenDetailsRouter {
/** Pop back stack */
fun popBackStack()

View file

@ -1,4 +1,4 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels
package com.tangem.feature.tokendetails.presentation.tokendetails.model
import com.tangem.core.ui.components.bottomsheets.tokenreceive.AddressModel
import com.tangem.core.ui.extensions.TextReference

View file

@ -1,21 +1,20 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels
package com.tangem.feature.tokendetails.presentation.tokendetails.model
import android.os.Bundle
import androidx.lifecycle.*
import androidx.compose.runtime.Stable
import androidx.paging.cachedIn
import arrow.core.getOrElse
import com.tangem.blockchain.common.address.AddressType
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.common.routing.bundle.unbundle
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.decompose.di.ComponentScoped
import com.tangem.core.decompose.di.GlobalUiMessageSender
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.deeplink.DeepLinksRegistry
import com.tangem.core.deeplink.global.BuyCurrencyDeepLink
import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.ui.clipboard.ClipboardManager
import com.tangem.core.ui.components.bottomsheets.tokenreceive.AddressModel
@ -75,10 +74,10 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDeta
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsStateFactory
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.express.ExpressStatusFactory
import com.tangem.features.onramp.OnrampFeatureToggles
import com.tangem.features.tokendetails.TokenDetailsComponent
import com.tangem.features.tokendetails.impl.R
import com.tangem.utils.Provider
import com.tangem.utils.coroutines.*
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.async
@ -89,9 +88,10 @@ import timber.log.Timber
import javax.inject.Inject
@Suppress("LongParameterList", "LargeClass", "TooManyFunctions")
@HiltViewModel
internal class TokenDetailsViewModel @Inject constructor(
private val dispatchers: CoroutineDispatcherProvider,
@Stable
@ComponentScoped
internal class TokenDetailsModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase,
@ -122,26 +122,19 @@ internal class TokenDetailsViewModel @Inject constructor(
private val onrampFeatureToggles: OnrampFeatureToggles,
private val shareManager: ShareManager,
@GlobalUiMessageSender private val uiMessageSender: UiMessageSender,
paramsContainer: ParamsContainer,
expressStatusFactory: ExpressStatusFactory.Factory,
getUserWalletUseCase: GetUserWalletUseCase,
getStakingIntegrationIdUseCase: GetStakingIntegrationIdUseCase,
deepLinksRegistry: DeepLinksRegistry,
savedStateHandle: SavedStateHandle,
private val appRouter: AppRouter,
) : ViewModel(), DefaultLifecycleObserver, TokenDetailsClickIntents {
private val router: InnerTokenDetailsRouter,
) : Model(), TokenDetailsClickIntents {
private val userWalletId: UserWalletId = savedStateHandle.get<Bundle>(AppRoute.CurrencyDetails.USER_WALLET_ID_KEY)
?.unbundle(UserWalletId.serializer())
?: error("This screen can't be opened without `UserWalletId`")
private val params = paramsContainer.require<TokenDetailsComponent.Params>()
private val userWalletId: UserWalletId = params.userWalletId
private val cryptoCurrency: CryptoCurrency = params.currency
private val cryptoCurrency: CryptoCurrency =
savedStateHandle.get<Bundle>(AppRoute.CurrencyDetails.CRYPTO_CURRENCY_KEY)
?.unbundle(CryptoCurrency.serializer())
?: error("This screen can't be opened without `CryptoCurrency`")
private val userWallet: UserWallet
lateinit var router: InnerTokenDetailsRouter
private val userWallet: UserWallet = getUserWalletUseCase(userWalletId).getOrNull() ?: error("UserWallet not found")
private val marketPriceJobHolder = JobHolder()
private val refreshStateJobHolder = JobHolder()
@ -195,18 +188,16 @@ internal class TokenDetailsViewModel @Inject constructor(
val uiState: StateFlow<TokenDetailsState> = internalUiState
init {
deepLinksRegistry.registerWithViewModel(
viewModel = this,
deepLinks = listOf(
BuyCurrencyDeepLink(
onReceive = ::onBuyCurrencyDeepLink,
),
),
analyticsEventsHandler.send(
event = TokenScreenAnalyticsEvent.DetailsScreenOpened(token = cryptoCurrency.symbol),
)
userWallet = getUserWalletUseCase(userWalletId).getOrNull() ?: error("UserWallet not found")
updateTopBarMenu()
initButtons()
updateContent()
handleBalanceHiding()
}
private fun onBuyCurrencyDeepLink(externalTxId: String) {
fun onBuyCurrencyDeepLink(externalTxId: String) {
if (onrampFeatureToggles.isFeatureEnabled) {
router.openOnrampSuccess(externalTxId)
} else {
@ -215,36 +206,24 @@ internal class TokenDetailsViewModel @Inject constructor(
}
}
override fun onCreate(owner: LifecycleOwner) {
analyticsEventsHandler.send(
event = TokenScreenAnalyticsEvent.DetailsScreenOpened(token = cryptoCurrency.symbol),
)
updateTopBarMenu()
initButtons()
updateContent()
handleBalanceHiding(owner)
}
override fun onPause(owner: LifecycleOwner) {
fun onPause() {
expressTxStatusTaskScheduler.cancelTask()
expressTxJobHolder.cancel()
super.onPause(owner)
}
override fun onResume(owner: LifecycleOwner) {
fun onResume() {
subscribeOnExpressTransactionsUpdates()
super.onResume(owner)
}
override fun onCleared() {
override fun onDestroy() {
expressTxStatusTaskScheduler.cancelTask()
expressTxJobHolder.cancel()
super.onCleared()
super.onDestroy()
}
private fun initButtons() {
// we need also init buttons before start all loading to avoid buttons blocking
viewModelScope.launch {
modelScope.launch {
val currentCryptoCurrencyStatus = getCryptoCurrencySyncUseCase.invoke(
userWalletId = userWalletId,
cryptoCurrencyId = cryptoCurrency.id,
@ -266,15 +245,14 @@ internal class TokenDetailsViewModel @Inject constructor(
updateStakingInfo()
}
private fun handleBalanceHiding(owner: LifecycleOwner) {
private fun handleBalanceHiding() {
getBalanceHidingSettingsUseCase()
.flowWithLifecycle(owner.lifecycle)
.onEach {
internalUiState.value = stateFactory.getStateWithUpdatedHidden(
isBalanceHidden = it.isBalanceHidden,
)
}
.launchIn(viewModelScope)
.launchIn(modelScope)
}
private suspend fun updateButtons(currencyStatus: CryptoCurrencyStatus) {
@ -288,11 +266,11 @@ internal class TokenDetailsViewModel @Inject constructor(
internalUiState.value = stateFactory.getManageButtonsState(actions = it.states)
}
.flowOn(dispatchers.main)
.launchIn(viewModelScope)
.launchIn(modelScope)
}
private fun updateWarnings(cryptoCurrencyStatus: CryptoCurrencyStatus) {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
getCurrencyWarningsUseCase.invoke(
userWalletId = userWalletId,
currencyStatus = cryptoCurrencyStatus,
@ -305,13 +283,13 @@ internal class TokenDetailsViewModel @Inject constructor(
notificationsAnalyticsSender.send(internalUiState.value, updatedState.notifications)
internalUiState.value = updatedState
}
.launchIn(viewModelScope)
.launchIn(modelScope)
.saveIn(warningsJobHolder)
}
}
private fun subscribeOnCurrencyStatusUpdates() {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
getCurrencyStatusUpdatesUseCase(
userWalletId = userWalletId,
currencyId = cryptoCurrency.id,
@ -328,13 +306,13 @@ internal class TokenDetailsViewModel @Inject constructor(
currencyStatusAnalyticsSender.send(maybeCurrencyStatus)
}
.flowOn(dispatchers.main)
.launchIn(viewModelScope)
.launchIn(modelScope)
.saveIn(marketPriceJobHolder)
}
}
private fun subscribeOnExpressTransactionsUpdates() {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
expressTxStatusTaskScheduler.cancelTask()
expressStatusFactory
.getExpressStatuses()
@ -345,7 +323,7 @@ internal class TokenDetailsViewModel @Inject constructor(
::updateNetworkToSwapBalance,
)
expressTxStatusTaskScheduler.scheduleTask(
viewModelScope,
modelScope,
PeriodicTask(
isDelayFirst = false,
delay = EXPRESS_STATUS_UPDATE_DELAY,
@ -365,13 +343,13 @@ internal class TokenDetailsViewModel @Inject constructor(
)
}
.flowOn(dispatchers.main)
.launchIn(viewModelScope)
.launchIn(modelScope)
.saveIn(expressTxJobHolder)
}
}
private fun updateNetworkToSwapBalance(toCryptoCurrency: CryptoCurrency) {
viewModelScope.launch {
modelScope.launch {
updateDelayedCurrencyStatusUseCase(
userWalletId = userWalletId,
network = toCryptoCurrency.network,
@ -385,7 +363,7 @@ internal class TokenDetailsViewModel @Inject constructor(
* @param showItemsLoading - show loading items placeholder.
*/
private fun updateTxHistory(refresh: Boolean, showItemsLoading: Boolean) {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
val txHistoryItemsCountEither = txHistoryItemsCountUseCase(
userWalletId = userWalletId,
currency = cryptoCurrency,
@ -404,7 +382,7 @@ internal class TokenDetailsViewModel @Inject constructor(
userWalletId = userWalletId,
currency = cryptoCurrency,
refresh = refresh,
).map { it.cachedIn(viewModelScope) }
).map { it.cachedIn(modelScope) }
internalUiState.value = stateFactory.getLoadedTxHistoryState(maybeTxHistory)
}
@ -412,7 +390,7 @@ internal class TokenDetailsViewModel @Inject constructor(
}
private fun updateStakingInfo() {
viewModelScope.launch {
modelScope.launch {
val availability = getStakingAvailabilityUseCase(
userWalletId = userWalletId,
cryptoCurrency = cryptoCurrency,
@ -432,7 +410,7 @@ internal class TokenDetailsViewModel @Inject constructor(
}
private fun updateTopBarMenu() {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
val hasDerivations =
networkHasDerivationUseCase(userWallet.scanResponse, cryptoCurrency.network).getOrElse { false }
@ -452,7 +430,7 @@ internal class TokenDetailsViewModel @Inject constructor(
maybeAppCurrency.getOrElse { AppCurrency.Default }
}
.stateIn(
scope = viewModelScope,
scope = modelScope,
started = SharingStarted.Eagerly,
initialValue = AppCurrency.Default,
)
@ -476,7 +454,7 @@ internal class TokenDetailsViewModel @Inject constructor(
val status = cryptoCurrencyStatus ?: return
if (onrampFeatureToggles.isFeatureEnabled) {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
reduxStateHolder.dispatch(
TradeCryptoAction.Buy(
userWallet = userWallet,
@ -488,7 +466,7 @@ internal class TokenDetailsViewModel @Inject constructor(
}
} else {
showErrorIfDemoModeOrElse {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
reduxStateHolder.dispatch(
TradeCryptoAction.Buy(
userWallet = userWallet,
@ -554,7 +532,7 @@ internal class TokenDetailsViewModel @Inject constructor(
return
}
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonReceive(cryptoCurrency.symbol))
analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ReceiveScreenOpened(cryptoCurrency.symbol))
@ -587,7 +565,7 @@ internal class TokenDetailsViewModel @Inject constructor(
}
override fun onGenerateExtendedKey() {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
val extendedKey = getExtendedPublicKeyForCurrencyUseCase(
userWalletId,
cryptoCurrency.network,
@ -661,7 +639,7 @@ internal class TokenDetailsViewModel @Inject constructor(
override fun onHideClick() {
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonRemoveToken(cryptoCurrency.symbol))
viewModelScope.launch {
modelScope.launch {
val hasLinkedTokens = removeCurrencyUseCase.hasLinkedTokens(userWalletId, cryptoCurrency)
internalUiState.value = if (hasLinkedTokens) {
stateFactory.getStateWithLinkedTokensDialog(cryptoCurrency)
@ -672,7 +650,7 @@ internal class TokenDetailsViewModel @Inject constructor(
}
override fun onHideConfirmed() {
viewModelScope.launch {
modelScope.launch {
removeCurrencyUseCase.invoke(userWalletId, cryptoCurrency)
.onLeft { Timber.e(it) }
.onRight { router.popBackStack() }
@ -687,7 +665,7 @@ internal class TokenDetailsViewModel @Inject constructor(
private fun openExplorer() {
val currencyStatus = cryptoCurrencyStatus ?: return
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
when (val addresses = currencyStatus.value.networkAddress) {
is NetworkAddress.Selectable -> {
internalUiState.value = stateFactory.getStateWithChooseAddressBottomSheet(cryptoCurrency, addresses)
@ -719,7 +697,7 @@ internal class TokenDetailsViewModel @Inject constructor(
}
override fun onAddressTypeSelected(addressModel: AddressModel) {
viewModelScope.launch {
modelScope.launch {
router.openUrl(
url = getExploreUrlUseCase(
userWalletId = userWalletId,
@ -744,7 +722,7 @@ internal class TokenDetailsViewModel @Inject constructor(
override fun onRefreshSwipe(isRefreshing: Boolean) {
internalUiState.value = stateFactory.getRefreshingState()
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
listOf(
async {
fetchCurrencyStatusUseCase(
@ -768,7 +746,7 @@ internal class TokenDetailsViewModel @Inject constructor(
override fun onDismissBottomSheet() {
when (val bsContent = internalUiState.value.bottomSheetConfig?.content) {
is ExpressStatusBottomSheetConfig -> {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
expressStatusFactory.removeTransactionOnBottomSheetClosed(bsContent.value)
}
}
@ -798,7 +776,7 @@ internal class TokenDetailsViewModel @Inject constructor(
}
override fun onSwapPromoDismiss() {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
shouldShowSwapPromoTokenUseCase.neverToShow()
analyticsEventsHandler.send(
TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
@ -811,7 +789,7 @@ internal class TokenDetailsViewModel @Inject constructor(
}
override fun onSwapPromoClick() {
viewModelScope.launch(dispatchers.main) {
modelScope.launch(dispatchers.main) {
shouldShowSwapPromoTokenUseCase.neverToShow()
analyticsEventsHandler.send(
TokenSwapPromoAnalyticsEvent.PromotionBannerClicked(
@ -842,7 +820,7 @@ internal class TokenDetailsViewModel @Inject constructor(
blockchain = cryptoCurrency.network.name,
),
)
viewModelScope.launch {
modelScope.launch {
retryIncompleteTransactionUseCase(
userWalletId = userWalletId,
currency = cryptoCurrency,
@ -883,13 +861,13 @@ internal class TokenDetailsViewModel @Inject constructor(
blockchain = cryptoCurrency.network.name,
),
)
viewModelScope.launch {
modelScope.launch {
internalUiState.value = stateFactory.getStateWithDismissIncompleteTransactionConfirmDialog()
}
}
override fun onConfirmDismissIncompleteTransactionClick() {
viewModelScope.launch {
modelScope.launch {
dismissIncompleteTransactionUseCase(
userWalletId = userWalletId,
currency = cryptoCurrency,
@ -914,7 +892,7 @@ internal class TokenDetailsViewModel @Inject constructor(
blockchain = cryptoCurrency.network.name,
),
)
viewModelScope.launch(dispatchers.io) {
modelScope.launch(dispatchers.io) {
associateAssetUseCase(
userWalletId = userWalletId,
currency = cryptoCurrency,
@ -953,7 +931,7 @@ internal class TokenDetailsViewModel @Inject constructor(
override fun onDisposeExpressStatus() {
val bottomSheetState = internalUiState.value.bottomSheetConfig?.content
if (bottomSheetState is ExpressStatusBottomSheetConfig) {
viewModelScope.launch {
modelScope.launch {
expressStatusFactory.removeTransactionOnBottomSheetClosed(
expressState = bottomSheetState.value,
isForceDispose = true,
@ -985,7 +963,7 @@ internal class TokenDetailsViewModel @Inject constructor(
}
private fun openStaking() {
viewModelScope.launch {
modelScope.launch {
val yield = getYieldUseCase.invoke(
cryptoCurrencyId = cryptoCurrency.id,
symbol = cryptoCurrency.symbol,

View file

@ -4,7 +4,7 @@ import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
import com.tangem.domain.tokens.model.TokenActionsState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsActionButton
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.utils.Provider
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.ImmutableList

View file

@ -23,7 +23,7 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.*
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsTxHistoryTransactionStateConverter
import com.tangem.feature.tokendetails.presentation.tokendetails.state.utils.getBalance
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.features.tokendetails.impl.R
import com.tangem.lib.crypto.BlockchainUtils.isBSC
import com.tangem.lib.crypto.BlockchainUtils.isSolana

View file

@ -13,7 +13,7 @@ import com.tangem.domain.tokens.model.warnings.KaspaWarnings
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification.*
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.features.tokendetails.impl.R
import com.tangem.utils.converter.Converter
import com.tangem.utils.extensions.removeBy

View file

@ -26,7 +26,7 @@ import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateIconUM
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateInfoUM
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.features.tokendetails.impl.R
import com.tangem.utils.Provider
import com.tangem.utils.converter.Converter

View file

@ -16,7 +16,7 @@ import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.feature.tokendetails.presentation.tokendetails.state.*
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsActionButton
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.features.tokendetails.impl.R
import com.tangem.lib.crypto.BlockchainUtils.isBitcoin
import com.tangem.utils.converter.Converter

View file

@ -33,7 +33,7 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.component
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadedTxHistoryConverter
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadingTxHistoryConverter
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadingTxHistoryConverter.TokenDetailsLoadingTxHistoryModel
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.features.tokendetails.impl.R
import com.tangem.utils.Provider
import kotlinx.collections.immutable.toImmutableList

View file

@ -27,7 +27,7 @@ import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionModel
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.ExchangeStatusNotifications
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeStatusState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.features.tokendetails.impl.R
import com.tangem.utils.Provider
import com.tangem.utils.converter.Converter

View file

@ -18,7 +18,7 @@ import com.tangem.feature.swap.domain.models.domain.*
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsSwapTransactionsStateConverter
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.utils.Provider
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory

View file

@ -13,7 +13,7 @@ import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.swap.domain.models.domain.ExchangeStatus
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.utils.Provider
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.assisted.Assisted

View file

@ -17,7 +17,7 @@ import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsOnrampTransactionStateConverter
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.utils.Provider
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory

View file

@ -6,7 +6,7 @@ import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.domain.txhistory.models.TxHistoryListError
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.utils.Provider
import com.tangem.utils.converter.Converter
import kotlinx.coroutines.flow.Flow

View file

@ -7,7 +7,7 @@ import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.domain.txhistory.models.TxHistoryStateError
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadingTxHistoryConverter.TokenDetailsLoadingTxHistoryModel
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.utils.Provider
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.toImmutableList

View file

@ -7,7 +7,7 @@ import com.tangem.core.ui.components.transactions.state.TxHistoryState.TxHistory
import com.tangem.core.ui.utils.toDateFormatWithTodayYesterday
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.utils.Provider
import com.tangem.utils.converter.Converter
import kotlinx.coroutines.CoroutineScope

View file

@ -12,7 +12,7 @@ import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.utils.toTimeFormat
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.domain.txhistory.models.TxHistoryItem.*
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.features.tokendetails.impl.R
import com.tangem.utils.StringsSigns.MINUS
import com.tangem.utils.StringsSigns.PLUS