From 1716318447b7a6a053dc7d7f1d82dad55a328c47 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 13 May 2024 23:23:11 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../utils/TokenListAnalyticsSender.kt | 2 +- .../utils/WalletWarningsAnalyticsSender.kt | 2 +- .../state/model/WalletPullToRefreshConfig.kt | 8 +++- .../state/utils/WalletLoadingStateFactory.kt | 2 +- .../presentation/wallet/ui/WalletScreen.kt | 12 +++-- .../wallet/utils/ScreenLifecycleProvider.kt | 10 +++-- .../wallet/viewmodels/WalletViewModel.kt | 44 +++++++++++++++++-- .../viewmodels/intents/WalletClickIntents.kt | 16 +++---- 8 files changed, 74 insertions(+), 22 deletions(-) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/TokenListAnalyticsSender.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/TokenListAnalyticsSender.kt index 23f6a0c60d..f0ca57a2b3 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/TokenListAnalyticsSender.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/TokenListAnalyticsSender.kt @@ -33,7 +33,7 @@ internal class TokenListAnalyticsSender @Inject constructor( private val mutex = Mutex() suspend fun send(displayedUiState: WalletState?, userWallet: UserWallet, tokenList: TokenList) { - if (screenLifecycleProvider.isBackground) return + if (screenLifecycleProvider.isBackgroundState.value) return if (displayedUiState == null || displayedUiState.pullToRefreshConfig.isRefreshing) return if (tokenList.totalFiatBalance is TokenList.FiatBalance.Loading) return diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt index e80fc87a24..ae05161912 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt @@ -16,7 +16,7 @@ internal class WalletWarningsAnalyticsSender @Inject constructor( ) { fun send(displayedUiState: WalletState?, newWarnings: List) { - if (screenLifecycleProvider.isBackground) return + if (screenLifecycleProvider.isBackgroundState.value) return if (newWarnings.isEmpty()) return if (displayedUiState == null || displayedUiState.pullToRefreshConfig.isRefreshing) return diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletPullToRefreshConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletPullToRefreshConfig.kt index ca91846374..8bf0e4ea63 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletPullToRefreshConfig.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletPullToRefreshConfig.kt @@ -6,4 +6,10 @@ package com.tangem.feature.wallet.presentation.wallet.state.model * @property isRefreshing state is indicator visible * @property onRefresh lambda be invoked when pulled to refresh */ -data class WalletPullToRefreshConfig(val isRefreshing: Boolean, val onRefresh: () -> Unit) \ No newline at end of file +data class WalletPullToRefreshConfig(val isRefreshing: Boolean, val onRefresh: (ShowRefreshState) -> Unit) { + + @JvmInline + value class ShowRefreshState( + val value: Boolean, + ) +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt index 2dc37ce327..71f0bdd4c4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt @@ -72,7 +72,7 @@ internal class WalletLoadingStateFactory(private val clickIntents: WalletClickIn } private fun createPullToRefreshConfig(): WalletPullToRefreshConfig { - return WalletPullToRefreshConfig(onRefresh = clickIntents::onRefreshSwipe, isRefreshing = false) + return WalletPullToRefreshConfig(onRefresh = { clickIntents.onRefreshSwipe(it.value) }, isRefreshing = false) } private fun UserWallet.toLoadingWalletCardState(): WalletCardState { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index cfeccc9db0..670f85b03a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -135,7 +135,9 @@ private fun WalletContent( .padding(horizontal = horizontalPadding) LazyColumn( - modifier = Modifier.fillMaxSize().testTag(TestTags.WALLET_SCREEN), + modifier = Modifier + .fillMaxSize() + .testTag(TestTags.WALLET_SCREEN), contentPadding = PaddingValues( top = TangemTheme.dimens.spacing8, bottom = TangemTheme.dimens.spacing92, @@ -317,7 +319,9 @@ private fun BaseScaffoldManageTokenRedesign( content = { paddingValues -> val pullRefreshState = rememberPullRefreshState( refreshing = selectedWallet.pullToRefreshConfig.isRefreshing, - onRefresh = selectedWallet.pullToRefreshConfig.onRefresh, + onRefresh = { + selectedWallet.pullToRefreshConfig.onRefresh(WalletPullToRefreshConfig.ShowRefreshState(true)) + }, ) Column( @@ -503,7 +507,9 @@ private fun BaseScaffold( content = { val pullRefreshState = rememberPullRefreshState( refreshing = selectedWallet.pullToRefreshConfig.isRefreshing, - onRefresh = selectedWallet.pullToRefreshConfig.onRefresh, + onRefresh = { + selectedWallet.pullToRefreshConfig.onRefresh(WalletPullToRefreshConfig.ShowRefreshState(true)) + }, ) Box( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/ScreenLifecycleProvider.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/ScreenLifecycleProvider.kt index 5ebccf6c11..f2c721ac87 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/ScreenLifecycleProvider.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/ScreenLifecycleProvider.kt @@ -3,19 +3,21 @@ package com.tangem.feature.wallet.presentation.wallet.utils import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import dagger.hilt.android.scopes.ViewModelScoped +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import javax.inject.Inject @ViewModelScoped internal class ScreenLifecycleProvider @Inject constructor() : DefaultLifecycleObserver { - var isBackground: Boolean = true - private set + private val _isBackgroundState = MutableStateFlow(false) + val isBackgroundState: StateFlow = _isBackgroundState override fun onResume(owner: LifecycleOwner) { - isBackground = false + _isBackgroundState.value = false } override fun onPause(owner: LifecycleOwner) { - isBackground = true + _isBackgroundState.value = true } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index e74ecbdb0b..775bb89a39 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -20,6 +20,7 @@ import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContent import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent.DemonstrateWalletsScrollPreview.Direction +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletPullToRefreshConfig import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState import com.tangem.feature.wallet.presentation.wallet.state.transformers.* import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender @@ -31,7 +32,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.JobHolder import com.tangem.utils.coroutines.saveIn import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.delay +import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -61,7 +62,9 @@ internal class WalletViewModel @Inject constructor( val uiState: StateFlow = stateHolder.uiState private lateinit var router: InnerWalletRouter - private var walletsUpdateJobHolder: JobHolder = JobHolder() + private val walletsUpdateJobHolder = JobHolder() + private val refreshWalletJobHolder = JobHolder() + private var needToRefreshWallet = false init { analyticsEventsHandler.send(WalletScreenAnalyticsEvent.MainScreen.ScreenOpened) @@ -71,6 +74,7 @@ internal class WalletViewModel @Inject constructor( subscribeToUserWalletsUpdates() subscribeOnBalanceHiding() subscribeOnSelectedWalletFlow() + subscribeToScreenBackgroundState() } fun setWalletRouter(router: InnerWalletRouter) { @@ -150,6 +154,36 @@ internal class WalletViewModel @Inject constructor( } } + // We need to update the current wallet if the application was in the background for more than 10 seconds + // and then returned to the foreground + private fun subscribeToScreenBackgroundState() { + screenLifecycleProvider.isBackgroundState + .onEach { isBackground -> + refreshWalletJobHolder.cancel() + when { + isBackground -> needToRefreshTimer() + needToRefreshWallet && !isBackground -> triggerRefreshWallet() + } + } + .launchIn(viewModelScope) + } + + private fun needToRefreshTimer() { + viewModelScope.launch { + delay(REFRESH_WALLET_BACKGROUND_TIMER_MILLIS) + needToRefreshWallet = true + }.saveIn(refreshWalletJobHolder) + } + + private fun triggerRefreshWallet() { + needToRefreshWallet = false + val state = stateHolder.uiState.value + val wallet = state.wallets.getOrNull(state.selectedWalletIndex) ?: return + wallet.pullToRefreshConfig.onRefresh.invoke( + WalletPullToRefreshConfig.ShowRefreshState(false), + ) + } + private suspend fun updateWallets(action: WalletsUpdateActionResolver.Action) { when (action) { is WalletsUpdateActionResolver.Action.InitializeWallets -> initializeWallets(action) @@ -280,7 +314,7 @@ internal class WalletViewModel @Inject constructor( } private fun closeScreen(screen: AppScreen) { - if (!screenLifecycleProvider.isBackground) { + if (!screenLifecycleProvider.isBackgroundState.value) { stateHolder.clear() router.popBackStack(screen = screen) } @@ -296,4 +330,8 @@ internal class WalletViewModel @Inject constructor( ), ) } + + private companion object { + const val REFRESH_WALLET_BACKGROUND_TIMER_MILLIS = 10000L + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletClickIntents.kt index 3cf99441ab..3266406422 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletClickIntents.kt @@ -77,17 +77,17 @@ internal class WalletClickIntents @Inject constructor( } } - fun onRefreshSwipe() { + fun onRefreshSwipe(showRefreshState: Boolean) { when (stateHolder.getSelectedWallet()) { is WalletState.MultiCurrency.Content -> { analyticsEventHandler.send(PortfolioEvent.Refreshed) - refreshMultiCurrencyContent() + refreshMultiCurrencyContent(showRefreshState) } is WalletState.SingleCurrency.Content, is WalletState.Visa.Content, -> { analyticsEventHandler.send(PortfolioEvent.Refreshed) - refreshSingleCurrencyContent() + refreshSingleCurrencyContent(showRefreshState) } is WalletState.MultiCurrency.Locked, is WalletState.SingleCurrency.Locked, @@ -97,14 +97,14 @@ internal class WalletClickIntents @Inject constructor( } fun onReloadClick() { - refreshSingleCurrencyContent() + refreshSingleCurrencyContent(showRefreshState = true) } - private fun refreshMultiCurrencyContent() { + private fun refreshMultiCurrencyContent(showRefreshState: Boolean) { val userWallet = getSelectedWalletSyncUseCase.unwrap() ?: return stateHolder.update( - SetRefreshStateTransformer(userWalletId = userWallet.walletId, isRefreshing = true), + SetRefreshStateTransformer(userWalletId = userWallet.walletId, isRefreshing = showRefreshState), ) viewModelScope.launch(dispatchers.main) { @@ -126,11 +126,11 @@ internal class WalletClickIntents @Inject constructor( // FIXME: refreshSingleCurrencyContent mustn't update the TxHistory and Buttons. It only must fetch primary // currency. Now it not works because GetPrimaryCurrency's subscriber uses .distinctUntilChanged() - private fun refreshSingleCurrencyContent() { + private fun refreshSingleCurrencyContent(showRefreshState: Boolean) { val userWallet = getSelectedWalletSyncUseCase.unwrap() ?: return stateHolder.update( - SetRefreshStateTransformer(userWalletId = userWallet.walletId, isRefreshing = true), + SetRefreshStateTransformer(userWalletId = userWallet.walletId, isRefreshing = showRefreshState), ) viewModelScope.launch(dispatchers.main) {