Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-06 12:26:33 +04:00
parent 7a197ec6c9
commit c662136303
26 changed files with 906 additions and 346 deletions

View file

@ -3,7 +3,9 @@ package com.tangem.domain.account.status.supplier
import com.tangem.domain.account.models.AccountStatusList
import com.tangem.domain.account.status.producer.MultiAccountStatusListProducer
import com.tangem.domain.core.flow.FlowCachingSupplier
import com.tangem.domain.models.wallet.UserWalletId
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
/**
* Supplier that provides a list of [AccountStatusList]s for all user wallets.
@ -18,4 +20,12 @@ abstract class MultiAccountStatusListSupplier(
operator fun invoke(): Flow<List<AccountStatusList>> {
return super.invoke(params = Unit)
}
fun invokeAsMap(): Flow<LinkedHashMap<UserWalletId, AccountStatusList>> = invoke()
.map { accountLists ->
accountLists.associateByTo(
destination = linkedMapOf(),
keySelector = { accountList -> accountList.userWalletId },
)
}
}

View file

@ -0,0 +1,113 @@
package com.tangem.domain.account.status.utils
import com.tangem.domain.account.models.AccountExpandedState
import com.tangem.domain.account.models.AccountList
import com.tangem.domain.account.repository.AccountsExpandedRepository
import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject
import javax.inject.Singleton
// todo swap separate for main and swap
@Singleton
class ExpandedAccountsHolder @Inject constructor(
private val singleAccountListSupplier: SingleAccountListSupplier,
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
private val accountsExpandedRepository: AccountsExpandedRepository,
private val dispatchers: CoroutineDispatcherProvider,
) {
private val actionChannel = MutableSharedFlow<Pair<AccountId, Boolean>>(
extraBufferCapacity = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST,
)
fun expandedAccounts(userWallet: UserWallet): Flow<Set<AccountId>> = expandedAccounts(userWallet.walletId)
fun expandedAccounts(walletId: UserWalletId): Flow<Set<AccountId>> = channelFlow {
val storedState = accountsExpandedRepository.expandedAccounts
.map { it[walletId].orEmpty() }
.stateIn(this)
val isAccountsMode = isAccountsModeEnabledUseCase.invoke()
.stateIn(this)
val initExpandedState = storedState.value
.mapNotNull { it.takeIf { state -> state.isExpanded }?.accountId }
.toSet()
// main state holder
val expandedAccounts = MutableStateFlow(initExpandedState)
var debounceJob: Job? = null
actionChannel
.filter { (accountId, _) -> accountId.userWalletId == walletId }
.filter { debounceJob?.isActive != true }
.onEach { (accountId, isExpand) ->
debounceJob = launch { delay(DEBOUNCE_MILLIS) }
val newState = AccountExpandedState(accountId, isExpand)
launch { accountsExpandedRepository.update(newState) }
if (isExpand) {
expandedAccounts.update { it.plus(accountId) }
} else {
expandedAccounts.update { it.minus(accountId) }
}
}
.launchIn(this)
walletAccounts(walletId).onEach { accountList ->
if (!isAccountsModeEnabledUseCase.invokeSync()) {
accountsExpandedRepository.clearStore()
expandedAccounts.update { emptySet() }
return@onEach
}
val idsSet = accountList.accounts.mapTo(mutableSetOf()) { it.accountId }
accountsExpandedRepository.syncStore(walletId, idsSet)
val isSingleAccount = accountList.accounts.size == 1
val storedMainAccountState = storedState.value
.find { it.accountId == accountList.mainAccount.accountId }
if (isSingleAccount && storedMainAccountState == null) {
// force expand for single and not stored account
expandedAccounts.update { setOf(accountList.mainAccount.accountId) }
}
}.launchIn(this)
combine(
flow = expandedAccounts,
flow2 = isAccountsMode,
transform = { expanded, isAccountMode ->
if (isAccountMode) {
channel.send(expanded)
} else {
channel.send(emptySet())
}
},
).collect()
}
.flowOn(dispatchers.default)
.distinctUntilChanged()
fun expandAccount(accountId: AccountId) {
actionChannel.tryEmit(accountId to true)
}
fun collapseAccount(accountId: AccountId) {
actionChannel.tryEmit(accountId to false)
}
private fun walletAccounts(walletId: UserWalletId): Flow<AccountList> = singleAccountListSupplier(walletId)
companion object {
private const val DEBOUNCE_MILLIS = 200L
}
}