Updated on 2026-08-14

This commit is contained in:
Tangem 2024-05-13 23:23:11 +03:00
parent 4a29162e11
commit 1716318447
8 changed files with 74 additions and 22 deletions

View file

@ -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

View file

@ -16,7 +16,7 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
) {
fun send(displayedUiState: WalletState?, newWarnings: List<WalletNotification>) {
if (screenLifecycleProvider.isBackground) return
if (screenLifecycleProvider.isBackgroundState.value) return
if (newWarnings.isEmpty()) return
if (displayedUiState == null || displayedUiState.pullToRefreshConfig.isRefreshing) return

View file

@ -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)
data class WalletPullToRefreshConfig(val isRefreshing: Boolean, val onRefresh: (ShowRefreshState) -> Unit) {
@JvmInline
value class ShowRefreshState(
val value: Boolean,
)
}

View file

@ -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 {

View file

@ -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(

View file

@ -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<Boolean> = _isBackgroundState
override fun onResume(owner: LifecycleOwner) {
isBackground = false
_isBackgroundState.value = false
}
override fun onPause(owner: LifecycleOwner) {
isBackground = true
_isBackgroundState.value = true
}
}

View file

@ -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<WalletScreenState> = 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
}
}

View file

@ -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) {