Updated on 2026-08-14

This commit is contained in:
Tangem 2023-11-07 13:19:09 +03:00
commit 9c868cf221
202 changed files with 4333 additions and 775 deletions

View file

@ -1,14 +1,21 @@
package com.tangem.tap
import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.view.View
import androidx.activity.viewModels
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.os.bundleOf
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
@ -16,14 +23,20 @@ import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import arrow.core.getOrElse
import by.kirich1409.viewbindingdelegate.viewBinding
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.core.ui.event.StateEvent
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.data.card.sdk.CardSdkLifecycleObserver
import com.tangem.domain.apptheme.model.AppThemeMode
import com.tangem.domain.card.ScanCardUseCase
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.features.managetokens.navigation.ManageTokensRouter
import com.tangem.features.send.api.navigation.SendRouter
import com.tangem.features.tester.api.TesterRouter
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
import com.tangem.features.wallet.navigation.WalletRouter
@ -46,12 +59,15 @@ import com.tangem.tap.features.intentHandler.handlers.BackgroundScanIntentHandle
import com.tangem.tap.features.intentHandler.handlers.BuyCurrencyIntentHandler
import com.tangem.tap.features.intentHandler.handlers.SellCurrencyIntentHandler
import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler
import com.tangem.tap.features.main.MainViewModel
import com.tangem.tap.features.main.model.Toast
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
import com.tangem.tap.features.shop.redux.ShopAction
import com.tangem.tap.features.welcome.ui.WelcomeFragment
import com.tangem.tap.proxy.AppStateHolder
import com.tangem.tap.proxy.redux.DaggerGraphAction
import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler
import com.tangem.wallet.BuildConfig
import com.tangem.wallet.R
import com.tangem.wallet.databinding.ActivityMainBinding
import dagger.hilt.android.AndroidEntryPoint
@ -109,9 +125,17 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
@Inject
lateinit var tokenDetailsRouter: TokenDetailsRouter
@Inject
lateinit var manageTokensRouter: ManageTokensRouter
@Inject
lateinit var walletConnectInteractor: WalletConnectInteractor
@Inject
lateinit var sendRouter: SendRouter
internal val viewModel: MainViewModel by viewModels()
private lateinit var appThemeModeFlow: SharedFlow<AppThemeMode?>
// TODO: fixme: inject through DI
@ -136,6 +160,29 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
initContent()
checkGooglePayAvailability()
checkForNotificationPermission()
observeStateUpdates()
}
private fun observeStateUpdates() {
viewModel.state
.flowWithLifecycle(lifecycle)
.onEach { state ->
if (state.toast is StateEvent.Triggered) {
showToast(state.toast.data)
state.toast.onConsume()
}
}
.launchIn(lifecycleScope)
}
private fun showToast(toast: Toast) {
dismissSnackbar()
showSnackbar(toast.message, Snackbar.LENGTH_LONG, toast.action.text) {
toast.action.onClick()
dismissSnackbar()
}
}
private fun installActivityDependencies() {
@ -156,7 +203,9 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
walletRouter = walletRouter,
walletConnectInteractor = walletConnectInteractor,
tokenDetailsRouter = tokenDetailsRouter,
manageTokensRouter = manageTokensRouter,
cardSdkConfigRepository = cardSdkConfigRepository,
sendRouter = sendRouter,
),
)
}
@ -285,18 +334,22 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
}
}
override fun showSnackbar(text: Int, buttonTitle: Int?, action: View.OnClickListener?) {
if (snackbar != null) return
override fun showSnackbar(
@StringRes text: Int,
length: Int,
@StringRes buttonTitle: Int?,
action: View.OnClickListener?,
) {
showSnackbar(getString(text), length, buttonTitle?.let(::getString), action)
}
snackbar = Snackbar.make(
binding.fragmentContainer,
getString(text),
Snackbar.LENGTH_INDEFINITE,
)
if (buttonTitle != null && action != null) {
snackbar?.setAction(getString(buttonTitle), action)
}
snackbar?.show()
override fun showSnackbar(
text: TextReference,
length: Int,
buttonTitle: TextReference?,
action: View.OnClickListener?,
) {
showSnackbar(text.resolveReference(resources), length, buttonTitle?.resolveReference(resources), action)
}
override fun dismissSnackbar() {
@ -332,6 +385,33 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
lockUserWalletsTimer?.restart()
}
private fun showSnackbar(text: String, length: Int, buttonTitle: String?, action: View.OnClickListener?) {
if (snackbar != null) return
snackbar = Snackbar.make(binding.fragmentContainer, text, length).apply {
val textColor = getColor(R.color.text_primary_2)
setBackgroundTint(getColor(R.color.button_primary))
setActionTextColor(textColor)
setTextColor(textColor)
if (buttonTitle != null && action != null) {
setAction(buttonTitle, action)
}
addCallback(
object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
snackbar = null
removeCallback(this)
}
},
)
}
snackbar?.show()
}
private fun navigateToInitialScreenIfNeeded(intentWhichStartedActivity: Intent?) {
val backStackIsEmpty = supportFragmentManager.backStackEntryCount == 0
val isNotScannedBefore = store.state.globalState.scanResponse == null
@ -371,4 +451,14 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
store.dispatch(BackupAction.CheckForUnfinishedBackup)
}
private fun checkForNotificationPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
BuildConfig.DEBUG &&
ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) !=
PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.POST_NOTIFICATIONS), 0)
}
}
}

View file

