Updated on 2026-08-14
This commit is contained in:
parent
0cd80d7c50
commit
7f95a6b518
7 changed files with 92 additions and 34 deletions
|
|
@ -15,6 +15,7 @@ import com.tangem.core.navigation.share.ShareManager
|
|||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
import com.tangem.data.card.TransactionSignerFactory
|
||||
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
|
||||
import com.tangem.datasource.connection.NetworkConnectionManager
|
||||
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
|
||||
import com.tangem.datasource.local.config.issuers.IssuersConfigStorage
|
||||
|
|
@ -142,4 +143,6 @@ interface ApplicationEntryPoint {
|
|||
fun getOnlineCardVerifier(): OnlineCardVerifier
|
||||
|
||||
fun getUserWalletBuilderFactory(): UserWalletBuilder.Factory
|
||||
|
||||
fun getApiConfigsManager(): ApiConfigsManager
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ import com.tangem.core.navigation.settings.SettingsManager
|
|||
import com.tangem.core.ui.clipboard.ClipboardManager
|
||||
import com.tangem.data.card.TransactionSignerFactory
|
||||
import com.tangem.datasource.api.common.MoshiConverter
|
||||
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
|
||||
import com.tangem.datasource.api.common.createNetworkLoggingInterceptor
|
||||
import com.tangem.datasource.connection.NetworkConnectionManager
|
||||
import com.tangem.datasource.local.config.environment.EnvironmentConfig
|
||||
|
|
@ -228,6 +229,9 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
|
|||
private val userWalletBuilderFactory: UserWalletBuilder.Factory
|
||||
get() = entryPoint.getUserWalletBuilderFactory()
|
||||
|
||||
private val apiConfigsManager: ApiConfigsManager
|
||||
get() = entryPoint.getApiConfigsManager()
|
||||
|
||||
// endregion
|
||||
|
||||
private val appScope = MainScope()
|
||||
|
|
@ -275,6 +279,8 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
|
|||
}
|
||||
|
||||
fun init() {
|
||||
apiConfigsManager.initialize()
|
||||
|
||||
store = createReduxStore()
|
||||
|
||||
tangemAppLoggerInitializer.initialize()
|
||||
|
|
@ -285,12 +291,8 @@ abstract class TangemApplication : Application(), ImageLoaderFactory, Configurat
|
|||
// We need to initialize the toggles and excludedBlockchainsManager before the MainActivity starts using them.
|
||||
runBlocking {
|
||||
awaitAll(
|
||||
async {
|
||||
featureTogglesManager.init()
|
||||
},
|
||||
async {
|
||||
excludedBlockchainsManager.init()
|
||||
},
|
||||
async { featureTogglesManager.init() },
|
||||
async { excludedBlockchainsManager.init() },
|
||||
)
|
||||
initWithConfigDependency(environmentConfig = environmentConfigStorage.initialize())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import com.tangem.core.analytics.models.event.TechAnalyticsEvent
|
|||
import com.tangem.core.decompose.di.GlobalUiMessageSender
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.deeplink.DeepLinksRegistry
|
||||
import com.tangem.core.ui.BuildConfig
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.coil.ImagePreloader
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
|
|
@ -40,10 +41,13 @@ import com.tangem.tap.features.onboarding.products.wallet.redux.BackupDialog
|
|||
import com.tangem.tap.store
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@HiltViewModel
|
||||
|
|
@ -57,7 +61,6 @@ internal class MainViewModel @Inject constructor(
|
|||
private val userWalletsListManager: UserWalletsListManager,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val fetchStakingTokensUseCase: FetchStakingTokensUseCase,
|
||||
private val apiConfigsManager: ApiConfigsManager,
|
||||
private val fetchUserCountryUseCase: FetchUserCountryUseCase,
|
||||
@GlobalUiMessageSender private val messageSender: UiMessageSender,
|
||||
private val keyboardValidator: KeyboardValidator,
|
||||
|
|
@ -68,6 +71,7 @@ internal class MainViewModel @Inject constructor(
|
|||
private val onboardingRepository: OnboardingRepository,
|
||||
private val deepLinksRegistry: DeepLinksRegistry,
|
||||
private val onrampDeepLinkFactory: OnrampDeepLink.Factory,
|
||||
private val apiConfigsManager: ApiConfigsManager,
|
||||
routingFeatureToggle: RoutingFeatureToggle,
|
||||
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
) : ViewModel() {
|
||||
|
|
@ -79,25 +83,27 @@ internal class MainViewModel @Inject constructor(
|
|||
private set
|
||||
|
||||
init {
|
||||
/**
|
||||
* Run any data initialization here that needs to happen before the app starts
|
||||
* and is hidden behind the SplashScreen
|
||||
*/
|
||||
loadApplicationResources()
|
||||
|
||||
viewModelScope.launch(dispatchers.main) { incrementAppLaunchCounterUseCase() }
|
||||
/** Run any API data load here that runs in parallel and does not block the app from starting */
|
||||
launchAPIRequests {
|
||||
launch { fetchHotCryptoUseCase() }
|
||||
|
||||
viewModelScope.launch {
|
||||
fetchUserCountryUseCase().onLeft {
|
||||
Timber.e("Unable to fetch the user country code $it")
|
||||
}
|
||||
launch { fetchAppCurrenciesUseCase() }
|
||||
|
||||
launch { fetchStakingTokens() }
|
||||
}
|
||||
|
||||
viewModelScope.launch { fetchHotCryptoUseCase() }
|
||||
viewModelScope.launch { incrementAppLaunchCounterUseCase() }
|
||||
|
||||
updateAppCurrencies()
|
||||
observeFlips()
|
||||
displayBalancesHidingStatusToast()
|
||||
displayHiddenBalancesModalNotification()
|
||||
|
||||
fetchStakingTokens()
|
||||
|
||||
deleteDeprecatedLogsUseCase()
|
||||
|
||||
sendKeyboardIdentifierEvent()
|
||||
|
|
@ -118,16 +124,41 @@ internal class MainViewModel @Inject constructor(
|
|||
|
||||
/** Loading the resources needed to run the application */
|
||||
private fun loadApplicationResources() {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
apiConfigsManager.initialize()
|
||||
viewModelScope.launch {
|
||||
launchAPIRequests {
|
||||
launch { blockchainSDKFactory.init() }
|
||||
|
||||
launch {
|
||||
withTimeout(timeMillis = 1.seconds.inWholeMilliseconds) { fetchUserCountry() }
|
||||
}
|
||||
}
|
||||
|
||||
blockchainSDKFactory.init()
|
||||
prepareSelectedWalletFeedback()
|
||||
|
||||
isSplashScreenShown = false
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchUserCountry() {
|
||||
fetchUserCountryUseCase().onLeft {
|
||||
Timber.e("Unable to fetch the user country code $it")
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchAPIRequests(function: suspend CoroutineScope.() -> Unit) {
|
||||
viewModelScope.launch {
|
||||
if (BuildConfig.TESTER_MENU_ENABLED) {
|
||||
apiConfigsManager.isInitialized
|
||||
.filter { it }
|
||||
.first() // wait until isInitialized becomes true
|
||||
|
||||
function()
|
||||
} else {
|
||||
function()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun prepareSelectedWalletFeedback() {
|
||||
userWalletsListManager.selectedUserWallet
|
||||
.distinctUntilChanged()
|
||||
|
|
@ -138,18 +169,10 @@ internal class MainViewModel @Inject constructor(
|
|||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
private fun updateAppCurrencies() {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
fetchAppCurrenciesUseCase.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchStakingTokens() {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
fetchStakingTokensUseCase()
|
||||
.onLeft { Timber.e(it.toString(), "Unable to fetch the staking tokens list") }
|
||||
.onRight { Timber.d("Staking token list was fetched successfully") }
|
||||
}
|
||||
private suspend fun fetchStakingTokens() {
|
||||
fetchStakingTokensUseCase()
|
||||
.onLeft { Timber.e(it.toString(), "Unable to fetch the staking tokens list") }
|
||||
.onRight { Timber.d("Staking token list was fetched successfully") }
|
||||
}
|
||||
|
||||
private fun observeFlips() {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.datasource.api.common.config.managers
|
|||
|
||||
import com.tangem.datasource.api.common.config.ApiConfig
|
||||
import com.tangem.datasource.api.common.config.ApiEnvironmentConfig
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* Api configs manager
|
||||
|
|
@ -10,8 +11,11 @@ import com.tangem.datasource.api.common.config.ApiEnvironmentConfig
|
|||
*/
|
||||
interface ApiConfigsManager {
|
||||
|
||||
/** Flag that determines whether the manager is initialized */
|
||||
val isInitialized: StateFlow<Boolean>
|
||||
|
||||
/** Initialize resources */
|
||||
fun initialize() {}
|
||||
fun initialize()
|
||||
|
||||
/** Get environment config by [id] */
|
||||
fun getEnvironmentConfig(id: ApiConfig.ID): ApiEnvironmentConfig
|
||||
|
|
|
|||
|
|
@ -29,7 +29,13 @@ internal class DevApiConfigsManager(
|
|||
|
||||
private val _apiConfigs = MutableStateFlow(value = apiConfigs.associateWith { it.defaultEnvironment })
|
||||
|
||||
override val isInitialized: StateFlow<Boolean> get() = _isInitialized.asStateFlow()
|
||||
|
||||
private val _isInitialized = MutableStateFlow(value = false)
|
||||
|
||||
override fun initialize() {
|
||||
_isInitialized.value = false
|
||||
|
||||
// We can't use appPreferencesStore.getObjectMap as base flow,
|
||||
// because we should keep possibility to work with configs synchronous.
|
||||
// See [getBaseUrl]
|
||||
|
|
@ -42,8 +48,12 @@ internal class DevApiConfigsManager(
|
|||
savedEnvironments[config.id.name] ?: currentEnvironment
|
||||
}
|
||||
}
|
||||
|
||||
if (!_isInitialized.value) {
|
||||
_isInitialized.value = true
|
||||
}
|
||||
}
|
||||
.launchIn(CoroutineScope(SupervisorJob() + dispatchers.main))
|
||||
.launchIn(CoroutineScope(SupervisorJob() + dispatchers.default))
|
||||
}
|
||||
|
||||
override fun getEnvironmentConfig(id: ApiConfig.ID): ApiEnvironmentConfig {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package com.tangem.datasource.api.common.config.managers
|
|||
import com.tangem.datasource.api.common.config.ApiConfig
|
||||
import com.tangem.datasource.api.common.config.ApiConfigs
|
||||
import com.tangem.datasource.api.common.config.ApiEnvironmentConfig
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/**
|
||||
* Implementation of [ApiConfigsManager] in PROD environment
|
||||
|
|
@ -13,6 +15,10 @@ internal class ProdApiConfigsManager(
|
|||
private val apiConfigs: ApiConfigs,
|
||||
) : ApiConfigsManager {
|
||||
|
||||
override val isInitialized: StateFlow<Boolean> = MutableStateFlow(value = true)
|
||||
|
||||
override fun initialize() = Unit
|
||||
|
||||
override fun getEnvironmentConfig(id: ApiConfig.ID): ApiEnvironmentConfig {
|
||||
val config = apiConfigs.firstOrNull { it.id == id }
|
||||
?: error("Api config with id [$id] not found. Check that ApiConfig with id [$id] was provided into DI")
|
||||
|
|
|
|||
|
|
@ -43,6 +43,10 @@ internal object NetworkModule {
|
|||
private const val TANGEM_TECH_MARKETS_SERVICE_TIMEOUT_SECONDS = 60L
|
||||
private const val STAKE_KIT_API_TIMEOUT_SECONDS = 60L
|
||||
|
||||
private val excludedApiForLogging: Set<ApiConfig.ID> = setOf(
|
||||
// ApiConfig.ID.StakeKit,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideApiConfigManager(
|
||||
|
|
@ -317,7 +321,7 @@ internal object NetworkModule {
|
|||
}
|
||||
b
|
||||
}
|
||||
.addLoggers(context)
|
||||
.addLoggers(context = context, id = id)
|
||||
.clientBuilder()
|
||||
.build(),
|
||||
)
|
||||
|
|
@ -325,6 +329,12 @@ internal object NetworkModule {
|
|||
.create(T::class.java)
|
||||
}
|
||||
|
||||
private fun OkHttpClient.Builder.addLoggers(context: Context, id: ApiConfig.ID): OkHttpClient.Builder {
|
||||
if (id in excludedApiForLogging) return this
|
||||
|
||||
return addLoggers(context)
|
||||
}
|
||||
|
||||
private data class Timeouts(
|
||||
val callTimeoutSeconds: Long? = null,
|
||||
val connectTimeoutSeconds: Long? = null,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue