From 73a48924a0beab02cf0062060a7df6ecbfc7c14f Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 17 Apr 2023 23:00:54 +0900 Subject: [PATCH] Updated on 2026-08-14 --- .../states/TokensListStateHolder.kt | 47 ++++++++++---- .../impl/presentation/ui/TokensListScreen.kt | 63 ++++++++++++++++++- .../viewmodels/TokensListViewModel.kt | 25 ++++---- .../com/tangem/core/ui/res/TangemDimens.kt | 1 + 4 files changed, 109 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/states/TokensListStateHolder.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/states/TokensListStateHolder.kt index 669242641f..e083f7991b 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/states/TokensListStateHolder.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/states/TokensListStateHolder.kt @@ -17,6 +17,9 @@ internal sealed interface TokensListStateHolder { /** Loading state */ val isLoading: Boolean + /** Flag that determines if warning block is visible */ + val isDifferentAddressesBlockVisible: Boolean + /** Tokens list */ val tokens: Flow> @@ -26,33 +29,50 @@ internal sealed interface TokensListStateHolder { /** * Util function that allow to make a copy * - * @param toolbarState toolbar state - * @param isLoading loading state - * @param tokens tokens list - * @param onTokensLoadStateChanged callback to be invoked when tokens loading state is been changed + * @param toolbarState toolbar state + * @param isLoading loading state + * @param isDifferentAddressesBlockVisible flag that determines if warning block is visible + * @param tokens tokens list + * @param onTokensLoadStateChanged callback to be invoked when tokens loading state is been changed */ fun copySealed( toolbarState: TokensListToolbarState = this.toolbarState, isLoading: Boolean = this.isLoading, + isDifferentAddressesBlockVisible: Boolean = this.isDifferentAddressesBlockVisible, tokens: Flow> = this.tokens, onTokensLoadStateChanged: (LoadState) -> Unit = this.onTokensLoadStateChanged, ): TokensListStateHolder { return when (this) { - is ManageContent -> copy(toolbarState, isLoading, tokens, onTokensLoadStateChanged) - is ReadContent -> copy(toolbarState, isLoading, tokens, onTokensLoadStateChanged) + is ManageContent -> copy( + toolbarState, + isLoading, + isDifferentAddressesBlockVisible, + tokens, + onTokensLoadStateChanged, + ) + is ReadContent -> copy( + toolbarState, + isLoading, + isDifferentAddressesBlockVisible, + tokens, + onTokensLoadStateChanged, + ) } } /** * State screen that is available only for read * - * @property toolbarState toolbar state - * @property tokens tokens list - * @property onTokensLoadStateChanged callback to be invoked when tokens loading state is been changed + * @property toolbarState toolbar state + * @property isLoading loading state + * @property isDifferentAddressesBlockVisible flag that determines if warning block is visible + * @property tokens tokens list + * @property onTokensLoadStateChanged callback to be invoked when tokens loading state is been changed */ data class ReadContent( override val toolbarState: TokensListToolbarState, override val isLoading: Boolean, + override val isDifferentAddressesBlockVisible: Boolean, override val tokens: Flow>, override val onTokensLoadStateChanged: (LoadState) -> Unit, ) : TokensListStateHolder @@ -60,14 +80,17 @@ internal sealed interface TokensListStateHolder { /** * State screen that is available for read and manage * - * @property toolbarState toolbar state - * @property tokens tokens list - * @property onTokensLoadStateChanged callback to be invoked when tokens loading state is been changed + * @property toolbarState toolbar state + * @property isLoading loading state + * @property isDifferentAddressesBlockVisible flag that determines if warning block is visible + * @property tokens tokens list + * @property onTokensLoadStateChanged callback to be invoked when tokens loading state is been changed * @property onSaveButtonClick callback to be invoked when SaveButton is being clicked */ data class ManageContent( override val toolbarState: TokensListToolbarState, override val isLoading: Boolean, + override val isDifferentAddressesBlockVisible: Boolean, override val tokens: Flow>, override val onTokensLoadStateChanged: (LoadState) -> Unit, val onSaveButtonClick: () -> Unit, diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/TokensListScreen.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/TokensListScreen.kt index 6d4494dcf5..08d9d6978c 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/TokensListScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/TokensListScreen.kt @@ -13,16 +13,25 @@ import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.FabPosition import androidx.compose.material.Scaffold +import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.ExperimentalUnitApi +import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.TextUnitType import androidx.paging.PagingData import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems @@ -55,7 +64,7 @@ internal fun TokensListScreen(stateHolder: TokensListStateHolder) { ) { scaffoldPadding -> val tokens = stateHolder.tokens.collectAsLazyPagingItems() - TokensListContent(tokens, scaffoldPadding) + TokensListContent(stateHolder.isDifferentAddressesBlockVisible, tokens, scaffoldPadding) stateHolder.onTokensLoadStateChanged(tokens.loadState.refresh) @@ -83,7 +92,11 @@ private fun LoadingContent() { @OptIn(ExperimentalComposeUiApi::class) @Composable -private fun TokensListContent(tokens: LazyPagingItems, scaffoldPadding: PaddingValues) { +private fun TokensListContent( + isDifferentAddressesBlockVisible: Boolean, + tokens: LazyPagingItems, + scaffoldPadding: PaddingValues, +) { val state = rememberLazyListState() if (state.isScrollInProgress) { @@ -96,12 +109,55 @@ private fun TokensListContent(tokens: LazyPagingItems, scaffoldP .padding(scaffoldPadding), state = state, ) { + if (isDifferentAddressesBlockVisible) { + item { DifferentAddressesWarning() } + } + items(items = tokens, key = TokenItemState::name) { it?.let { TokenItem(model = it) } } } } +@OptIn(ExperimentalUnitApi::class) +@Composable +private fun DifferentAddressesWarning() { + Box( + modifier = Modifier + .padding(TangemTheme.dimens.spacing16) + .background( + color = TangemColorPalette.Light1, + shape = RoundedCornerShape(TangemTheme.dimens.radius10), + ), + contentAlignment = Alignment.Center, + ) { + val text = stringResource(id = R.string.alert_manage_tokens_addresses_message) + val firstSpaceIndex = text.indexOf(" ") + Text( + text = AnnotatedString( + text = text, + spanStyles = listOf( + AnnotatedString.Range( + SpanStyle(fontWeight = FontWeight.Bold), + start = 0, + end = firstSpaceIndex, + ), + ), + ), + modifier = Modifier.padding( + horizontal = TangemTheme.dimens.spacing16, + vertical = TangemTheme.dimens.spacing8, + ), + color = TangemColorPalette.Dark1, + textAlign = TextAlign.Start, + style = TangemTheme.typography.body2.copy( + letterSpacing = TextUnit(value = 0.5f, type = TextUnitType.Sp), + lineHeight = TextUnit(value = 25f, type = TextUnitType.Sp), + ), + ) + } +} + @Composable private fun SaveChangesButton(onClick: () -> Unit) { PrimaryButton( @@ -127,6 +183,7 @@ private fun Preview_TokensListScreen_Loading() { onAddCustomTokenClick = {}, ), isLoading = true, + isDifferentAddressesBlockVisible = false, tokens = emptyFlow(), onTokensLoadStateChanged = {}, ), @@ -147,6 +204,7 @@ private fun Preview_TokensListScreen_Manage() { onAddCustomTokenClick = {}, ), isLoading = false, + isDifferentAddressesBlockVisible = true, tokens = flow { emit( PagingData.from( @@ -176,6 +234,7 @@ private fun Preview_TokensListScreen_Read() { onSearchButtonClick = {}, ), isLoading = false, + isDifferentAddressesBlockVisible = false, tokens = flow { emit( PagingData.from( diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt index d709e49df6..543e23d996 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt @@ -11,6 +11,7 @@ import androidx.paging.map import com.tangem.blockchain.common.Blockchain import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.ui.extensions.getActiveIconRes +import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation import com.tangem.domain.common.extensions.canHandleToken import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.tap.common.extensions.fullNameWithoutTestnet @@ -26,7 +27,9 @@ import com.tangem.tap.features.tokens.impl.presentation.states.TokenItemState import com.tangem.tap.features.tokens.impl.presentation.states.TokensListStateHolder import com.tangem.tap.features.tokens.impl.presentation.states.TokensListToolbarState import com.tangem.tap.features.tokens.legacy.redux.TokenWithBlockchain +import com.tangem.tap.features.tokens.legacy.redux.TokensAction import com.tangem.tap.proxy.AppStateHolder +import com.tangem.tap.store import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider import com.tangem.utils.coroutines.Debouncer import com.tangem.wallet.R @@ -34,9 +37,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch import kotlinx.coroutines.plus -import kotlinx.coroutines.withContext import javax.inject.Inject import com.tangem.blockchain.common.Token as BlockchainToken @@ -71,13 +72,12 @@ internal class TokensListViewModel @Inject constructor( private val changedTokensList: MutableList = args.mainScreenTokenList.toMutableList() private val changedBlockchainList: MutableList = args.mainScreenBlockchainList.toMutableList() - private var saveButtonPressed = false - private fun getInitialUiState(): TokensListStateHolder { return if (args.isManageAccess) { TokensListStateHolder.ManageContent( toolbarState = getInitialToolbarState(), isLoading = true, + isDifferentAddressesBlockVisible = isDifferentAddressesBlockVisible(), tokens = getInitialTokensList(), onTokensLoadStateChanged = actionsHandler::onTokensLoadStateChanged, onSaveButtonClick = actionsHandler::onSaveButtonClick, @@ -86,6 +86,7 @@ internal class TokensListViewModel @Inject constructor( TokensListStateHolder.ReadContent( toolbarState = getInitialToolbarState(), isLoading = true, + isDifferentAddressesBlockVisible = isDifferentAddressesBlockVisible(), tokens = getInitialTokensList(), onTokensLoadStateChanged = actionsHandler::onTokensLoadStateChanged, ) @@ -109,6 +110,10 @@ internal class TokensListViewModel @Inject constructor( } } + private fun isDifferentAddressesBlockVisible(): Boolean { + return reduxStateHolder.scanResponse?.card?.useOldStyleDerivation == true + } + private fun getInitialTokensList(searchText: String = ""): Flow> { if (searchText.isNotEmpty()) analyticsSender.sendWhenTokenSearched() @@ -237,18 +242,12 @@ internal class TokensListViewModel @Inject constructor( } fun onTokensLoadStateChanged(state: LoadState) { - uiState = uiState.copySealed(isLoading = state is LoadState.Loading || saveButtonPressed) - if (saveButtonPressed) saveButtonPressed = false + uiState = uiState.copySealed(isLoading = state is LoadState.Loading) } fun onSaveButtonClick() { - viewModelScope.launch(dispatchers.main) { - saveButtonPressed = true - analyticsSender.sendWhenSaveButtonClicked() - uiState = uiState.copySealed(isLoading = true) - withContext(dispatchers.io) { interactor.saveChanges(changedTokensList, changedBlockchainList) } - router.popBackStack() - } + analyticsSender.sendWhenSaveButtonClicked() + store.dispatch(TokensAction.SaveChanges(changedTokensList, changedBlockchainList)) } private fun onSearchValueChange(newValue: String) { diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt index 7a5dcfec58..256d81f8e8 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt @@ -25,6 +25,7 @@ data class TangemDimens internal constructor( val radius4: Dp = 4.dp, val radius6: Dp = 6.dp, val radius8: Dp = 8.dp, + val radius10: Dp = 10.dp, val radius12: Dp = 12.dp, val radius16: Dp = 16.dp, val radius28: Dp = 28.dp,