Updated on 2026-08-14
This commit is contained in:
parent
207a2b52df
commit
50af6db261
20 changed files with 418 additions and 147 deletions
|
|
@ -85,7 +85,7 @@ private fun BaseContainer(
|
|||
Surface(
|
||||
modifier = modifier
|
||||
.defaultMinSize(minHeight = TangemTheme.dimens.size62)
|
||||
.wrapContentWidth(),
|
||||
.fillMaxWidth(),
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
color = containerColor,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.core.ui.extensions
|
||||
|
||||
import android.content.res.Resources
|
||||
import androidx.annotation.PluralsRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -121,6 +122,22 @@ fun TextReference.resolveReference(): String {
|
|||
}
|
||||
}
|
||||
|
||||
/** Resolve [TextReference] as [String] using [resources] (non-composable context) */
|
||||
fun TextReference.resolveReference(resources: Resources): String {
|
||||
return when (this) {
|
||||
is TextReference.Res -> resources.getString(id, *formatArgs.toTypedArray())
|
||||
is TextReference.PluralRes -> resources.getQuantityString(id, count, *formatArgs.toTypedArray())
|
||||
is TextReference.Str -> value
|
||||
is TextReference.Combined -> {
|
||||
buildString {
|
||||
refs.forEach {
|
||||
append(it.resolveReference(resources))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Concatenate [this] reference with [ref] */
|
||||
operator fun TextReference.plus(ref: TextReference): TextReference {
|
||||
return when (this) {
|
||||
|
|
|
|||
|
|
@ -5,5 +5,7 @@ package com.tangem.domain.wallets.models
|
|||
*/
|
||||
sealed interface SaveWalletError {
|
||||
|
||||
object CommonError : SaveWalletError
|
||||
object DataError : SaveWalletError
|
||||
|
||||
data class WalletAlreadySaved(val messageId: Int) : SaveWalletError
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import arrow.core.left
|
|||
import arrow.core.right
|
||||
import com.tangem.common.doOnFailure
|
||||
import com.tangem.common.doOnSuccess
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListError
|
||||
import com.tangem.domain.wallets.legacy.WalletsStateHolder
|
||||
import com.tangem.domain.wallets.models.SaveWalletError
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
|
|
@ -19,9 +20,17 @@ import com.tangem.domain.wallets.models.UserWallet
|
|||
class SaveWalletUseCase(private val walletsStateHolder: WalletsStateHolder) {
|
||||
|
||||
suspend operator fun invoke(userWallet: UserWallet, canOverride: Boolean = false): Either<SaveWalletError, Unit> {
|
||||
requireNotNull(walletsStateHolder.userWalletsListManager).save(userWallet, canOverride)
|
||||
val userWalletListManager = walletsStateHolder.userWalletsListManager
|
||||
?: return SaveWalletError.DataError.left()
|
||||
|
||||
userWalletListManager.save(userWallet, canOverride)
|
||||
.doOnSuccess { return Unit.right() }
|
||||
.doOnFailure { return SaveWalletError.CommonError.left() }
|
||||
.doOnFailure {
|
||||
return when (it) {
|
||||
is UserWalletsListError.WalletAlreadySaved -> SaveWalletError.WalletAlreadySaved(it.messageResId)
|
||||
else -> SaveWalletError.DataError
|
||||
}.left()
|
||||
}
|
||||
|
||||
return Unit.right()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -383,6 +383,7 @@ internal object WalletPreviewData {
|
|||
bottomSheetConfig = bottomSheet,
|
||||
tokenActionsBottomSheet = actionsBottomSheet,
|
||||
onManageTokensClick = {},
|
||||
event = consumedEvent(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -432,6 +433,7 @@ internal object WalletPreviewData {
|
|||
),
|
||||
),
|
||||
),
|
||||
event = consumedEvent(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
@Immutable
|
||||
internal sealed class WalletEvent {
|
||||
|
||||
data class ChangeWallet(val index: Int) : WalletEvent()
|
||||
|
||||
data class ShowError(val text: TextReference) : WalletEvent()
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state
|
||||
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.event.StateEvent
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.*
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
@ -23,6 +25,7 @@ internal sealed class WalletMultiCurrencyState : WalletState.ContentState() {
|
|||
override val notifications: ImmutableList<WalletNotification>,
|
||||
override val bottomSheetConfig: TangemBottomSheetConfig?,
|
||||
override val tokensListState: WalletTokensListState,
|
||||
override val event: StateEvent<WalletEvent> = consumedEvent(),
|
||||
val tokenActionsBottomSheet: ActionsBottomSheetConfig?,
|
||||
val onManageTokensClick: () -> Unit,
|
||||
) : WalletMultiCurrencyState()
|
||||
|
|
@ -37,6 +40,7 @@ internal sealed class WalletMultiCurrencyState : WalletState.ContentState() {
|
|||
override val onScanClick: () -> Unit,
|
||||
override val isBottomSheetShow: Boolean = false,
|
||||
override val onBottomSheetDismiss: () -> Unit = {},
|
||||
override val event: StateEvent<WalletEvent> = consumedEvent(),
|
||||
) : WalletMultiCurrencyState(), WalletLockedState {
|
||||
|
||||
override val notifications = persistentListOf(
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
|||
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
|
||||
import com.tangem.core.ui.components.transactions.state.TransactionState
|
||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||
import com.tangem.core.ui.event.StateEvent
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.*
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
|
|
@ -35,6 +37,7 @@ internal sealed class WalletSingleCurrencyState : WalletState.ContentState() {
|
|||
override val bottomSheetConfig: TangemBottomSheetConfig?,
|
||||
override val buttons: PersistentList<WalletManageButton>,
|
||||
override val txHistoryState: TxHistoryState,
|
||||
override val event: StateEvent<WalletEvent> = consumedEvent(),
|
||||
val marketPriceBlockState: MarketPriceBlockState,
|
||||
) : WalletSingleCurrencyState()
|
||||
|
||||
|
|
@ -49,6 +52,7 @@ internal sealed class WalletSingleCurrencyState : WalletState.ContentState() {
|
|||
override val onScanClick: () -> Unit,
|
||||
override val isBottomSheetShow: Boolean = false,
|
||||
override val onBottomSheetDismiss: () -> Unit = {},
|
||||
override val event: StateEvent<WalletEvent> = consumedEvent(),
|
||||
val onExploreClick: () -> Unit,
|
||||
) : WalletSingleCurrencyState(), WalletLockedState {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state
|
||||
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.event.StateEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.*
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
|
|
@ -32,28 +33,49 @@ internal sealed class WalletState {
|
|||
/** Bottom sheet config */
|
||||
abstract val bottomSheetConfig: TangemBottomSheetConfig?
|
||||
|
||||
/** State event */
|
||||
abstract val event: StateEvent<WalletEvent>
|
||||
|
||||
/**
|
||||
* Util function that allow to make a copy
|
||||
*
|
||||
* @param walletsListConfig wallets list config
|
||||
* @param pullToRefreshConfig pull to refresh config
|
||||
* @param event state event
|
||||
*/
|
||||
fun copySealed(
|
||||
walletsListConfig: WalletsListConfig = this.walletsListConfig,
|
||||
pullToRefreshConfig: WalletPullToRefreshConfig = this.pullToRefreshConfig,
|
||||
event: StateEvent<WalletEvent> = this.event,
|
||||
): ContentState {
|
||||
return when (this) {
|
||||
is WalletMultiCurrencyState.Content -> {
|
||||
copy(walletsListConfig = walletsListConfig, pullToRefreshConfig = pullToRefreshConfig)
|
||||
copy(
|
||||
walletsListConfig = walletsListConfig,
|
||||
pullToRefreshConfig = pullToRefreshConfig,
|
||||
event = event,
|
||||
)
|
||||
}
|
||||
is WalletMultiCurrencyState.Locked -> {
|
||||
copy(walletsListConfig = walletsListConfig, pullToRefreshConfig = pullToRefreshConfig)
|
||||
copy(
|
||||
walletsListConfig = walletsListConfig,
|
||||
pullToRefreshConfig = pullToRefreshConfig,
|
||||
event = event,
|
||||
)
|
||||
}
|
||||
is WalletSingleCurrencyState.Content -> {
|
||||
copy(walletsListConfig = walletsListConfig, pullToRefreshConfig = pullToRefreshConfig)
|
||||
copy(
|
||||
walletsListConfig = walletsListConfig,
|
||||
pullToRefreshConfig = pullToRefreshConfig,
|
||||
event = event,
|
||||
)
|
||||
}
|
||||
is WalletSingleCurrencyState.Locked -> {
|
||||
copy(walletsListConfig = walletsListConfig, pullToRefreshConfig = pullToRefreshConfig)
|
||||
copy(
|
||||
walletsListConfig = walletsListConfig,
|
||||
pullToRefreshConfig = pullToRefreshConfig,
|
||||
event = event,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import arrow.core.Either
|
|||
import com.tangem.common.Provider
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.core.ui.event.triggeredEvent
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.common.CardTypesResolver
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
|
|
@ -16,10 +18,7 @@ import com.tangem.domain.txhistory.models.TxHistoryItem
|
|||
import com.tangem.domain.txhistory.models.TxHistoryListError
|
||||
import com.tangem.domain.txhistory.models.TxHistoryStateError
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.ActionsBottomSheetConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory.WalletLoadedTxHistoryConverter
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory.WalletLoadingTxHistoryConverter
|
||||
|
|
@ -36,8 +35,10 @@ import kotlinx.coroutines.flow.Flow
|
|||
* @property currentStateProvider current ui state provider
|
||||
* @property currentCardTypeResolverProvider current card type resolver
|
||||
* @property currentWalletProvider current wallet
|
||||
* @property appCurrencyProvider app currency provider
|
||||
* @property clickIntents screen click intents
|
||||
*/
|
||||
@Suppress("TooManyFunctions")
|
||||
internal class WalletStateFactory(
|
||||
private val currentStateProvider: Provider<WalletState>,
|
||||
private val currentCardTypeResolverProvider: Provider<CardTypesResolver>,
|
||||
|
|
@ -246,4 +247,25 @@ internal class WalletStateFactory(
|
|||
fun getStateByCurrencyStatusError(error: CurrencyStatusError): WalletState {
|
||||
return currencyStatusErrorConverter.convert(error)
|
||||
}
|
||||
|
||||
fun getStateAndTriggerEvent(
|
||||
state: WalletState,
|
||||
event: WalletEvent,
|
||||
setUiState: (WalletState) -> Unit,
|
||||
): WalletState {
|
||||
return when (state) {
|
||||
is WalletState.ContentState -> state.copySealed(
|
||||
event = triggeredEvent(
|
||||
data = event,
|
||||
onConsume = {
|
||||
val currentState = currentStateProvider()
|
||||
if (currentState is WalletState.ContentState) {
|
||||
setUiState(currentState.copySealed(event = consumedEvent()))
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
is WalletState.Initial -> state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -63,7 +63,9 @@ internal class WalletLoadedTxHistoryConverter(
|
|||
private fun convert(items: Flow<PagingData<TxHistoryItem>>): WalletState {
|
||||
return when (val state = currentStateProvider()) {
|
||||
is WalletSingleCurrencyState.Content -> {
|
||||
state.copy(txHistoryState = walletTxHistoryItemFlowConverter.convert(value = items))
|
||||
return state.copy(
|
||||
txHistoryState = walletTxHistoryItemFlowConverter.convert(value = items) ?: state.txHistoryState,
|
||||
)
|
||||
}
|
||||
is WalletMultiCurrencyState.Content,
|
||||
is WalletMultiCurrencyState.Locked,
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ internal class WalletTxHistoryItemFlowConverter(
|
|||
private val currentStateProvider: Provider<WalletState>,
|
||||
private val blockchain: Blockchain,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
) : Converter<Flow<PagingData<TxHistoryItem>>, TxHistoryState> {
|
||||
) : Converter<Flow<PagingData<TxHistoryItem>>, TxHistoryState?> {
|
||||
|
||||
/** Example, 2 Aug, 2023 */
|
||||
private val dateFormatter by lazy {
|
||||
|
|
@ -67,9 +67,9 @@ internal class WalletTxHistoryItemFlowConverter(
|
|||
.withLocale(Locale.getDefault())
|
||||
}
|
||||
|
||||
override fun convert(value: Flow<PagingData<TxHistoryItem>>): TxHistoryState {
|
||||
val state = currentStateProvider() as WalletSingleCurrencyState
|
||||
val txHistoryContent = state.txHistoryState as TxHistoryState.Content
|
||||
override fun convert(value: Flow<PagingData<TxHistoryItem>>): TxHistoryState? {
|
||||
val state = currentStateProvider() as? WalletSingleCurrencyState ?: return null
|
||||
val txHistoryContent = state.txHistoryState as? TxHistoryState.Content ?: return state.txHistoryState
|
||||
|
||||
// FIXME: TxHistoryRepository should send loading transactions
|
||||
// [REDACTED_JIRA]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.tangem.core.ui.event.EventEffect
|
||||
import com.tangem.core.ui.event.StateEvent
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletEvent
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
@Composable
|
||||
internal fun WalletEventEffect(
|
||||
walletsListState: LazyListState,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
event: StateEvent<WalletEvent>,
|
||||
onAutoScrollSet: () -> Unit,
|
||||
) {
|
||||
val resources = LocalContext.current.resources
|
||||
EventEffect(
|
||||
event = event,
|
||||
onTrigger = { value ->
|
||||
when (value) {
|
||||
is WalletEvent.ChangeWallet -> {
|
||||
onAutoScrollSet()
|
||||
delay(timeMillis = 800)
|
||||
walletsListState.animateScrollToItem(index = value.index)
|
||||
}
|
||||
is WalletEvent.ShowError -> {
|
||||
snackbarHostState.showSnackbar(message = value.text.resolveReference(resources))
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -3,13 +3,13 @@ package com.tangem.feature.wallet.presentation.wallet.ui
|
|||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.pullrefresh.pullRefresh
|
||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||
import androidx.compose.material3.FabPosition
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
|
@ -28,6 +28,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencySt
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletBottomSheetConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletPullToRefreshConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState.OrganizeTokensButtonState
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.TokenActionsBottomSheet
|
||||
|
|
@ -50,27 +51,44 @@ internal fun WalletScreen(state: WalletState) {
|
|||
BackHandler(onBack = state.onBackClick)
|
||||
|
||||
when (state) {
|
||||
is WalletState.ContentState -> WalletContent(state = state)
|
||||
is WalletState.ContentState -> {
|
||||
val walletsListState = rememberLazyListState()
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val isAutoScroll = remember { mutableStateOf(value = false) }
|
||||
|
||||
WalletContent(
|
||||
state = state,
|
||||
walletsListState = walletsListState,
|
||||
snackbarHostState = snackbarHostState,
|
||||
isAutoScroll = isAutoScroll,
|
||||
onAutoScrollReset = { isAutoScroll.value = false },
|
||||
)
|
||||
|
||||
WalletEventEffect(
|
||||
walletsListState = walletsListState,
|
||||
snackbarHostState = snackbarHostState,
|
||||
event = state.event,
|
||||
onAutoScrollSet = { isAutoScroll.value = true },
|
||||
)
|
||||
}
|
||||
is WalletState.Initial -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
private fun WalletContent(state: WalletState.ContentState) {
|
||||
val walletsListState = rememberLazyListState()
|
||||
|
||||
BaseScaffold(state = state) { scaffoldPaddings ->
|
||||
private fun WalletContent(
|
||||
state: WalletState.ContentState,
|
||||
walletsListState: LazyListState,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
isAutoScroll: State<Boolean>,
|
||||
onAutoScrollReset: () -> Unit,
|
||||
) {
|
||||
BaseScaffold(state = state, snackbarHostState) { scaffoldPaddings ->
|
||||
val movableItemModifier = Modifier.changeWalletAnimator(walletsListState)
|
||||
val pullRefreshState = rememberPullRefreshState(
|
||||
refreshing = state.pullToRefreshConfig.isRefreshing,
|
||||
onRefresh = state.pullToRefreshConfig.onRefresh,
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues = scaffoldPaddings)
|
||||
.pullRefresh(pullRefreshState),
|
||||
UpdatableContainer(
|
||||
pullToRefreshConfig = state.pullToRefreshConfig,
|
||||
modifier = Modifier.padding(paddingValues = scaffoldPaddings),
|
||||
) {
|
||||
val txHistoryItems = if (state is WalletSingleCurrencyState &&
|
||||
state.txHistoryState is TxHistoryState.Content
|
||||
|
|
@ -126,24 +144,51 @@ private fun WalletContent(state: WalletState.ContentState) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
WalletPullToRefreshIndicator(
|
||||
isRefreshing = state.pullToRefreshConfig.isRefreshing,
|
||||
state = pullRefreshState,
|
||||
modifier = Modifier.align(Alignment.TopCenter),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
WalletBottomSheets(state = state)
|
||||
|
||||
WalletSideEffects(lazyListState = walletsListState, walletsListConfig = state.walletsListConfig)
|
||||
WalletsListEffects(
|
||||
lazyListState = walletsListState,
|
||||
walletsListConfig = state.walletsListConfig,
|
||||
isAutoScroll = isAutoScroll,
|
||||
onAutoScrollReset = onAutoScrollReset,
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
private fun UpdatableContainer(
|
||||
pullToRefreshConfig: WalletPullToRefreshConfig,
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable BoxScope.() -> Unit,
|
||||
) {
|
||||
val pullRefreshState = rememberPullRefreshState(
|
||||
refreshing = pullToRefreshConfig.isRefreshing,
|
||||
onRefresh = pullToRefreshConfig.onRefresh,
|
||||
)
|
||||
|
||||
Box(modifier = modifier.pullRefresh(pullRefreshState)) {
|
||||
content()
|
||||
|
||||
WalletPullToRefreshIndicator(
|
||||
isRefreshing = pullToRefreshConfig.isRefreshing,
|
||||
state = pullRefreshState,
|
||||
modifier = Modifier.align(Alignment.TopCenter),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BaseScaffold(state: WalletState.ContentState, content: @Composable (PaddingValues) -> Unit) {
|
||||
private fun BaseScaffold(
|
||||
state: WalletState.ContentState,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
content: @Composable (PaddingValues) -> Unit,
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = { WalletTopBar(config = state.topBarConfig) },
|
||||
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
|
||||
floatingActionButton = {
|
||||
if (state is WalletMultiCurrencyState.Content) {
|
||||
ManageTokensButton(onManageTokensClick = state.onManageTokensClick)
|
||||
|
|
@ -193,7 +238,7 @@ private fun WalletBottomSheets(state: WalletState) {
|
|||
@Composable
|
||||
private fun WalletScreenPreview_Light(@PreviewParameter(WalletScreenParameterProvider::class) state: WalletState) {
|
||||
TangemTheme {
|
||||
WalletScreen(state)
|
||||
WalletScreen(state = state)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -201,7 +246,7 @@ private fun WalletScreenPreview_Light(@PreviewParameter(WalletScreenParameterPro
|
|||
@Composable
|
||||
private fun WalletScreenPreview_Dark(@PreviewParameter(WalletScreenParameterProvider::class) state: WalletState) {
|
||||
TangemTheme(isDark = true) {
|
||||
WalletScreen(state)
|
||||
WalletScreen(state = state)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.utils.ScrollOffsetCollector
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.utils.WalletsListInteractionsCollector
|
||||
|
||||
@Composable
|
||||
internal fun WalletsListEffects(
|
||||
lazyListState: LazyListState,
|
||||
walletsListConfig: WalletsListConfig,
|
||||
isAutoScroll: State<Boolean>,
|
||||
onAutoScrollReset: () -> Unit,
|
||||
) {
|
||||
LaunchedEffect(key1 = lazyListState, key2 = walletsListConfig.onWalletChange) {
|
||||
snapshotFlow { lazyListState.layoutInfo.visibleItemsInfo }
|
||||
.collect(
|
||||
collector = ScrollOffsetCollector(
|
||||
lazyListState = lazyListState,
|
||||
walletsListConfig = walletsListConfig,
|
||||
isAutoScroll = isAutoScroll,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
lazyListState.interactionSource.interactions.collect(
|
||||
collector = WalletsListInteractionsCollector(onDragStart = onAutoScrollReset),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,11 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components
|
||||
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.animation.rememberSplineBasedDecay
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.snapping.SnapFlingBehavior
|
||||
import androidx.compose.foundation.gestures.snapping.SnapLayoutInfoProvider
|
||||
import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
|
|
@ -10,19 +14,20 @@ import androidx.compose.foundation.lazy.LazyListState
|
|||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.common.WalletCard
|
||||
|
||||
private const val SHORT_SNAP_ELEMENT_COUNT = 50
|
||||
|
||||
/**
|
||||
* Wallets list component
|
||||
*
|
||||
|
|
@ -43,7 +48,7 @@ internal fun WalletsList(config: WalletsListConfig, lazyListState: LazyListState
|
|||
state = lazyListState,
|
||||
contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing16),
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||
flingBehavior = rememberSnapFlingBehavior(lazyListState = lazyListState),
|
||||
flingBehavior = rememberWalletsFlingBehaviour(lazyListState = lazyListState, itemWidth = itemWidth),
|
||||
) {
|
||||
items(
|
||||
items = config.wallets,
|
||||
|
|
@ -60,6 +65,35 @@ internal fun WalletsList(config: WalletsListConfig, lazyListState: LazyListState
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom implementation of fling behaviour that overrides 'shortSnapVelocityThreshold'.
|
||||
* Every user's drag action will similar to a short snap
|
||||
* if drag offset is less than [SHORT_SNAP_ELEMENT_COUNT] * item width.
|
||||
*
|
||||
* @param lazyListState lazy list state
|
||||
* @param itemWidth list item width
|
||||
*
|
||||
* @see rememberSnapFlingBehavior
|
||||
*/
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun rememberWalletsFlingBehaviour(lazyListState: LazyListState, itemWidth: Dp): SnapFlingBehavior {
|
||||
val snappingLayout = remember(lazyListState) { SnapLayoutInfoProvider(lazyListState) }
|
||||
val density = LocalDensity.current
|
||||
val highVelocityApproachSpec: DecayAnimationSpec<Float> = rememberSplineBasedDecay()
|
||||
|
||||
return remember(key1 = snappingLayout, key2 = highVelocityApproachSpec, key3 = density) {
|
||||
SnapFlingBehavior(
|
||||
snapLayoutInfoProvider = snappingLayout,
|
||||
lowVelocityAnimationSpec = tween(durationMillis = 1000, easing = LinearEasing),
|
||||
highVelocityAnimationSpec = highVelocityApproachSpec,
|
||||
snapAnimationSpec = spring(stiffness = Spring.StiffnessMediumLow),
|
||||
density = density,
|
||||
shortSnapVelocityThreshold = itemWidth * SHORT_SNAP_ELEMENT_COUNT,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun Preview_WalletsList_LightTheme() {
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components.common
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.utils.ScrollOffsetCollector
|
||||
|
||||
/**
|
||||
* Wallet screen side effects
|
||||
*
|
||||
* @param lazyListState lazy list state
|
||||
* @param walletsListConfig wallets list config
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Composable
|
||||
internal fun WalletSideEffects(lazyListState: LazyListState, walletsListConfig: WalletsListConfig) {
|
||||
LaunchedEffect(key1 = walletsListConfig.selectedWalletIndex) {
|
||||
if (!lazyListState.isScrollInProgress) {
|
||||
lazyListState.animateScrollToItem(walletsListConfig.selectedWalletIndex)
|
||||
}
|
||||
}
|
||||
|
||||
val dragInteraction = lazyListState.interactionSource.interactions.collectAsState(initial = null)
|
||||
LaunchedEffect(key1 = lazyListState, key2 = walletsListConfig.onWalletChange) {
|
||||
snapshotFlow { lazyListState.layoutInfo.visibleItemsInfo }
|
||||
.collect(
|
||||
collector = ScrollOffsetCollector(
|
||||
lazyListState = lazyListState,
|
||||
dragInteraction = dragInteraction,
|
||||
callback = walletsListConfig.onWalletChange,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +1,58 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.utils
|
||||
|
||||
import androidx.compose.foundation.interaction.DragInteraction
|
||||
import androidx.compose.foundation.interaction.Interaction
|
||||
import androidx.compose.foundation.lazy.LazyListItemInfo
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.runtime.State
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig
|
||||
import kotlinx.coroutines.flow.FlowCollector
|
||||
import kotlin.math.abs
|
||||
|
||||
/**
|
||||
* Flow collector for scroll items tracking.
|
||||
* If first visible item offset is greater than half item size, then [callback] be invoked.
|
||||
* If last visible item offset is greater than half item size, then [callback] be invoked.
|
||||
* If first visible item offset is greater than half item size, then change selected wallet index.
|
||||
* If last visible item offset is greater than half item size, then change selected wallet index.
|
||||
*
|
||||
* @property lazyListState lazy list state
|
||||
* @property dragInteraction current drag interaction
|
||||
* @property callback lambda be invoked when current scroll items is changed
|
||||
* @property lazyListState lazy list state
|
||||
* @property walletsListConfig wallets list config
|
||||
* @property isAutoScroll check if last scrolling is auto scroll
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class ScrollOffsetCollector(
|
||||
private val lazyListState: LazyListState,
|
||||
private val dragInteraction: State<Interaction?>,
|
||||
private val callback: (Int) -> Unit,
|
||||
private val walletsListConfig: WalletsListConfig,
|
||||
private val isAutoScroll: State<Boolean>,
|
||||
) : FlowCollector<List<LazyListItemInfo>> {
|
||||
|
||||
private val LazyListItemInfo.halfItemSize get() = size.div(other = 2)
|
||||
|
||||
private var currentIndex = walletsListConfig.selectedWalletIndex
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun emit(value: List<LazyListItemInfo>) {
|
||||
if (isNotUserInteraction() || value.size <= 1) return
|
||||
// Auto scroll must not change wallet
|
||||
if (isAutoScroll.value) {
|
||||
currentIndex = walletsListConfig.selectedWalletIndex
|
||||
return
|
||||
}
|
||||
|
||||
if (!lazyListState.isScrollInProgress || value.size <= 1) return
|
||||
|
||||
val firstItem = value.firstOrNull() ?: return
|
||||
val lastItem = value.lastOrNull() ?: return
|
||||
|
||||
if (abs(firstItem.offset) > firstItem.halfItemSize) {
|
||||
callback(firstItem.index + 1)
|
||||
val newIndex = firstItem.index + 1
|
||||
currentIndex = newIndex
|
||||
walletsListConfig.onWalletChange(newIndex)
|
||||
} else if (abs(lastItem.offset) > lastItem.halfItemSize) {
|
||||
callback(lastItem.index - 1)
|
||||
val newIndex = lastItem.index - 1
|
||||
currentIndex = newIndex
|
||||
walletsListConfig.onWalletChange(newIndex)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sometimes the list is scrolled programmatically. Example: selecting a specific wallet when a user opens the
|
||||
* screen for the first time or scans a new wallet. Therefore [ScrollOffsetCollector] should not respond to changes.
|
||||
*/
|
||||
private fun isNotUserInteraction(): Boolean {
|
||||
return !lazyListState.isScrollInProgress || dragInteraction.value !is DragInteraction.Start
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.utils
|
||||
|
||||
import androidx.compose.foundation.interaction.DragInteraction
|
||||
import androidx.compose.foundation.interaction.Interaction
|
||||
import kotlinx.coroutines.flow.FlowCollector
|
||||
|
||||
internal class WalletsListInteractionsCollector(
|
||||
private val onDragStart: () -> Unit,
|
||||
) : FlowCollector<Interaction?> {
|
||||
|
||||
override suspend fun emit(value: Interaction?) {
|
||||
if (value is DragInteraction.Start) onDragStart()
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ import com.tangem.core.navigation.AppScreen
|
|||
import com.tangem.core.ui.components.bottomsheets.tokenreceive.AddressModel
|
||||
import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheetConfig
|
||||
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.card.*
|
||||
|
|
@ -30,15 +31,13 @@ import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
|
|||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
|
||||
import com.tangem.domain.userwallets.UserWalletBuilder
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.models.SaveWalletError
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.*
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletLockedState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletBottomSheetConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState
|
||||
|
|
@ -51,6 +50,7 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
|
|
@ -131,6 +131,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
private val buttonsJobHolder = JobHolder()
|
||||
private val notificationsJobHolder = JobHolder()
|
||||
private val refreshContentJobHolder = JobHolder()
|
||||
private val onWalletChangeJobHolder = JobHolder()
|
||||
|
||||
private val walletsUpdateActionResolver = WalletsUpdateActionResolver(
|
||||
currentStateProvider = Provider { uiState },
|
||||
|
|
@ -181,18 +182,17 @@ internal class WalletViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun loadAndUpdateState(index: Int) {
|
||||
uiState = stateFactory.getSkeletonState(wallets = wallets, selectedWalletIndex = index)
|
||||
|
||||
getContentItemsUpdates(index = index)
|
||||
}
|
||||
|
||||
private fun deleteWalletAndUpdateState(action: WalletsUpdateActionResolver.Action.DeleteWallet) {
|
||||
val cacheState = WalletStateCache.getState(userWalletId = action.selectedWalletId)
|
||||
if (cacheState != null) {
|
||||
uiState = stateFactory.getStateWithoutDeletedWallet(cacheState, action)
|
||||
|
||||
if (cacheState.isLoadingState()) {
|
||||
uiState = stateFactory.getStateAndTriggerEvent(
|
||||
state = uiState,
|
||||
event = WalletEvent.ChangeWallet(action.selectedWalletIndex),
|
||||
setUiState = { uiState = it },
|
||||
)
|
||||
getContentItemsUpdates(action.selectedWalletIndex)
|
||||
}
|
||||
} else {
|
||||
|
|
@ -200,6 +200,18 @@ internal class WalletViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun loadAndUpdateState(index: Int) {
|
||||
uiState = stateFactory.getSkeletonState(wallets = wallets, selectedWalletIndex = index)
|
||||
|
||||
uiState = stateFactory.getStateAndTriggerEvent(
|
||||
state = uiState,
|
||||
event = WalletEvent.ChangeWallet(index = index),
|
||||
setUiState = { uiState = it },
|
||||
)
|
||||
|
||||
getContentItemsUpdates(index = index)
|
||||
}
|
||||
|
||||
override fun onBackClick() {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
router.popBackStack(screen = if (shouldSaveUserWalletsUseCase()) AppScreen.Welcome else AppScreen.Home)
|
||||
|
|
@ -215,8 +227,31 @@ internal class WalletViewModel @Inject constructor(
|
|||
|
||||
if (userWallet != null) {
|
||||
saveWalletUseCase(userWallet = userWallet, canOverride = false)
|
||||
.onLeft { saveWalletError ->
|
||||
when (saveWalletError) {
|
||||
is SaveWalletError.DataError -> Unit
|
||||
is SaveWalletError.WalletAlreadySaved -> {
|
||||
uiState = stateFactory.getStateAndTriggerEvent(
|
||||
state = uiState,
|
||||
event = WalletEvent.ShowError(
|
||||
text = TextReference.Res(saveWalletError.messageId),
|
||||
),
|
||||
setUiState = { uiState = it },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.doOnFailure { tangemError ->
|
||||
uiState = stateFactory.getStateAndTriggerEvent(
|
||||
state = uiState,
|
||||
event = WalletEvent.ShowError(
|
||||
text = TextReference.Str(tangemError.customMessage),
|
||||
),
|
||||
setUiState = { uiState = it },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -307,39 +342,45 @@ internal class WalletViewModel @Inject constructor(
|
|||
val state = uiState as? WalletState.ContentState ?: return
|
||||
if (state.walletsListConfig.selectedWalletIndex == index) return
|
||||
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
selectWalletUseCase(getWallet(index = index).walletId)
|
||||
}
|
||||
// Reset the job to avoid a redundant state updating
|
||||
onWalletChangeJobHolder.update(null)
|
||||
|
||||
val cacheState = WalletStateCache.getState(userWalletId = state.walletsListConfig.wallets[index].id)
|
||||
if (cacheState != null && cacheState !is WalletLockedState) {
|
||||
uiState = cacheState.copySealed(
|
||||
walletsListConfig = state.walletsListConfig.copy(
|
||||
selectedWalletIndex = index,
|
||||
wallets = state.walletsListConfig.wallets
|
||||
.mapIndexed { mapIndex, currentWallet ->
|
||||
val cacheWallet = cacheState.walletsListConfig.wallets.getOrNull(mapIndex)
|
||||
|
||||
if (currentWallet is WalletCardState.Loading && cacheWallet != null &&
|
||||
cacheWallet.isLoaded()
|
||||
) {
|
||||
cacheWallet
|
||||
} else {
|
||||
currentWallet
|
||||
}
|
||||
}
|
||||
.toImmutableList(),
|
||||
),
|
||||
pullToRefreshConfig = cacheState.pullToRefreshConfig.copy(isRefreshing = false),
|
||||
)
|
||||
|
||||
if (cacheState.isLoadingState()) {
|
||||
getContentItemsUpdates(index)
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
withContext(dispatchers.io) {
|
||||
selectWalletUseCase(userWalletId = state.walletsListConfig.wallets[index].id)
|
||||
}
|
||||
|
||||
val cacheState = WalletStateCache.getState(userWalletId = state.walletsListConfig.wallets[index].id)
|
||||
if (cacheState != null && cacheState !is WalletLockedState) {
|
||||
uiState = cacheState.copySealed(
|
||||
walletsListConfig = state.walletsListConfig.copy(
|
||||
selectedWalletIndex = index,
|
||||
wallets = state.walletsListConfig.wallets
|
||||
.mapIndexed { mapIndex, currentWallet ->
|
||||
val cacheWallet = cacheState.walletsListConfig.wallets.getOrNull(mapIndex)
|
||||
|
||||
if (currentWallet is WalletCardState.Loading && cacheWallet != null &&
|
||||
cacheWallet.isLoaded()
|
||||
) {
|
||||
cacheWallet
|
||||
} else {
|
||||
currentWallet
|
||||
}
|
||||
}
|
||||
.toImmutableList(),
|
||||
),
|
||||
pullToRefreshConfig = cacheState.pullToRefreshConfig.copy(isRefreshing = false),
|
||||
)
|
||||
|
||||
if (cacheState.isLoadingState()) {
|
||||
getContentItemsUpdates(index)
|
||||
}
|
||||
} else {
|
||||
uiState = stateFactory.getSkeletonState(wallets = wallets, selectedWalletIndex = index)
|
||||
getContentItemsUpdates(index = index)
|
||||
}
|
||||
} else {
|
||||
uiState = stateFactory.getSkeletonState(wallets = wallets, selectedWalletIndex = index)
|
||||
getContentItemsUpdates(index = index)
|
||||
}
|
||||
.saveIn(onWalletChangeJobHolder)
|
||||
}
|
||||
|
||||
private fun WalletCardState.isLoaded(): Boolean {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue