Updated on 2026-08-14
This commit is contained in:
parent
673774bf7c
commit
73a48924a0
4 changed files with 109 additions and 27 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue