Updated on 2026-08-14

This commit is contained in:
Tangem 2023-04-17 23:00:54 +09:00
parent 673774bf7c
commit 73a48924a0
4 changed files with 109 additions and 27 deletions

View file

@ -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<PagingData<TokenItemState>>
@ -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<PagingData<TokenItemState>> = 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<PagingData<TokenItemState>>,
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<PagingData<TokenItemState>>,
override val onTokensLoadStateChanged: (LoadState) -> Unit,
val onSaveButtonClick: () -> Unit,

View file

@ -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<TokenItemState>, scaffoldPadding: PaddingValues) {
private fun TokensListContent(
isDifferentAddressesBlockVisible: Boolean,
tokens: LazyPagingItems<TokenItemState>,
scaffoldPadding: PaddingValues,
) {
val state = rememberLazyListState()
if (state.isScrollInProgress) {
@ -96,12 +109,55 @@ private fun TokensListContent(tokens: LazyPagingItems<TokenItemState>, 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(

View file

@ -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<TokenWithBlockchain> = args.mainScreenTokenList.toMutableList()
private val changedBlockchainList: MutableList<Blockchain> = 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<PagingData<TokenItemState>> {
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) {

View file

@ -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,