diff --git a/app/src/main/java/com/tangem/tap/common/Throttling.kt b/app/src/main/java/com/tangem/tap/common/Throttling.kt new file mode 100644 index 0000000000..e0d3c6ee86 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/Throttling.kt @@ -0,0 +1,64 @@ +package com.tangem.tap.common + +/** +[REDACTED_AUTHOR] + */ +open class Throttler( + private val duration: Long, +) : Throttle { + + private val items: MutableMap = mutableMapOf() + + override fun isStillThrottled(item: T): Boolean { + val inThrottlingUpTo = items[item] ?: return false + val diff = System.currentTimeMillis() - inThrottlingUpTo + return diff < 0 + } + + override fun updateThrottlingTo(item: T): T { + val now = System.currentTimeMillis() + val throttledUpTo = items[item] ?: 0L + if (throttledUpTo == 0L || throttledUpTo < now) { + val newTime = now + duration + items[item] = newTime + } + return item + } + + open fun clear() { + items.clear() + } +} + +class ThrottlerWithValues( + duration: Long +) : Throttler(duration), ValuesHolder { + + private val valuesHolder: MutableMap = mutableMapOf() + + override fun setValue(item: T, value: V) { + valuesHolder[item] = value + } + + override fun geValue(item: T): V? = valuesHolder[item] + + override fun remove(item: T) { + valuesHolder.remove(item) + } + + override fun clear() { + valuesHolder.clear() + super.clear() + } +} + +interface Throttle { + fun isStillThrottled(item: T): Boolean + fun updateThrottlingTo(item: T): T +} + +interface ValuesHolder { + fun setValue(item: K, value: V) + fun geValue(item: K): V? + fun remove(item: K) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt index b4824b60a3..c500f330b2 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt @@ -13,7 +13,7 @@ class GlobalAnalyticsHandler(val analyticsHandlers: List) : blockchain: String?, params: Map ) { - analyticsHandlers.forEach { it.triggerEvent(event, card, blockchain) } + analyticsHandlers.forEach { it.triggerEvent(event, card, blockchain, params) } } override fun triggerEvent(event: String, params: Map) { diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt index 15ac94f6ac..cb094b7c7a 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -3,6 +3,7 @@ package com.tangem.tap.domain import com.tangem.blockchain.blockchains.solana.RentProvider import com.tangem.blockchain.common.* import com.tangem.common.services.Result +import com.tangem.tap.common.ThrottlerWithValues import com.tangem.tap.common.extensions.dispatchDebugErrorNotification import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.safeUpdate @@ -42,32 +43,30 @@ class TapWalletManager { store.state.globalState.configManager?.config?.blockchainSdkConfig ?: BlockchainSdkConfig() } - private val walletManagersThrottler = Throttler(10000) - private val fiatRatesThrottler = Throttler(60000) + private val walletManagersThrottler = ThrottlerWithValues>(10000) + private val fiatRatesThrottler = ThrottlerWithValues?>(60000) suspend fun loadWalletData(walletManager: WalletManager) { val blockchain = walletManager.wallet.blockchain val result = if (walletManagersThrottler.isStillThrottled(blockchain)) { - delay(200) - Result.Success(walletManager.wallet) + delay(500) + walletManagersThrottler.geValue(blockchain)!! } else { - walletManagersThrottler.updateThrottlingTo(blockchain) - walletManager.safeUpdate() + updateThrottlingForWalletManager(walletManager) } handleUpdateWalletResult(result, walletManager) } suspend fun updateWallet(walletManager: WalletManager, force: Boolean) { val result = if (force) { - walletManager.safeUpdate() + updateThrottlingForWalletManager(walletManager) } else { val blockchain = walletManager.wallet.blockchain if (walletManagersThrottler.isStillThrottled(blockchain)) { - delay(200) - Result.Success(walletManager.wallet) + delay(500) + walletManagersThrottler.geValue(blockchain)!! } else { - walletManagersThrottler.updateThrottlingTo(blockchain) - walletManager.safeUpdate() + updateThrottlingForWalletManager(walletManager) } } withContext(Dispatchers.Main) { @@ -78,7 +77,14 @@ class TapWalletManager { store.dispatch(WalletAction.UpdateWallet.Failure(result.error.localizedMessage)) } } + } + private suspend fun updateThrottlingForWalletManager(walletManager: WalletManager): Result { + val newResult = walletManager.safeUpdate() + val blockchain = walletManager.wallet.blockchain + walletManagersThrottler.updateThrottlingTo(blockchain) + walletManagersThrottler.setValue(blockchain, newResult) + return newResult } suspend fun loadFiatRate(fiatCurrency: FiatCurrencyName, wallet: Wallet) { @@ -88,25 +94,25 @@ class TapWalletManager { loadFiatRate(fiatCurrency, currencies) } - suspend fun loadFiatRate(fiatCurrency: FiatCurrencyName, currency: Currency) { - val currencies = listOf(currency) - loadFiatRate(fiatCurrency, currencies) - } - suspend fun loadFiatRate(fiatCurrency: FiatCurrencyName, currencies: List) { - val results = mutableListOf?>>() - currencies.forEach { - if (!fiatRatesThrottler.isStillThrottled(it)) { - fiatRatesThrottler.updateThrottlingTo(it) - results.add(it to coinMarketCapService.getRate(it.currencySymbol, fiatCurrency)) - handleFiatRatesResult(results) - } + // get and submit previous result of equivalents. + val throttledResult = currencies.filter { fiatRatesThrottler.isStillThrottled(it) }.map { + Pair(it, fiatRatesThrottler.geValue(it)) + } + if (throttledResult.isNotEmpty()) handleFiatRatesResult(throttledResult) + + val toUpdate = currencies.filter { !fiatRatesThrottler.isStillThrottled(it) } + toUpdate.forEach { + fiatRatesThrottler.updateThrottlingTo(it) + val result = coinMarketCapService.getRate(it.currencySymbol, fiatCurrency) + fiatRatesThrottler.setValue(it, result) + handleFiatRatesResult(listOf(it to result)) } } suspend fun onCardScanned(data: ScanResponse) { walletManagersThrottler.clear() - fiatRatesThrottler.clear() +// fiatRatesThrottler.clear() store.state.globalState.feedbackManager?.infoHolder?.setCardInfo(data.card) updateConfigManager(data) @@ -323,8 +329,7 @@ class TapWalletManager { store.dispatch(WalletAction.LoadFiatRate.Success(rate)) } is Result.Failure -> store.dispatch(WalletAction.LoadFiatRate.Failure) - null -> { - } + null -> {} } } } @@ -333,31 +338,4 @@ class TapWalletManager { fun Wallet.getFirstToken(): Token? { return getTokens().toList().getOrNull(0) -} - -class Throttler( - private val duration: Long, -) { - - private val items: MutableMap = mutableMapOf() - - fun isStillThrottled(item: T): Boolean { - val inThrottlingUpTo = items[item] ?: return false - val diff = System.currentTimeMillis() - inThrottlingUpTo - return diff < 0 - } - - fun updateThrottlingTo(item: T): T { - val now = System.currentTimeMillis() - val throttledUpTo = items[item] ?: 0L - if (throttledUpTo == 0L || throttledUpTo < now) { - val newTime = now + duration - items[item] = newTime - } - return item - } - - fun clear() { - items.clear() - } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt index e9d11624a9..482e629344 100644 --- a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt @@ -1,12 +1,11 @@ package com.tangem.tap.features.home -import android.content.Context import android.os.Bundle -import android.telephony.TelephonyManager import android.view.View import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.text.intl.Locale import androidx.fragment.app.Fragment import com.google.accompanist.appcompattheme.AppCompatTheme import com.tangem.tap.features.home.compose.StoriesScreen @@ -30,20 +29,25 @@ class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber(R.id.cv_stories)?.setContent { AppCompatTheme { StoriesScreen( homeState, onScanButtonClick = { store.dispatch(HomeAction.ReadCard) }, - onShopButtonClick = { store.dispatch(HomeAction.GoToShop(countryCodeValue)) } + onShopButtonClick = { store.dispatch(HomeAction.GoToShop(getLocale())) } ) } } } + private fun getLocale(): String { +// val tm = requireContext().getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager +// val countryCodeValue = tm.networkCountryIso + return Locale.current.language + } + override fun onStart() { super.onStart() store.subscribe(this) { state -> diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt index f103fc3817..d659510cd4 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt @@ -204,7 +204,7 @@ private fun sendTransaction( withContext(Dispatchers.Main) { dispatch(WalletAction.UpdateWallet(walletManager.wallet.blockchain)) } - delay(10000) + delay(11000) // more than 10000 to avoid throttling withContext(Dispatchers.Main) { dispatch(WalletAction.UpdateWallet(walletManager.wallet.blockchain)) } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt index f2ec828995..26fae5925a 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt @@ -81,7 +81,7 @@ sealed class WalletAction : Action { } data class LoadFiatRate( - val wallet: Wallet? = null, val currency: Currency? = null, + val wallet: Wallet? = null, val currencyList: List? = null, ) : WalletAction() { data class Success(val fiatRate: Pair) : WalletAction() object Failure : WalletAction() diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt index 19197ad893..f0b6c3506e 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt @@ -3,7 +3,6 @@ package com.tangem.tap.features.wallet.redux.middlewares import com.tangem.blockchain.common.* import com.tangem.blockchain.extensions.Result import com.tangem.common.extensions.isZero -import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.safeUpdate import com.tangem.tap.common.redux.global.GlobalState import com.tangem.tap.common.redux.navigation.AppScreen @@ -47,7 +46,7 @@ class MultiWalletMiddleware { globalState.scanResponse?.card?.cardId?.let { currenciesRepository.saveAddedToken(it, action.token) } - addToken(action.token, walletState, globalState) + addTokens(listOf(action.token), walletState, globalState) } is WalletAction.MultiWallet.AddTokens -> { addTokens(action.tokens, walletState, globalState) @@ -62,7 +61,9 @@ class MultiWalletMiddleware { } } } - store.dispatch(WalletAction.LoadFiatRate(currency = Currency.Blockchain(action.blockchain))) + store.dispatch(WalletAction.LoadFiatRate( + currencyList = listOf(Currency.Blockchain(action.blockchain))) + ) store.dispatch(WalletAction.LoadWallet(action.blockchain) ) } @@ -172,80 +173,41 @@ class MultiWalletMiddleware { } } - private fun addToken(token: Token, walletState: WalletState?, globalState: GlobalState?) { + private fun addTokens(tokens: List, walletState: WalletState?, globalState: GlobalState?) { val scanResponse = globalState?.scanResponse ?: return + val wmFactory = globalState.tapWalletManager.walletManagerFactory - val walletManager = walletState?.getWalletManager(token) - ?: globalState.tapWalletManager.walletManagerFactory.makeWalletManagerForApp( - scanResponse, - token.blockchain - )?.also { walletManager -> - store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManager)) - store.dispatch(WalletAction.MultiWallet.AddBlockchain(walletManager.wallet.blockchain)) - } - - store.dispatch(WalletAction.LoadFiatRate(currency = Currency.Token(token))) - - scope.launch { - when (val result = walletManager?.addToken(token)) { - is Result.Success -> { - store.dispatchOnMain(WalletAction.MultiWallet.TokenLoaded(result.data, token)) + val groupedTokens = tokens.groupBy { it.blockchain } + val walletManagers = groupedTokens.mapNotNull { entry -> + val blockchain = entry.key + val tokensList = entry.value + val walletManager = walletState?.getWalletManager(blockchain) + ?: wmFactory.makeWalletManagerForApp(scanResponse, blockchain)?.also { + store.dispatch(WalletAction.MultiWallet.AddWalletManagers(it)) + store.dispatch(WalletAction.MultiWallet.AddBlockchain(blockchain)) } - is Result.Failure -> { - when (val result = walletManager.safeUpdate()) { - is com.tangem.common.services.Result.Success -> { - val tokenAmount = result.data.getTokenAmount(token) ?: return@launch - store.dispatchOnMain(WalletAction.MultiWallet.TokenLoaded(tokenAmount, token)) - } - is com.tangem.common.services.Result.Failure -> { - } + store.dispatch(WalletAction.LoadFiatRate(currencyList = tokensList.map { Currency.Token(it) })) + walletManager?.apply { + scope.launch { async { addTokens(tokensList) }.await() } + } + } + scope.launch { + walletManagers.forEach { walletManager -> + when (val result = walletManager.safeUpdate()) { + is com.tangem.common.services.Result.Success -> { + val wallet = result.data + wallet.getTokens() + .filter { tokens.contains(it) } + .mapNotNull { token -> wallet.getTokenAmount(token)?.let { Pair(token, it) } } + .forEach { + withContext(Dispatchers.Main) { + store.dispatch(WalletAction.MultiWallet.TokenLoaded(it.second, it.first)) + } + } } + is com.tangem.common.services.Result.Failure -> {} } } } } - - private fun addTokens( - tokens: List, - walletState: WalletState?, - globalState: GlobalState?, - ) { - val scanResponse = globalState?.scanResponse ?: return - - val tokensWithManagers = tokens.map { token -> - val walletManager = walletState?.getWalletManager(token) - ?: globalState.tapWalletManager.walletManagerFactory.makeWalletManagerForApp( - scanResponse, - token.blockchain - )?.also { walletManager -> - store.dispatch(WalletAction.MultiWallet.AddWalletManagers(walletManager)) - store.dispatch(WalletAction.MultiWallet.AddBlockchain(walletManager.wallet.blockchain)) - } - store.dispatch( - WalletAction.LoadFiatRate(currency = Currency.Token(token)) - ) - TokenWithManager(token, walletManager) - } - scope.launch { - tokensWithManagers.forEach { - when (val result = it.walletManager?.addToken(it.token)) { - is Result.Success -> { - store.dispatchOnMain(WalletAction.MultiWallet.TokenLoaded(result.data, it.token)) - } - is Result.Failure -> { - when (val result = it.walletManager.safeUpdate()) { - is com.tangem.common.services.Result.Success -> { - val tokenAmount = result.data.getTokenAmount(it.token) ?: return@launch - store.dispatchOnMain(WalletAction.MultiWallet.TokenLoaded(tokenAmount, it.token)) - } - is com.tangem.common.services.Result.Failure -> { - } - } - } - } - } - } - } - - private data class TokenWithManager(val token: Token, val walletManager: WalletManager?) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt index dc17653642..8c9a06c2e2 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt @@ -88,18 +88,20 @@ class WalletMiddleware { when { action.wallet != null -> { globalState.tapWalletManager.loadFiatRate( - globalState.appCurrency, action.wallet + fiatCurrency = globalState.appCurrency, + wallet = action.wallet, ) } - action.currency != null -> { + action.currencyList != null -> { globalState.tapWalletManager.loadFiatRate( - globalState.appCurrency, action.currency + fiatCurrency = globalState.appCurrency, + currencies = action.currencyList, ) } else -> { globalState.tapWalletManager.loadFiatRate( fiatCurrency = globalState.appCurrency, - currencies = walletState.wallets.map { it.currency } + currencies = walletState.wallets.map { it.currency }, ) } }