diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index 3e1d581e0c..5dfb53248c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -20,7 +20,7 @@ internal object WalletPreviewData { val walletTopBarConfig = WalletTopBarConfig(onScanCardClick = {}, onMoreClick = {}) val walletCardContentState = WalletCardState.Content( - id = UUID.randomUUID().toString(), + id = UserWalletId(UUID.randomUUID().toString()), title = "Wallet 1", balance = "8923,05 $", additionalInfo = "3 cards • Seed enabled", @@ -29,7 +29,7 @@ internal object WalletPreviewData { ) val walletCardLoadingState = WalletCardState.Loading( - id = UUID.randomUUID().toString(), + id = UserWalletId(UUID.randomUUID().toString()), title = "Wallet 1", additionalInfo = "3 cards • Seed enabled", imageResId = R.drawable.ill_businessman_3d, @@ -37,7 +37,7 @@ internal object WalletPreviewData { ) val walletCardHiddenContentState = WalletCardState.HiddenContent( - id = UUID.randomUUID().toString(), + id = UserWalletId(UUID.randomUUID().toString()), title = "Wallet 1", additionalInfo = "3 cards • Seed enabled", imageResId = R.drawable.ill_businessman_3d, @@ -45,7 +45,7 @@ internal object WalletPreviewData { ) val walletCardErrorState = WalletCardState.Error( - id = UUID.randomUUID().toString(), + id = UserWalletId(UUID.randomUUID().toString()), title = "Wallet 1", additionalInfo = "3 cards • Seed enabled", imageResId = R.drawable.ill_businessman_3d, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index 2406ada436..2f30c3d355 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -4,6 +4,7 @@ import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.hilt.navigation.compose.hiltViewModel @@ -40,8 +41,9 @@ internal class DefaultWalletRouter(private val navigationStateHolder: Navigation startDestination = WalletScreens.WALLET.name, ) { composable(WalletScreens.WALLET.name) { - val viewModel = hiltViewModel() - .apply { router = this@DefaultWalletRouter } + val viewModel = hiltViewModel().apply { router = this@DefaultWalletRouter } + LocalLifecycleOwner.current.lifecycle.addObserver(observer = viewModel) + WalletScreen(state = viewModel.uiState) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletCardState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletCardState.kt index a04b0e3f45..b1f88360b1 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletCardState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletCardState.kt @@ -2,13 +2,14 @@ package com.tangem.feature.wallet.presentation.wallet.state import androidx.annotation.DrawableRes import androidx.compose.runtime.Immutable +import com.tangem.domain.wallets.models.UserWalletId /** Wallet card state */ @Immutable internal sealed interface WalletCardState { /** Id */ - val id: String + val id: UserWalletId /** Title */ val title: String @@ -34,7 +35,7 @@ internal sealed interface WalletCardState { * @property balance wallet balance */ data class Content( - override val id: String, + override val id: UserWalletId, override val title: String, override val additionalInfo: String, override val imageResId: Int?, @@ -52,7 +53,7 @@ internal sealed interface WalletCardState { * @property onClick lambda be invoked when wallet card is clicked */ data class Loading( - override val id: String, + override val id: UserWalletId, override val title: String, override val additionalInfo: String, override val imageResId: Int?, @@ -69,7 +70,7 @@ internal sealed interface WalletCardState { * @property onClick lambda be invoked when wallet card is clicked */ data class HiddenContent( - override val id: String, + override val id: UserWalletId, override val title: String, override val additionalInfo: String, override val imageResId: Int?, @@ -86,7 +87,7 @@ internal sealed interface WalletCardState { * @property onClick lambda be invoked when wallet card is clicked */ data class Error( - override val id: String, + override val id: UserWalletId, override val title: String, override val additionalInfo: String, override val imageResId: Int?, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt index 66785bd0ee..169139dacd 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt @@ -2,7 +2,9 @@ package com.tangem.feature.wallet.presentation.wallet.state import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.core.ui.components.marketprice.MarketPriceBlockState +import com.tangem.domain.wallets.models.UserWalletId import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf /** * Wallet screen state holder @@ -26,6 +28,44 @@ internal sealed class WalletStateHolder( open val bottomSheet: WalletBottomSheetConfig? = null, ) { + fun copySealed( + onBackClick: () -> Unit = this.onBackClick, + topBarConfig: WalletTopBarConfig = this.topBarConfig, + walletsListConfig: WalletsListConfig = this.walletsListConfig, + pullToRefreshConfig: WalletPullToRefreshConfig = this.pullToRefreshConfig, + contentItems: ImmutableList = this.contentItems, + notifications: ImmutableList = this.notifications, + bottomSheet: WalletBottomSheetConfig? = this.bottomSheet, + ): WalletStateHolder { + return when (this) { + is MultiCurrencyContent -> this.copy( + onBackClick = onBackClick, + topBarConfig = topBarConfig, + walletsListConfig = walletsListConfig, + pullToRefreshConfig = pullToRefreshConfig, + contentItems = contentItems as ImmutableList, + notifications = notifications, + bottomSheet = bottomSheet, + ) + is SingleCurrencyContent -> this.copy( + onBackClick = onBackClick, + topBarConfig = topBarConfig, + walletsListConfig = walletsListConfig, + pullToRefreshConfig = pullToRefreshConfig, + contentItems = contentItems as ImmutableList, + notifications = notifications, + bottomSheet = bottomSheet, + ) + is UnlockWalletContent -> this.copy( + onBackClick = onBackClick, + topBarConfig = topBarConfig, + walletsListConfig = walletsListConfig, + pullToRefreshConfig = pullToRefreshConfig, + ) + is Loading -> copy(onBackClick = onBackClick) + } + } + /** * Multi currency wallet content state * @@ -44,7 +84,7 @@ internal sealed class WalletStateHolder( override val pullToRefreshConfig: WalletPullToRefreshConfig, override val contentItems: ImmutableList, override val notifications: ImmutableList, - override val bottomSheet: WalletBottomSheetConfig, + override val bottomSheet: WalletBottomSheetConfig? = null, val onOrganizeTokensClick: () -> Unit, ) : WalletStateHolder( onBackClick = onBackClick, @@ -75,7 +115,7 @@ internal sealed class WalletStateHolder( override val pullToRefreshConfig: WalletPullToRefreshConfig, override val contentItems: ImmutableList, override val notifications: ImmutableList, - override val bottomSheet: WalletBottomSheetConfig, + override val bottomSheet: WalletBottomSheetConfig? = null, val buttons: ImmutableList, val marketPriceBlockState: MarketPriceBlockState, ) : WalletStateHolder( @@ -87,4 +127,67 @@ internal sealed class WalletStateHolder( notifications = notifications, bottomSheet = bottomSheet, ) + + /** + * Unlock wallet content state + * + * @property onBackClick lambda be invoked when back button is clicked + * @property topBarConfig top bar config + * @property walletsListConfig wallets list config + * @property onUnlockWalletsNotificationClick lambda be invoked when unlock wallets notification is clicked + * @property onBottomSheetDismissRequest lambda be invoked when bottom sheet is dismissed + * @property onUnlockClick lambda be invoked when unlock button is clicked + * @property onScanClick lambda be invoked when scan card button is clicked + */ + data class UnlockWalletContent( + override val onBackClick: () -> Unit, + override val topBarConfig: WalletTopBarConfig, + override val walletsListConfig: WalletsListConfig, + override val pullToRefreshConfig: WalletPullToRefreshConfig, + val onUnlockWalletsNotificationClick: () -> Unit, + val onBottomSheetDismissRequest: () -> Unit, + val onUnlockClick: () -> Unit, + val onScanClick: () -> Unit, + ) : WalletStateHolder( + onBackClick = onBackClick, + topBarConfig = topBarConfig, + walletsListConfig = walletsListConfig, + pullToRefreshConfig = pullToRefreshConfig, + contentItems = persistentListOf(WalletContentItemState.Loading), + notifications = persistentListOf(WalletNotification.UnlockWallets(onUnlockWalletsNotificationClick)), + bottomSheet = WalletBottomSheetConfig( + isShow = false, + onDismissRequest = onBottomSheetDismissRequest, + content = WalletBottomSheetConfig.BottomSheetContentConfig.UnlockWallets( + onUnlockClick = onUnlockClick, + onScanClick = onScanClick, + ), + ), + ) + + /** + * Loading state + * + * @property onBackClick lambda be invoked when back button is clicked + */ + data class Loading(override val onBackClick: () -> Unit) : WalletStateHolder( + onBackClick = onBackClick, + topBarConfig = WalletTopBarConfig(onScanCardClick = {}, onMoreClick = {}), + walletsListConfig = WalletsListConfig( + selectedWalletIndex = 0, + wallets = persistentListOf( + WalletCardState.Loading( + id = UserWalletId(stringValue = ""), + title = "", + additionalInfo = "", + imageResId = null, + ), + ), + onWalletChange = {}, + ), + pullToRefreshConfig = WalletPullToRefreshConfig(isRefreshing = false, onRefresh = {}), + contentItems = persistentListOf(WalletContentItemState.Loading), + notifications = persistentListOf(), + bottomSheet = null, + ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/builder/WalletStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/builder/WalletStateFactory.kt new file mode 100644 index 0000000000..21bf52bee0 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/builder/WalletStateFactory.kt @@ -0,0 +1,109 @@ +package com.tangem.feature.wallet.presentation.wallet.state.builder + +import com.tangem.common.Provider +import com.tangem.domain.common.util.cardTypesResolver +import com.tangem.domain.wallets.models.UserWallet +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.feature.wallet.presentation.common.WalletPreviewData +import com.tangem.feature.wallet.presentation.router.InnerWalletRouter +import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver +import com.tangem.feature.wallet.presentation.wallet.state.* +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList + +internal class WalletStateFactory( + private val routerProvider: Provider, + private val onScanCardClick: () -> Unit, + private val onWalletChange: (Int) -> Unit, + private val onRefreshSwipe: () -> Unit, +) { + + fun getInitialState(): WalletStateHolder = WalletStateHolder.Loading(onBackClick = ::onBackClick) + + fun getContentState(wallets: List): WalletStateHolder { + val cardTypeResolver = requireNotNull(wallets.firstOrNull()).scanResponse.cardTypesResolver + + return if (cardTypeResolver.isMultiwalletAllowed()) { + createMultiCurrencyState(wallets) + } else { + createSingleCurrencyState(wallets) + } + } + + private fun createMultiCurrencyState(wallets: List): WalletStateHolder.MultiCurrencyContent { + return WalletStateHolder.MultiCurrencyContent( + onBackClick = ::onBackClick, + topBarConfig = createTopBarConfig(), + walletsListConfig = createWalletsListConfig(wallets), + pullToRefreshConfig = createPullToRefreshConfig(), + contentItems = persistentListOf(), + notifications = persistentListOf(), // TODO: create notifications + bottomSheet = WalletBottomSheetConfig( + // TODO: check notifications + isShow = false, + onDismissRequest = {}, + content = WalletBottomSheetConfig.BottomSheetContentConfig.LikeTangemApp( + onRateTheAppClick = {}, + onShareClick = {}, + ), + ), + onOrganizeTokensClick = routerProvider()::openOrganizeTokensScreen, + ) + } + + private fun createSingleCurrencyState(wallets: List): WalletStateHolder.SingleCurrencyContent { + return WalletStateHolder.SingleCurrencyContent( + onBackClick = ::onBackClick, + topBarConfig = createTopBarConfig(), + walletsListConfig = createWalletsListConfig(wallets), + pullToRefreshConfig = createPullToRefreshConfig(), + contentItems = persistentListOf(), + notifications = persistentListOf(), // TODO: create notifications + bottomSheet = WalletBottomSheetConfig( + // TODO: check notifications + isShow = false, + onDismissRequest = {}, + content = WalletBottomSheetConfig.BottomSheetContentConfig.LikeTangemApp( + onRateTheAppClick = {}, + onShareClick = {}, + ), + ), + buttons = WalletPreviewData.singleWalletScreenState.buttons, // TODO: create buttons + // TODO: create market price block + marketPriceBlockState = WalletPreviewData.singleWalletScreenState.marketPriceBlockState, + ) + } + + private fun onBackClick() = routerProvider().popBackStack() + + private fun createTopBarConfig(): WalletTopBarConfig { + return WalletTopBarConfig( + onScanCardClick = onScanCardClick, + onMoreClick = routerProvider()::openDetailsScreen, + ) + } + + private fun createWalletsListConfig(wallets: List): WalletsListConfig { + return WalletsListConfig( + selectedWalletIndex = 0, + wallets = wallets.map { wallet -> + WalletCardState.Loading( + id = if (wallet.scanResponse.cardTypesResolver.isMultiwalletAllowed()) { // TODO + UserWalletId("123") + } else { + UserWalletId("321") + }, + // TODO: wallet.walletId, + title = wallet.name, + additionalInfo = "", // TODO + imageResId = WalletImageResolver.resolve(cardTypesResolver = wallet.scanResponse.cardTypesResolver), + ) + }.toImmutableList(), + onWalletChange = onWalletChange, + ) + } + + private fun createPullToRefreshConfig(): WalletPullToRefreshConfig { + return WalletPullToRefreshConfig(isRefreshing = false, onRefresh = onRefreshSwipe) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt index 6c39b86f10..3f162e41f0 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt @@ -19,10 +19,8 @@ import androidx.compose.ui.tooling.preview.Preview 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.WalletCardState import com.tangem.feature.wallet.presentation.wallet.state.WalletsListConfig import com.tangem.feature.wallet.presentation.wallet.ui.utils.ScrollOffsetCollector -import kotlinx.coroutines.InternalCoroutinesApi /** * Wallets list component @@ -32,7 +30,7 @@ import kotlinx.coroutines.InternalCoroutinesApi * [REDACTED_AUTHOR] */ -@OptIn(ExperimentalFoundationApi::class, InternalCoroutinesApi::class) +@OptIn(ExperimentalFoundationApi::class) @Composable internal fun WalletsList(config: WalletsListConfig, lazyListState: LazyListState, modifier: Modifier = Modifier) { val horizontalCardPadding = TangemTheme.dimens.spacing16 @@ -45,7 +43,7 @@ internal fun WalletsList(config: WalletsListConfig, lazyListState: LazyListState horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), flingBehavior = rememberSnapFlingBehavior(lazyListState = lazyListState), ) { - items(items = config.wallets, key = WalletCardState::id) { state -> + items(items = config.wallets, key = { it.id.stringValue }) { state -> WalletCard(state = state, modifier = Modifier.width(itemWidth)) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/decorations/WalletContentItemDecoration.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/decorations/WalletContentItemDecoration.kt index ea04e144c4..b06ad40f02 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/decorations/WalletContentItemDecoration.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/decorations/WalletContentItemDecoration.kt @@ -12,21 +12,27 @@ import com.tangem.core.ui.res.TangemTheme */ internal fun Modifier.walletContentItemDecoration(currentIndex: Int, lastIndex: Int): Modifier = composed { val modifierWithHorizontalPadding = this.padding(horizontal = TangemTheme.dimens.spacing16) - when (currentIndex) { - 0 -> { + val isSingleItem = currentIndex == 0 && lastIndex == 0 + when { + isSingleItem -> { + modifierWithHorizontalPadding + .padding(top = TangemTheme.dimens.spacing14) + .clip(shape = TangemTheme.shapes.roundedCornersXMedium) + } + currentIndex == 0 -> { modifierWithHorizontalPadding .padding(top = TangemTheme.dimens.spacing14) .clip( - RoundedCornerShape( + shape = RoundedCornerShape( topStart = TangemTheme.dimens.radius16, topEnd = TangemTheme.dimens.radius16, ), ) } - lastIndex -> { + currentIndex == lastIndex -> { modifierWithHorizontalPadding .clip( - RoundedCornerShape( + shape = RoundedCornerShape( bottomStart = TangemTheme.dimens.radius16, bottomEnd = TangemTheme.dimens.radius16, ), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt index af7cf9373e..1db861142b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt @@ -19,6 +19,9 @@ internal class TokenListToWalletStateConverter( return when (currentState) { is MultiCurrencyContent -> currentState.updateWithTokenList(value) is SingleCurrencyContent -> currentState.updateWithTokenList(value) + is WalletStateHolder.Loading, + is WalletStateHolder.UnlockWalletContent, + -> currentState } } 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 c606c9e404..a369895e5b 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 @@ -1,35 +1,33 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels -import android.util.Log import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import androidx.lifecycle.* import arrow.core.Either +import com.tangem.common.Provider import com.tangem.common.doOnFailure import com.tangem.common.doOnSuccess +import com.tangem.core.ui.components.transactions.TransactionState import com.tangem.domain.card.GetAccessCodeSavingStatusUseCase import com.tangem.domain.card.GetBiometricsStatusUseCase import com.tangem.domain.card.ScanCardProcessor import com.tangem.domain.card.SetAccessCodeRequestPolicyUseCase import com.tangem.domain.tokens.GetTokenListUseCase -import com.tangem.domain.userwallets.UserWalletBuilder -import com.tangem.domain.wallets.usecase.SaveWalletUseCase import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.model.TokenList -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.common.WalletPreviewData +import com.tangem.domain.userwallets.UserWalletBuilder +import com.tangem.domain.wallets.usecase.GetWalletsUseCase +import com.tangem.domain.wallets.usecase.SaveWalletUseCase +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.WalletStateHolder -import com.tangem.feature.wallet.presentation.wallet.state.WalletTopBarConfig -import com.tangem.feature.wallet.presentation.wallet.utils.LoadingItemsProvider.getLoadingMultiCurrencyTokens +import com.tangem.feature.wallet.presentation.wallet.state.* +import com.tangem.feature.wallet.presentation.wallet.state.builder.WalletStateFactory import com.tangem.feature.wallet.presentation.wallet.utils.TokenListErrorToWalletStateConverter import com.tangem.feature.wallet.presentation.wallet.utils.TokenListToWalletStateConverter import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import javax.inject.Inject @@ -48,50 +46,66 @@ internal class WalletViewModel @Inject constructor( private val setAccessCodeRequestPolicyUseCase: SetAccessCodeRequestPolicyUseCase, private val getAccessCodeSavingStatusUseCase: GetAccessCodeSavingStatusUseCase, private val getTokenListUseCase: GetTokenListUseCase, + private val getWalletsUseCase: GetWalletsUseCase, private val scanCardProcessor: ScanCardProcessor, private val dispatchers: CoroutineDispatcherProvider, -) : ViewModel() { +) : ViewModel(), DefaultLifecycleObserver { /** Feature router */ var router: InnerWalletRouter by Delegates.notNull() + private val stateFactory = WalletStateFactory( + routerProvider = Provider { router }, + onScanCardClick = ::onScanCardClick, + onWalletChange = ::changeWallet, + onRefreshSwipe = ::refreshContent, + ) + /** Screen state */ - var uiState by mutableStateOf(getInitialState()) + var uiState by mutableStateOf(stateFactory.getInitialState()) private set - private var getTokenListJob: Job? = null - set(value) { - field?.cancel() - field = value + override fun onCreate(owner: LifecycleOwner) { + getWalletsUseCase() + .distinctUntilChanged() + .flowWithLifecycle(owner.lifecycle) + .onEach { wallets -> + if (wallets.isEmpty()) return@onEach + + uiState = stateFactory.getContentState(wallets = wallets) + updateContentItems() + } + .flowOn(dispatchers.io) + .launchIn(viewModelScope) + } + + @OptIn(ExperimentalCoroutinesApi::class) + private fun updateContentItems() { + getTokenListUseCase( + userWalletId = uiState.walletsListConfig.wallets.get( + index = uiState.walletsListConfig.selectedWalletIndex, + ).id, + ) + .distinctUntilChanged() + .mapLatest(::updateStateWithTokenListOrError) + .onEach { + uiState = it.copySealed( + pullToRefreshConfig = uiState.pullToRefreshConfig.copy(isRefreshing = getRefreshingStatus()), + ) + } + .flowOn(dispatchers.io) + .launchIn(viewModelScope) + } + + private fun getRefreshingStatus(): Boolean { + return uiState.contentItems.any { state -> + val isMultiCurrencyItem = state as? WalletContentItemState.MultiCurrencyItem.Token + val isSingleCurrencyItem = state as? WalletContentItemState.SingleCurrencyItem.Transaction + + isMultiCurrencyItem?.state is TokenItemState.Loading || + isSingleCurrencyItem?.state is TransactionState.Loading || + state is WalletContentItemState.Loading } - - // TODO: [REDACTED_TASK_KEY] Use production data instead of WalletPreviewData - private fun getInitialState(): WalletStateHolder { - val state = WalletPreviewData.multicurrencyWalletScreenState.copy( - onBackClick = ::onBackClick, - topBarConfig = createTopBarConfig(), - walletsListConfig = WalletPreviewData.multicurrencyWalletScreenState.walletsListConfig.copy( - onWalletChange = ::selectWallet, - ), - contentItems = getLoadingMultiCurrencyTokens(), - ) - - val selectedWalletIndex = state.walletsListConfig.selectedWalletIndex - val selectedWalletId = WalletPreviewData.wallets.keys.elementAt(selectedWalletIndex) - launchGetTokenListJob(selectedWalletId) - - return state - } - - private fun onBackClick() { - router.popBackStack() - } - - private fun createTopBarConfig(): WalletTopBarConfig { - return WalletTopBarConfig( - onScanCardClick = ::onScanCardClick, - onMoreClick = { router.openDetailsScreen() }, - ) } private fun onScanCardClick() { @@ -124,12 +138,9 @@ internal class WalletViewModel @Inject constructor( } } - // TODO: [REDACTED_TASK_KEY] Use production data instead of WalletPreviewData - private fun selectWallet(index: Int) { + private fun changeWallet(index: Int) { if (uiState.walletsListConfig.selectedWalletIndex == index) return - Log.i("WalletViewModel", "selectWallet: $index") - uiState = when (val state = uiState) { is WalletStateHolder.MultiCurrencyContent -> state.copy( walletsListConfig = uiState.walletsListConfig.copy(selectedWalletIndex = index), @@ -137,19 +148,18 @@ internal class WalletViewModel @Inject constructor( is WalletStateHolder.SingleCurrencyContent -> state.copy( walletsListConfig = uiState.walletsListConfig.copy(selectedWalletIndex = index), ) + is WalletStateHolder.UnlockWalletContent -> state.copy( + walletsListConfig = uiState.walletsListConfig.copy(selectedWalletIndex = index), + ) + is WalletStateHolder.Loading -> state } - val selectedWalletId = WalletPreviewData.wallets.keys.elementAt(index) - launchGetTokenListJob(selectedWalletId) + updateContentItems() } - private fun launchGetTokenListJob(userWalletId: UserWalletId) { - getTokenListJob = getTokenListUseCase(userWalletId) - .distinctUntilChanged() - .mapLatest(::updateStateWithTokenListOrError) - .onEach { uiState = it } - .flowOn(Dispatchers.Default) - .launchIn(viewModelScope) + private fun refreshContent() { + uiState = uiState.copySealed(pullToRefreshConfig = uiState.pullToRefreshConfig.copy(isRefreshing = true)) + updateContentItems() } private fun updateStateWithTokenListOrError(tokenList: Either): WalletStateHolder {