@ -5,6 +5,7 @@ import android.content.Context
import android.content.pm.PackageManager
import coil.ImageLoader
import coil.ImageLoaderFactory
import com.chuckerteam.chucker.api.ChuckerInterceptor
import com.orhanobut.logger.AndroidLogAdapter
import com.orhanobut.logger.Logger
import com.tangem.Log
@ -36,6 +37,8 @@ import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.legacy.WalletManagersRepository
import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles
import com.tangem.features.send.api.featuretoggles.SendFeatureToggles
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import com.tangem.tap.common.analytics.AnalyticsFactory
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
@ -170,6 +173,9 @@ internal class TapApplication : Application(), ImageLoaderFactory {
// @Inject
// lateinit var learn2earnInteractor: Learn2earnInteractor
@Inject
lateinit var manageTokensFeatureToggles: ManageTokensFeatureToggles
@Inject
lateinit var scanCardProcessor: ScanCardProcessor
@ -211,6 +217,9 @@ internal class TapApplication : Application(), ImageLoaderFactory {
@Inject
lateinit var walletsRepository: WalletsRepository
@Inject
lateinit var sendFeatureToggles: SendFeatureToggles
// endregion Injected
override fun onCreate() {
@ -255,6 +264,7 @@ internal class TapApplication : Application(), ImageLoaderFactory {
if (LogConfig.network.blockchainSdkNetwork) {
BlockchainSdkRetrofitBuilder.interceptors = listOf(
createNetworkLoggingInterceptor(),
ChuckerInterceptor(this),
)
}
@ -290,6 +300,7 @@ internal class TapApplication : Application(), ImageLoaderFactory {
walletFeatureToggles = walletFeatureToggles,
walletConnectRepository = walletConnect2Repository,
walletConnectSessionsRepository = walletConnectSessionsRepository,
manageTokensFeatureToggles = manageTokensFeatureToggles,
scanCardProcessor = scanCardProcessor,
appCurrencyRepository = appCurrencyRepository,
walletManagersFacade = walletManagersFacade,
@ -300,6 +311,7 @@ internal class TapApplication : Application(), ImageLoaderFactory {
balanceHidingRepository = balanceHidingRepository,
detailsFeatureToggles = detailsFeatureToggles,
walletsRepository = walletsRepository,
sendFeatureToggles = sendFeatureToggles,
),
),
)

View file

@ -1,8 +1,25 @@
package com.tangem.tap.common
import android.view.View
import androidx.annotation.StringRes
import com.google.android.material.snackbar.Snackbar
import com.tangem.core.ui.extensions.TextReference
interface SnackbarHandler {
fun showSnackbar(text: Int, buttonTitle: Int? = null, action: View.OnClickListener? = null)
fun showSnackbar(
@StringRes text: Int,
length: Int = Snackbar.LENGTH_INDEFINITE,
@StringRes buttonTitle: Int? = null,
action: View.OnClickListener? = null,
)
fun showSnackbar(
text: TextReference,
length: Int = Snackbar.LENGTH_INDEFINITE,
buttonTitle: TextReference? = null,
action: View.OnClickListener? = null,
)
fun dismissSnackbar()
}

View file

@ -1,56 +1,7 @@
package com.tangem.tap.common.extensions
import androidx.annotation.DrawableRes
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.extensions.remove
import com.tangem.wallet.R
@Suppress("ComplexMethod")
@DrawableRes
fun Blockchain.getGreyedOutIconRes(): Int {
return when (this) {
Blockchain.Arbitrum, Blockchain.ArbitrumTestnet -> R.drawable.ic_arbitrum_no_color
Blockchain.Bitcoin, Blockchain.BitcoinTestnet -> R.drawable.ic_bitcoin_no_color
Blockchain.BitcoinCash -> R.drawable.ic_bitcoin_cash_no_color
Blockchain.Litecoin -> R.drawable.ic_litecoin_no_color
Blockchain.Ethereum, Blockchain.EthereumTestnet -> R.drawable.ic_eth_no_color
Blockchain.EthereumClassic, Blockchain.EthereumClassicTestnet -> R.drawable.ic_eth_no_color
Blockchain.RSK -> R.drawable.ic_rsk_no_color
Blockchain.Cardano -> R.drawable.ic_cardano_no_color
Blockchain.Tezos -> R.drawable.ic_tezos_no_color
Blockchain.XRP -> R.drawable.ic_xrp_no_color
Blockchain.Stellar -> R.drawable.ic_stellar_no_color
Blockchain.Avalanche, Blockchain.AvalancheTestnet -> R.drawable.ic_avalanche_no_color
Blockchain.Polygon, Blockchain.PolygonTestnet -> R.drawable.ic_polygon_no_color
Blockchain.Solana, Blockchain.SolanaTestnet -> R.drawable.ic_solana_no_color
Blockchain.Fantom, Blockchain.FantomTestnet -> R.drawable.ic_fantom_no_color
Blockchain.BSC, Blockchain.BSCTestnet, Blockchain.Binance, Blockchain.BinanceTestnet ->
R.drawable.ic_bsc_no_color
Blockchain.Dogecoin -> R.drawable.ic_dogecoin_no_color
Blockchain.Tron, Blockchain.TronTestnet -> R.drawable.ic_tron_no_color
Blockchain.Gnosis -> R.drawable.ic_gnosis_no_color
Blockchain.EthereumPow, Blockchain.EthereumPowTestnet -> R.drawable.ic_ethereumpow_no_color
Blockchain.EthereumFair -> R.drawable.ic_ethereumfair_no_color
Blockchain.Polkadot, Blockchain.PolkadotTestnet -> R.drawable.ic_polkadot_no_color
Blockchain.Kusama -> R.drawable.ic_kusama_no_color
Blockchain.Optimism, Blockchain.OptimismTestnet -> R.drawable.ic_optimism_no_color
Blockchain.Dash -> R.drawable.ic_dash_no_color
Blockchain.Kaspa -> R.drawable.ic_kaspa_no_color
Blockchain.TON, Blockchain.TONTestnet -> R.drawable.ic_ton_no_color
Blockchain.Kava, Blockchain.KavaTestnet -> R.drawable.ic_kava_no_color
Blockchain.Ravencoin, Blockchain.RavencoinTestnet -> R.drawable.ic_ravencoin_no_color
Blockchain.Cosmos, Blockchain.CosmosTestnet -> R.drawable.ic_cosmos_no_color
Blockchain.TerraV1 -> R.drawable.ic_terra_no_color
Blockchain.TerraV2 -> R.drawable.ic_terra2_no_color
Blockchain.Cronos -> R.drawable.ic_cronos_no_color
Blockchain.Telos, Blockchain.TelosTestnet -> R.drawable.ic_telos_no_color
Blockchain.AlephZero, Blockchain.AlephZeroTestnet -> R.drawable.ic_azero_no_color
Blockchain.OctaSpace, Blockchain.OctaSpaceTestnet -> R.drawable.ic_octaspace_no_color
Blockchain.Chia, Blockchain.ChiaTestnet -> R.drawable.ic_chia_no_color
Blockchain.Near, Blockchain.NearTestnet -> R.drawable.ic_near_no_color
else -> R.drawable.ic_tangem_logo
}
}
fun Blockchain.getNetworkName(): String {
return when (this) {

View file

@ -18,6 +18,7 @@ import com.tangem.tap.features.details.ui.walletconnect.QrScanFragment
import com.tangem.tap.features.details.ui.walletconnect.WalletConnectFragment
import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment
import com.tangem.tap.features.home.HomeFragment
import com.tangem.tap.features.main.ui.ModalNotificationBottomSheetFragment
import com.tangem.tap.features.onboarding.products.note.OnboardingNoteFragment
import com.tangem.tap.features.onboarding.products.otherCards.OnboardingOtherCardsFragment
import com.tangem.tap.features.onboarding.products.twins.ui.TwinsCardsFragment
@ -59,12 +60,8 @@ fun FragmentActivity.openFragment(
}
}
if (screen.isDialogFragment) {
val dialogFragment = requireNotNull(fragment as? DialogFragment) {
"If screen.isDialogFragment == true then fragment must be a DialogFragment"
}
dialogFragment.showAllowingStateLoss(
if (screen.isDialogFragment && fragment is DialogFragment) {
fragment.showAllowingStateLoss(
fragmentManager = supportFragmentManager,
baseTransaction = transaction,
tag = screen.name,
@ -125,7 +122,7 @@ fun FragmentActivity.getPreviousScreen(): AppScreen? {
return tag?.let { AppScreen.valueOf(tag) }
}
@Suppress("ComplexMethod")
@Suppress("ComplexMethod", "LongMethod")
private fun fragmentFactory(screen: AppScreen): Fragment {
return when (screen) {
AppScreen.Home -> HomeFragment()
@ -146,7 +143,18 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
WalletFragment()
}
}
AppScreen.Send -> SendFragment()
AppScreen.Send -> {
val featureToggles = store.state.daggerGraphState.get(
getDependency = DaggerGraphState::sendFeatureToggles,
)
if (featureToggles.isRedesignedSendEnabled) {
store.state.daggerGraphState
.get(getDependency = DaggerGraphState::sendRouter)
.getEntryFragment()
} else {
SendFragment()
}
}
AppScreen.Details -> DetailsFragment()
AppScreen.DetailsSecurity -> SecurityModeFragment()
AppScreen.CardSettings -> CardSettingsFragment()
@ -154,7 +162,18 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
AppScreen.ResetToFactory -> ResetCardFragment()
AppScreen.AccessCodeRecovery -> AccessCodeRecoveryFragment()
AppScreen.Disclaimer -> DisclaimerFragment()
AppScreen.AddTokens -> TokensListFragment()
AppScreen.ManageTokens -> {
val featureToggles = store.state.daggerGraphState.get(
getDependency = DaggerGraphState::manageTokensFeatureToggles,
)
if (featureToggles.isRedesignedScreenEnabled) {
store.state.daggerGraphState
.get(getDependency = DaggerGraphState::manageTokensRouter)
.getEntryFragment()
} else {
TokensListFragment()
}
}
AppScreen.AddCustomToken -> AddCustomTokenFragment()
AppScreen.WalletDetails -> {
val featureToggles = store.state.daggerGraphState.get(
@ -176,5 +195,6 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
AppScreen.SaveWallet -> SaveWalletBottomSheetFragment()
AppScreen.WalletSelector -> WalletSelectorBottomSheetFragment()
AppScreen.AppCurrencySelector -> AppCurrencySelectorFragment()
AppScreen.ModalNotification -> ModalNotificationBottomSheetFragment()
}
}

View file

@ -1,8 +1,9 @@
package com.tangem.tap.di.domain
import com.tangem.domain.balancehiding.DeviceFlipDetector
import com.tangem.domain.balancehiding.IsBalanceHiddenUseCase
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.balancehiding.ListenToFlipsUseCase
import com.tangem.domain.balancehiding.UpdateBalanceHidingSettingsUseCase
import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository
import com.tangem.domain.settings.*
import com.tangem.domain.settings.repositories.AppRatingRepository
@ -63,8 +64,10 @@ internal object SettingsDomainModule {
@Provides
@ViewModelScoped
fun providesIsBalanceHiddenUseCase(balanceHidingRepository: BalanceHidingRepository): IsBalanceHiddenUseCase {
return IsBalanceHiddenUseCase(
fun providesGetBalanceHidingSettingsUseCase(
balanceHidingRepository: BalanceHidingRepository,
): GetBalanceHidingSettingsUseCase {
return GetBalanceHidingSettingsUseCase(
balanceHidingRepository = balanceHidingRepository,
)
}
@ -80,4 +83,12 @@ internal object SettingsDomainModule {
balanceHidingRepository = balanceHidingRepository,
)
}
@Provides
@ViewModelScoped
fun provideUpdateHideBalancesSettingsUseCase(
balanceHidingRepository: BalanceHidingRepository,
): UpdateBalanceHidingSettingsUseCase {
return UpdateBalanceHidingSettingsUseCase(balanceHidingRepository)
}
}

View file

@ -13,7 +13,6 @@ import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.apptheme.model.AppThemeMode
import com.tangem.domain.balancehiding.BalanceHidingSettings
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.models.scan.ScanResponse
@ -280,7 +279,7 @@ class DetailsMiddleware {
val repository = store.state.daggerGraphState.get(DaggerGraphState::balanceHidingRepository)
scope.launch {
val newState = BalanceHidingSettings(
val newState = repository.getBalanceHidingSettings().copy(
isHidingEnabledInSettings = hideBalance,
isBalanceHidden = false,
)

View file

@ -94,7 +94,7 @@ class HomeFragment : ComposeFragment(), StoreSubscriber<HomeState> {
},
onSearchTokensClick = {
Analytics.send(IntroductionProcess.ButtonTokensList())
store.dispatch(NavigationAction.NavigateTo(AppScreen.AddTokens))
store.dispatch(NavigationAction.NavigateTo(AppScreen.ManageTokens))
store.dispatch(TokensAction.SetArgs.ReadAccess)
},
)

View file

@ -0,0 +1,12 @@
package com.tangem.tap.features.main
internal interface MainIntents {
fun onHiddenBalanceToastAction()
fun onShownBalanceToastAction()
fun onHiddenBalanceNotificationAction(isPermanent: Boolean)
fun onDismissBottomSheet()
}

View file

@ -0,0 +1,62 @@
package com.tangem.tap.features.main
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.event.consumedEvent
import com.tangem.core.ui.event.triggeredEvent
import com.tangem.tap.features.main.model.MainScreenState
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
internal class MainScreenStateHolder(private val intents: MainIntents) {
private val notificationsFactory = NotificationsFactory(intents)
private val stateFlowInternal: MutableStateFlow<MainScreenState> = MutableStateFlow(getInitialState())
val stateFlow: StateFlow<MainScreenState> = stateFlowInternal
fun updateWithHiddenBalancesToast(isBalanceHidden: Boolean) {
stateFlowInternal.update { state ->
state.copy(
toast = triggeredEvent(
data = if (isBalanceHidden) {
notificationsFactory.createBalancesAreHiddenToast()
} else {
notificationsFactory.createBalancesAreShownToast()
},
onConsume = ::consumeToastEvent,
),
)
}
}
fun updateWithHiddenBalancesNotification() {
stateFlowInternal.update { state ->
state.copy(
modalNotification = TangemBottomSheetConfig(
isShow = true,
onDismissRequest = intents::onDismissBottomSheet,
content = notificationsFactory.createBalancesAreHiddenModalNotification(),
),
)
}
}
fun updateWithoutModalNotification() {
stateFlowInternal.update { state ->
state.copy(modalNotification = state.modalNotification?.copy(isShow = false))
}
}
private fun consumeToastEvent() {
stateFlowInternal.update { state ->
state.copy(toast = consumedEvent())
}
}
private fun getInitialState() = MainScreenState(
toast = consumedEvent(),
modalNotification = null,
)
}

View file

@ -0,0 +1,127 @@
package com.tangem.tap.features.main
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.core.navigation.ReduxNavController
import com.tangem.domain.balancehiding.BalanceHidingSettings
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.balancehiding.ListenToFlipsUseCase
import com.tangem.domain.balancehiding.UpdateBalanceHidingSettingsUseCase
import com.tangem.tap.features.main.model.MainScreenState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
internal class MainViewModel @Inject constructor(
private val updateBalanceHidingSettingsUseCase: UpdateBalanceHidingSettingsUseCase,
private val listenToFlipsUseCase: ListenToFlipsUseCase,
private val reduxNavController: ReduxNavController,
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
) : ViewModel(), MainIntents {
private val stateHolder = MainScreenStateHolder(
intents = this,
)
private val balanceHidingSettingsFlow: SharedFlow<BalanceHidingSettings> = getBalanceHidingSettingsUseCase()
.shareIn(viewModelScope, SharingStarted.WhileSubscribed())
val state: StateFlow<MainScreenState> = stateHolder.stateFlow
init {
observeFlips()
displayBalancesHidingStatusToast()
displayHiddenBalancesModalNotification()
}
private fun observeFlips() {
listenToFlipsUseCase().launchIn(viewModelScope)
}
private fun displayHiddenBalancesModalNotification() {
balanceHidingSettingsFlow
.drop(count = 1) // Skip initial emit
.distinctUntilChanged()
.filter {
it.isBalanceHidingNotificationEnabled && it.isBalanceHidden
}
.onEach {
if (state.value.modalNotification?.isShow != true) {
stateHolder.updateWithHiddenBalancesNotification()
reduxNavController.navigate(NavigationAction.NavigateTo(AppScreen.ModalNotification))
}
}
.launchIn(viewModelScope)
}
private fun displayBalancesHidingStatusToast() {
var previousSettings: BalanceHidingSettings? = null
balanceHidingSettingsFlow
.onEach { settings ->
if (previousSettings == null) {
previousSettings = settings
return@onEach
}
/*
* Skip if the feature to hide balances has just been
* enabled or disabled in the settings, or if the hide balances status has not been changed
* */
if (settings.isHidingEnabledInSettings != previousSettings?.isHidingEnabledInSettings ||
settings.isBalanceHidden == previousSettings?.isBalanceHidden
) {
previousSettings = settings
return@onEach
}
displayBalancesHiddenStatusToast(settings)
previousSettings = settings
}
.launchIn(viewModelScope)
}
private fun displayBalancesHiddenStatusToast(settings: BalanceHidingSettings) {
// If modal notification is enabled and balances are hidden, the toast will not show
if (!settings.isBalanceHidingNotificationEnabled || !settings.isBalanceHidden) {
stateHolder.updateWithHiddenBalancesToast(settings.isBalanceHidden)
}
}
override fun onHiddenBalanceToastAction() {
viewModelScope.launch {
updateBalanceHidingSettingsUseCase.invoke {
copy(isBalanceHidden = false)
}
}
}
override fun onShownBalanceToastAction() {
viewModelScope.launch {
updateBalanceHidingSettingsUseCase.invoke {
copy(isBalanceHidden = true)
}
}
}
override fun onHiddenBalanceNotificationAction(isPermanent: Boolean) {
onDismissBottomSheet()
if (isPermanent) {
viewModelScope.launch {
updateBalanceHidingSettingsUseCase.invoke {
copy(isBalanceHidingNotificationEnabled = false)
}
}
}
}
override fun onDismissBottomSheet() {
stateHolder.updateWithoutModalNotification()
}
}

View file

@ -0,0 +1,48 @@
package com.tangem.tap.features.main
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.tap.features.main.model.ActionConfig
import com.tangem.tap.features.main.model.ModalNotification
import com.tangem.tap.features.main.model.Toast
import com.tangem.wallet.R
internal class NotificationsFactory(
private val intents: MainIntents,
) {
fun createBalancesAreHiddenToast(): Toast {
return Toast(
message = resourceReference(R.string.toast_balances_hidden),
action = ActionConfig(
text = resourceReference(R.string.toast_undo),
onClick = intents::onHiddenBalanceToastAction,
),
)
}
fun createBalancesAreShownToast(): Toast {
return Toast(
message = resourceReference(R.string.toast_balances_shown),
action = ActionConfig(
text = resourceReference(R.string.toast_undo),
onClick = intents::onShownBalanceToastAction,
),
)
}
fun createBalancesAreHiddenModalNotification(): ModalNotification {
return ModalNotification(
iconResId = R.drawable.ic_eye_off_outline_24,
title = resourceReference(R.string.balance_hidden_title),
message = resourceReference(R.string.balance_hidden_description),
primaryAction = ActionConfig(
text = resourceReference(R.string.balance_hidden_got_it_button),
onClick = { intents.onHiddenBalanceNotificationAction(isPermanent = false) },
),
secondaryAction = ActionConfig(
text = resourceReference(R.string.balance_hidden_do_not_show_button),
onClick = { intents.onHiddenBalanceNotificationAction(isPermanent = true) },
),
)
}
}

View file

@ -0,0 +1,8 @@
package com.tangem.tap.features.main.model
import com.tangem.core.ui.extensions.TextReference
internal data class ActionConfig(
val text: TextReference,
val onClick: () -> Unit,
)

View file

@ -0,0 +1,9 @@
package com.tangem.tap.features.main.model
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.event.StateEvent
internal data class MainScreenState(
val toast: StateEvent<Toast>,
val modalNotification: TangemBottomSheetConfig?,
)

View file

@ -0,0 +1,13 @@
package com.tangem.tap.features.main.model
import androidx.annotation.DrawableRes
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.extensions.TextReference
internal data class ModalNotification(
@DrawableRes val iconResId: Int,
val title: TextReference,
val message: TextReference,
val primaryAction: ActionConfig,
val secondaryAction: ActionConfig?,
) : TangemBottomSheetConfigContent

View file

@ -0,0 +1,8 @@
package com.tangem.tap.features.main.model
import com.tangem.core.ui.extensions.TextReference
internal data class Toast(
val message: TextReference,
val action: ActionConfig,
)

View file

@ -0,0 +1,57 @@
package com.tangem.tap.features.main.ui
import android.content.DialogInterface
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.ui.screen.ComposeBottomSheetFragment
import com.tangem.core.ui.theme.AppThemeModeHolder
import com.tangem.tap.features.main.MainViewModel
import com.tangem.tap.features.main.model.ModalNotification
import com.tangem.tap.features.main.ui.components.ModalNotificationContent
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@AndroidEntryPoint
internal class ModalNotificationBottomSheetFragment : ComposeBottomSheetFragment() {
@Inject
override lateinit var appThemeModeHolder: AppThemeModeHolder
private val viewModel: MainViewModel by activityViewModels()
@Composable
override fun ScreenContent(modifier: Modifier) {
val state by viewModel.state.collectAsStateWithLifecycle()
val notification = state.modalNotification
if (notification == null) {
dismiss()
return
}
BackHandler(onBack = notification.onDismissRequest)
when (val content = notification.content) {
is ModalNotification -> ModalNotificationContent(
modifier = modifier,
notification = content,
)
else -> Unit
}
LaunchedEffect(notification) {
if (!notification.isShow) {
dismiss()
}
}
}
override fun onDismiss(dialog: DialogInterface) {
viewModel.state.value.modalNotification?.onDismissRequest?.invoke()
super.onDismiss(dialog)
}
}

View file

@ -0,0 +1,133 @@
package com.tangem.tap.features.main.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.components.SecondaryButton
import com.tangem.core.ui.components.atoms.Hand
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.tap.features.main.model.ActionConfig
import com.tangem.tap.features.main.model.ModalNotification
import com.tangem.wallet.R
@Composable
internal fun ModalNotificationContent(notification: ModalNotification, modifier: Modifier = Modifier) {
Column(
modifier = modifier.padding(horizontal = TangemTheme.dimens.spacing16),
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing40),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Hand()
Icon(
modifier = Modifier.size(TangemTheme.dimens.size48),
painter = painterResource(notification.iconResId),
tint = TangemTheme.colors.icon.primary1,
contentDescription = null,
)
Column(
modifier = Modifier
.padding(horizontal = TangemTheme.dimens.spacing16)
.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = notification.title.resolveReference(),
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
)
Text(
text = notification.message.resolveReference(),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.secondary,
textAlign = TextAlign.Center,
)
}
Column(
modifier = Modifier
.padding(bottom = TangemTheme.dimens.spacing16)
.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
horizontalAlignment = Alignment.CenterHorizontally,
) {
PrimaryButton(
modifier = Modifier.fillMaxWidth(),
text = notification.primaryAction.text.resolveReference(),
onClick = notification.primaryAction.onClick,
)
when (val secondaryAction = notification.secondaryAction) {
null -> Unit
else -> SecondaryButton(
modifier = Modifier.fillMaxWidth(),
text = secondaryAction.text.resolveReference(),
onClick = secondaryAction.onClick,
)
}
}
}
}
// region Preview
@Preview(widthDp = 360, heightDp = 404)
@Composable
private fun ModalNotificationContentPreview_Light(
@PreviewParameter(GlobalNotificationProvider::class) param: ModalNotification,
) {
TangemTheme(isDark = false) {
ModalNotificationContent(
param,
modifier = Modifier.background(
color = TangemTheme.colors.background.plain,
shape = TangemTheme.shapes.bottomSheet,
),
)
}
}
@Preview(widthDp = 360, heightDp = 404)
@Composable
private fun ModalNotificationContentPreview_Dark(
@PreviewParameter(GlobalNotificationProvider::class) param: ModalNotification,
) {
TangemTheme(isDark = true) {
ModalNotificationContent(
param,
modifier = Modifier.background(
color = TangemTheme.colors.background.plain,
shape = TangemTheme.shapes.bottomSheet,
),
)
}
}
private class GlobalNotificationProvider : CollectionPreviewParameterProvider<ModalNotification>(
collection = listOf(
ModalNotification(
iconResId = R.drawable.ic_eye_off_outline_24,
title = resourceReference(R.string.balance_hidden_title),
message = resourceReference(R.string.balance_hidden_description),
primaryAction = ActionConfig(
text = resourceReference(R.string.balance_hidden_got_it_button),
onClick = {},
),
secondaryAction = ActionConfig(
text = resourceReference(R.string.balance_hidden_do_not_show_button),
onClick = {},
),
),
),
)
// endregion Preview

View file

@ -4,9 +4,7 @@ import com.google.android.material.button.MaterialButton
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.postUiDelayBg
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupState
import com.tangem.tap.features.onboarding.products.wallet.ui.BackupCardType.FIRST_BACKUP
import com.tangem.tap.features.onboarding.products.wallet.ui.BackupCardType.ORIGIN
import com.tangem.tap.features.onboarding.products.wallet.ui.BackupCardType.SECOND_BACKUP
import com.tangem.tap.features.onboarding.products.wallet.ui.BackupCardType.*
import com.tangem.wallet.databinding.FragmentOnboardingWalletBinding
/**
@ -29,7 +27,7 @@ class WalletBackupAnimator(
private val viewState: State = when (cardsWidget.leapfrogWidget.getViewsCount()) {
2 -> State.TwoCards
3 -> State.TreeCards
3 -> State.ThreeCards
else -> throw UnsupportedOperationException()
}
@ -131,7 +129,7 @@ class WalletBackupAnimator(
}
}
}
State.TreeCards -> {
State.ThreeCards -> {
when (backupCard) {
1 -> {
if (firstBackupCardAnimated) return
@ -147,6 +145,15 @@ class WalletBackupAnimator(
if (secondBackupCardAnimated) return
secondBackupCardAnimated = true
/*
* !!! Workaround !!!
* For interrupted backup
* */
if (!firstBackupCardAnimated) {
showWriteBackupCard(state, backupCard = 1)
return
}
SECOND_BACKUP.alpha(1f)
cardsWidget.leapfrogWidget.leap {
ORIGIN.alpha(0.4f)
@ -165,7 +172,7 @@ class WalletBackupAnimator(
}
private enum class State {
TreeCards,
ThreeCards,
TwoCards,
}
@ -186,7 +193,7 @@ class WalletBackupAnimator(
}
}
SECOND_BACKUP -> {
if (viewState != State.TreeCards) return
if (viewState != State.ThreeCards) return
cardsWidget.getSecondBackupCardView().animate().apply {
alpha(alpha)
this.startDelay = startDelay
@ -201,7 +208,7 @@ class WalletBackupAnimator(
ORIGIN -> cardsWidget.getOriginCardView().alpha = alpha
FIRST_BACKUP -> cardsWidget.getFirstBackupCardView().alpha = alpha
SECOND_BACKUP -> {
if (viewState != State.TreeCards) return
if (viewState != State.ThreeCards) return
cardsWidget.getSecondBackupCardView().alpha = alpha
}
}
@ -212,7 +219,7 @@ class WalletBackupAnimator(
ORIGIN -> cardsWidget.getOriginCardView().show()
FIRST_BACKUP -> cardsWidget.getFirstBackupCardView().show()
SECOND_BACKUP -> {
if (viewState != State.TreeCards) return
if (viewState != State.ThreeCards) return
cardsWidget.getSecondBackupCardView().show()
}
}

View file

@ -1,7 +1,7 @@
package com.tangem.tap.features.send.ui
import androidx.lifecycle.*
import com.tangem.domain.balancehiding.IsBalanceHiddenUseCase
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.balancehiding.ListenToFlipsUseCase
import com.tangem.domain.tokens.FetchPendingTransactionsUseCase
import com.tangem.domain.tokens.UpdateDelayedNetworkStatusUseCase
@ -9,7 +9,7 @@ import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.features.send.navigation.SendRouter
import com.tangem.features.send.api.navigation.SendRouter
import com.tangem.tap.di.DelayedWork
import com.tangem.tap.features.send.redux.AmountAction
import com.tangem.tap.proxy.AppStateHolder
@ -29,7 +29,7 @@ import javax.inject.Inject
internal class SendViewModel @Inject constructor(
private val dispatchers: CoroutineDispatcherProvider,
private val appStateHolder: AppStateHolder,
private val isBalanceHiddenUseCase: IsBalanceHiddenUseCase,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val listenToFlipsUseCase: ListenToFlipsUseCase,
private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase,
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
@ -41,11 +41,11 @@ internal class SendViewModel @Inject constructor(
private val cryptoCurrency: CryptoCurrency? = savedStateHandle[SendRouter.CRYPTO_CURRENCY_KEY]
override fun onCreate(owner: LifecycleOwner) {
isBalanceHiddenUseCase()
getBalanceHidingSettingsUseCase()
.flowWithLifecycle(owner.lifecycle)
.onEach { isBalanceHidden ->
.onEach {
withContext(dispatchers.main) {
appStateHolder.mainStore?.dispatch(AmountAction.HideBalance(isBalanceHidden))
appStateHolder.mainStore?.dispatch(AmountAction.HideBalance(it.isBalanceHidden))
}
}
.launchIn(viewModelScope)

View file

@ -3,7 +3,7 @@ package com.tangem.tap.features.tokens.impl.presentation.states
import androidx.compose.runtime.MutableState
import com.tangem.blockchain.common.Blockchain
import com.tangem.core.ui.extensions.getActiveIconRes
import com.tangem.tap.common.extensions.getGreyedOutIconRes
import com.tangem.core.ui.extensions.getGreyedOutIconRes
/**
* Network item state
@ -76,7 +76,7 @@ sealed interface NetworkItemState {
fun changeToggleState() {
val reverseState = !isAdded.value
isAdded.value = reverseState
iconResId.value = if (reverseState) getActiveIconRes(blockchain.id) else blockchain.getGreyedOutIconRes()
iconResId.value = if (reverseState) getActiveIconRes(blockchain.id) else getGreyedOutIconRes(blockchain.id)
}
}
}

View file

@ -40,7 +40,7 @@ object TokenListPreviewData {
NetworkItemState.ManageContent(
name = "Ethereum",
protocolName = "MAIN",
iconResId = mutableStateOf(R.drawable.ic_eth_no_color),
iconResId = mutableStateOf(R.drawable.ic_eth_16),
isMainNetwork = true,
isAdded = mutableStateOf(true),
id = "",
@ -53,7 +53,7 @@ object TokenListPreviewData {
NetworkItemState.ManageContent(
name = "BNB SMART CHAIN",
protocolName = "BEP20",
iconResId = mutableStateOf(R.drawable.ic_bsc_no_color),
iconResId = mutableStateOf(R.drawable.ic_bsc_16),
isMainNetwork = false,
isAdded = mutableStateOf(false),
id = "",
@ -71,13 +71,13 @@ object TokenListPreviewData {
NetworkItemState.ReadContent(
name = "Ethereum",
protocolName = "MAIN",
iconResId = mutableStateOf(R.drawable.ic_eth_no_color),
iconResId = mutableStateOf(R.drawable.ic_eth_16),
isMainNetwork = true,
),
NetworkItemState.ReadContent(
name = "BNB SMART CHAIN",
protocolName = "BEP20",
iconResId = mutableStateOf(R.drawable.ic_bsc_no_color),
iconResId = mutableStateOf(R.drawable.ic_bsc_16),
isMainNetwork = false,
),
)

View file

@ -9,6 +9,7 @@ import androidx.paging.*
import com.tangem.blockchain.common.Blockchain
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.ui.extensions.getActiveIconRes
import com.tangem.core.ui.extensions.getGreyedOutIconRes
import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation
import com.tangem.domain.common.extensions.canHandleBlockchain
import com.tangem.domain.common.extensions.canHandleToken
@ -20,7 +21,6 @@ import com.tangem.domain.tokens.TokenWithBlockchain
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import com.tangem.tap.common.extensions.fullNameWithoutTestnet
import com.tangem.tap.common.extensions.getGreyedOutIconRes
import com.tangem.tap.common.extensions.getNetworkName
import com.tangem.tap.features.tokens.impl.domain.TokensListInteractor
import com.tangem.tap.features.tokens.impl.domain.models.Token
@ -225,7 +225,7 @@ internal class TokensListViewModel @Inject constructor(
return if (isAdded(address = address, blockchain = blockchain)) {
getActiveIconRes(blockchain.id)
} else {
blockchain.getGreyedOutIconRes()
getGreyedOutIconRes(blockchain.id)
}
}

View file

@ -15,7 +15,7 @@ import com.tangem.domain.tokens.legacy.TradeCryptoAction
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.feature.swap.presentation.SwapFragment
import com.tangem.features.send.navigation.SendRouter
import com.tangem.features.send.api.navigation.SendRouter
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Token
import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder
@ -366,7 +366,10 @@ class TradeCryptoMiddleware {
),
)
val bundle = bundleOf(SendRouter.CRYPTO_CURRENCY_KEY to currency)
val bundle = bundleOf(
SendRouter.CRYPTO_CURRENCY_KEY to currency,
SendRouter.USER_WALLET_ID_KEY to action.userWallet.walletId.stringValue,
)
store.dispatchOnMain(NavigationAction.NavigateTo(screen = AppScreen.Send, bundle = bundle))
}
}
@ -416,7 +419,10 @@ class TradeCryptoMiddleware {
is CryptoCurrency.Token -> error("Action.tokenStatus.currency is Token")
}
val bundle = bundleOf(SendRouter.CRYPTO_CURRENCY_KEY to currency)
val bundle = bundleOf(
SendRouter.CRYPTO_CURRENCY_KEY to currency,
SendRouter.USER_WALLET_ID_KEY to action.userWallet.walletId.stringValue,
)
store.dispatchOnMain(NavigationAction.NavigateTo(screen = AppScreen.Send, bundle = bundle))
}
}

View file

@ -106,7 +106,7 @@ class MultiWalletView : WalletView() {
Analytics.send(Portfolio.ButtonManageTokens())
store.dispatch(action = TokensAction.SetArgs.ManageAccess)
store.dispatch(action = NavigationAction.NavigateTo(screen = AppScreen.AddTokens))
store.dispatch(action = NavigationAction.NavigateTo(screen = AppScreen.ManageTokens))
}
handleErrorStates(state = state, binding = binding, fragment = fragment)
}

View file

@ -2,6 +2,8 @@ package com.tangem.tap.proxy.redux
import com.tangem.domain.card.ScanCardUseCase
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.features.managetokens.navigation.ManageTokensRouter
import com.tangem.features.send.api.navigation.SendRouter
import com.tangem.features.tester.api.TesterRouter
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
import com.tangem.features.wallet.navigation.WalletRouter
@ -16,6 +18,8 @@ sealed interface DaggerGraphAction : Action {
val walletRouter: WalletRouter,
val walletConnectInteractor: WalletConnectInteractor,
val tokenDetailsRouter: TokenDetailsRouter,
val manageTokensRouter: ManageTokensRouter,
val cardSdkConfigRepository: CardSdkConfigRepository,
val sendRouter: SendRouter,
) : DaggerGraphAction
}

View file

@ -18,7 +18,9 @@ object DaggerGraphReducer {
walletRouter = action.walletRouter,
walletConnectInteractor = action.walletConnectInteractor,
tokenDetailsRouter = action.tokenDetailsRouter,
manageTokensRouter = action.manageTokensRouter,
cardSdkConfigRepository = action.cardSdkConfigRepository,
sendRouter = action.sendRouter,
)
}
}

View file

@ -12,6 +12,10 @@ import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles
import com.tangem.features.managetokens.navigation.ManageTokensRouter
import com.tangem.features.send.api.featuretoggles.SendFeatureToggles
import com.tangem.features.send.api.navigation.SendRouter
import com.tangem.features.tester.api.TesterRouter
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
@ -36,6 +40,8 @@ data class DaggerGraphState(
val walletConnectSessionsRepository: WalletConnectSessionsRepository? = null,
val walletConnectInteractor: WalletConnectInteractor? = null,
val tokenDetailsRouter: TokenDetailsRouter? = null,
val manageTokensFeatureToggles: ManageTokensFeatureToggles? = null,
val manageTokensRouter: ManageTokensRouter? = null,
val scanCardProcessor: ScanCardProcessor? = null,
val cardSdkConfigRepository: CardSdkConfigRepository? = null,
val appCurrencyRepository: AppCurrencyRepository? = null,
@ -46,6 +52,8 @@ data class DaggerGraphState(
val detailsFeatureToggles: DetailsFeatureToggles? = null,
val walletsRepository: WalletsRepository? = null,
val networksRepository: NetworksRepository? = null,
val sendFeatureToggles: SendFeatureToggles? = null,
val sendRouter: SendRouter? = null,
// FIXME: It is used only for TokensList screen. Remove after refactoring of TokensList
val currenciesRepository: CurrenciesRepository? = null,