diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/rows/ChainRow.kt b/core/ui/src/main/java/com/tangem/core/ui/components/rows/ChainRow.kt index c57b6ebc1a..7b8f2c9fff 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/rows/ChainRow.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/rows/ChainRow.kt @@ -25,15 +25,8 @@ import com.tangem.core.ui.res.TangemThemePreview * */ @Composable fun ChainRow(model: ChainRowUM, modifier: Modifier = Modifier, action: @Composable BoxScope.() -> Unit = {}) { - RowContentContainer( - modifier = modifier - .heightIn(min = TangemTheme.dimens.size68) - .padding(vertical = TangemTheme.dimens.spacing8) - .padding( - start = TangemTheme.dimens.spacing8, - end = TangemTheme.dimens.spacing12, - ), - horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), + ChainRowContainer( + modifier = modifier, icon = { CurrencyIcon( state = model.icon, @@ -57,6 +50,28 @@ fun ChainRow(model: ChainRowUM, modifier: Modifier = Modifier, action: @Composab ) } +@Composable +inline fun ChainRowContainer( + icon: @Composable BoxScope.() -> Unit, + text: @Composable BoxScope.() -> Unit, + action: @Composable BoxScope.() -> Unit, + modifier: Modifier = Modifier, +) { + RowContentContainer( + modifier = modifier + .heightIn(min = TangemTheme.dimens.size68) + .padding(vertical = TangemTheme.dimens.spacing8) + .padding( + start = TangemTheme.dimens.spacing8, + end = TangemTheme.dimens.spacing12, + ), + horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), + icon = icon, + text = text, + action = action, + ) +} + // region Preview @Preview(showBackground = true, widthDp = 360) @Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/rows/RowComponents.kt b/core/ui/src/main/java/com/tangem/core/ui/components/rows/RowComponents.kt index f93f42b41d..ede73396a0 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/rows/RowComponents.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/rows/RowComponents.kt @@ -12,7 +12,7 @@ import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme @Composable -internal inline fun RowContentContainer( +inline fun RowContentContainer( icon: @Composable BoxScope.() -> Unit, text: @Composable BoxScope.() -> Unit, action: @Composable BoxScope.() -> Unit, diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewManageTokensComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewManageTokensComponent.kt index 76be782c89..ff77045832 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewManageTokensComponent.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/preview/PreviewManageTokensComponent.kt @@ -25,7 +25,9 @@ import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update -internal class PreviewManageTokensComponent : ManageTokensComponent { +internal class PreviewManageTokensComponent( + private val isLoading: Boolean = false, +) : ManageTokensComponent { private val changedItemsIds: MutableSet = mutableSetOf() @@ -94,10 +96,14 @@ internal class PreviewManageTokensComponent : ManageTokensComponent { } private fun initItems() = List(size = 30) { index -> - if (index < 2) { - getCustomItem(index) + if (isLoading) { + CurrencyItemUM.Loading(index) } else { - getBasicItem(index) + if (index < 2) { + getCustomItem(index) + } else { + getBasicItem(index) + } } }.toPersistentList() @@ -138,8 +144,8 @@ internal class PreviewManageTokensComponent : ManageTokensComponent { network = Network( id = Network.ID(networkIndex.toString()), backendId = networkIndex.toString(), - name = "", - currencySymbol = "", + name = "Network $networkIndex", + currencySymbol = "N$networkIndex", derivationPath = Network.DerivationPath.Card(""), isTestnet = false, standardType = Network.StandardType.ERC20, @@ -164,7 +170,9 @@ internal class PreviewManageTokensComponent : ManageTokensComponent { CurrencyItemUM.Basic.NetworksUM.Collapsed }, ) - is CurrencyItemUM.Custom -> return + is CurrencyItemUM.Custom, + is CurrencyItemUM.Loading, + -> return } previewState.update { state -> @@ -199,7 +207,9 @@ internal class PreviewManageTokensComponent : ManageTokensComponent { item.copy(networks = updatedNetworks) } - is CurrencyItemUM.Custom -> return + is CurrencyItemUM.Custom, + is CurrencyItemUM.Loading, + -> return } val id = "${currencyIndex}_$networkIndex" diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/item/CurrencyItemUM.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/item/CurrencyItemUM.kt index b61c576c2f..d81b14e587 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/item/CurrencyItemUM.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/entity/item/CurrencyItemUM.kt @@ -40,4 +40,12 @@ internal sealed class CurrencyItemUM { override val icon: CurrencyIconState, val onRemoveClick: () -> Unit, ) : CurrencyItemUM() + + class Loading(val index: Int) : CurrencyItemUM() { + + override val id: ManagedCryptoCurrency.ID = ManagedCryptoCurrency.ID(value = "loading_$index") + override val name: String = "loading" + override val symbol: String = "loading" + override val icon: CurrencyIconState = CurrencyIconState.Loading + } } \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt index 030e583e14..5c4d924942 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/model/ManageTokensModel.kt @@ -28,7 +28,7 @@ import com.tangem.pagination.BatchFetchResult import com.tangem.pagination.PaginationStatus import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch @@ -91,7 +91,7 @@ internal class ManageTokensModel @Inject constructor( popBack = router::pop, isInitialBatchLoading = true, isNextBatchLoading = false, - items = persistentListOf(), + items = getLoadingItems(), topBar = ManageTokensTopBarUM.ReadContent( title = resourceReference(R.string.common_search_tokens), onBackButtonClick = router::pop, @@ -112,7 +112,7 @@ internal class ManageTokensModel @Inject constructor( popBack = router::pop, isInitialBatchLoading = true, isNextBatchLoading = false, - items = persistentListOf(), + items = getLoadingItems(), topBar = ManageTokensTopBarUM.ManageContent( title = resourceReference(id = R.string.main_manage_tokens), onBackButtonClick = router::pop, @@ -175,9 +175,12 @@ internal class ManageTokensModel @Inject constructor( is PaginationStatus.InitialLoading, -> { if (state.search.isActive) { - state + state.copySealed( + items = getLoadingItems(), + ) } else { state.copySealed( + items = getLoadingItems(), isInitialBatchLoading = true, ) } @@ -224,6 +227,12 @@ internal class ManageTokensModel @Inject constructor( } } + private fun getLoadingItems(): ImmutableList { + return List(size = 10) { index -> + CurrencyItemUM.Loading(index) + }.toPersistentList() + } + private fun consumeScrollToTopEvent() { this.state.update { state -> state.copySealed( diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/ManageTokensScreen.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/ManageTokensScreen.kt index 1cff9a28cb..d84cc432eb 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/ManageTokensScreen.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/ui/ManageTokensScreen.kt @@ -29,20 +29,24 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.compose.ui.unit.dp import androidx.compose.ui.util.fastForEachIndexed -import com.tangem.core.ui.components.BottomFade -import com.tangem.core.ui.components.PrimaryButtonIconEnd -import com.tangem.core.ui.components.TangemSwitch +import com.tangem.core.ui.components.* import com.tangem.core.ui.components.appbar.TangemTopAppBar import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM import com.tangem.core.ui.components.buttons.SecondarySmallButton import com.tangem.core.ui.components.buttons.SmallButtonConfig +import com.tangem.core.ui.components.currency.icon.CurrencyIcon +import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.components.fields.SearchBar import com.tangem.core.ui.components.fields.entity.SearchBarUM import com.tangem.core.ui.components.list.InfiniteListHandler import com.tangem.core.ui.components.rows.ArrowRow import com.tangem.core.ui.components.rows.BlockchainRow import com.tangem.core.ui.components.rows.ChainRow +import com.tangem.core.ui.components.rows.ChainRowContainer import com.tangem.core.ui.components.rows.model.BlockchainRowUM import com.tangem.core.ui.components.rows.model.ChainRowUM import com.tangem.core.ui.components.snackbar.TangemSnackbarHost @@ -53,6 +57,7 @@ import com.tangem.core.ui.res.LocalSnackbarHostState import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.utils.WindowInsetsZero +import com.tangem.features.managetokens.component.ManageTokensComponent import com.tangem.features.managetokens.component.preview.PreviewManageTokensComponent import com.tangem.features.managetokens.entity.item.CurrencyItemUM import com.tangem.features.managetokens.entity.item.CurrencyItemUM.Basic.NetworksUM @@ -183,14 +188,6 @@ private fun Content(state: ManageTokensUM, modifier: Modifier = Modifier) { BottomFade(modifier = Modifier.align(Alignment.BottomCenter)) } - Crossfade(targetState = state.isInitialBatchLoading, label = "ManageTokensLoadingContent") { isVisible -> - if (isVisible) { - ProgressIndicator( - modifier = Modifier.fillMaxSize(), - ) - } - } - EventEffect(event = state.scrollToTop) { listState.animateScrollToItem(index = 0) } @@ -234,6 +231,11 @@ private fun Currencies( item = item, ) } + is CurrencyItemUM.Loading -> { + LoadingItem( + modifier = Modifier.fillMaxWidth(), + ) + } } } @@ -265,6 +267,30 @@ private fun ProgressIndicator(modifier: Modifier = Modifier) { } } +@Composable +private fun LoadingItem(modifier: Modifier = Modifier) { + ChainRowContainer( + modifier = modifier, + icon = { + CurrencyIcon(CurrencyIconState.Loading) + }, + text = { + TextShimmer( + modifier = Modifier.width(70.dp), + style = TangemTheme.typography.subtitle2, + ) + }, + action = { + RectangleShimmer( + modifier = Modifier.size( + width = 24.dp, + height = 16.dp, + ), + ) + }, + ) +} + @Composable private fun CustomCurrencyItem(item: CurrencyItemUM.Custom, modifier: Modifier = Modifier) { ChainRow( @@ -393,9 +419,19 @@ private fun NetworksList( @Preview(showBackground = true, widthDp = 360, heightDp = 800) @Preview(showBackground = true, widthDp = 360, heightDp = 800, uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable -private fun Preview_ManageTokens() { +private fun Preview_ManageTokens( + @PreviewParameter(PreviewManageTokensComponentProvider::class) component: ManageTokensComponent, +) { TangemThemePreview { - PreviewManageTokensComponent().Content(Modifier.fillMaxWidth()) + component.Content(Modifier.fillMaxWidth()) } } + +private class PreviewManageTokensComponentProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + PreviewManageTokensComponent(), + PreviewManageTokensComponent(isLoading = true), + ) +} // endregion Preview \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/ui/CurrencyItemOperations.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/ui/CurrencyItemOperations.kt index 557b49d960..a539a0524d 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/ui/CurrencyItemOperations.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/utils/ui/CurrencyItemOperations.kt @@ -15,7 +15,9 @@ internal fun CurrencyItemUM.toggleExpanded( if (currency !is ManagedCryptoCurrency.Token) return this return when (this) { - is CurrencyItemUM.Custom -> this + is CurrencyItemUM.Custom, + is CurrencyItemUM.Loading, + -> this is CurrencyItemUM.Basic -> { val isExpanded = networks !is NetworksUM.Expanded @@ -35,7 +37,9 @@ internal fun CurrencyItemUM.toggleExpanded( internal fun CurrencyItemUM.update(currency: ManagedCryptoCurrency): CurrencyItemUM { return when (this) { - is CurrencyItemUM.Custom -> this + is CurrencyItemUM.Custom, + is CurrencyItemUM.Loading, + -> this is CurrencyItemUM.Basic -> { if (currency !is ManagedCryptoCurrency.Token) { return this