Updated on 2026-08-14

This commit is contained in:
Tangem 2026-05-13 17:45:24 +02:00
parent 0b78c692ee
commit 16ebea436f
10 changed files with 101 additions and 79 deletions

View file

@ -94,16 +94,17 @@ fun TangemHeaderRow(
AnimatedVisibility(
visible = subtitle != null,
) {
val wrappedSubtitle = remember(this) { requireNotNull(subtitle) }
Text(
text = wrappedSubtitle.orMaskWithStars(isBalanceHidden).resolveAnnotatedReference(),
style = TangemTheme.typography2.captionSemibold12,
color = TangemTheme.colors2.text.neutral.secondary,
maxLines = 1,
modifier = Modifier
.testTag(tag = TokenElementsTestTags.TOKEN_CRYPTO_AMOUNT)
.padding(start = TangemTheme.dimens2.x1),
)
if (subtitle != null) {
Text(
text = subtitle.orMaskWithStars(isBalanceHidden).resolveAnnotatedReference(),
style = TangemTheme.typography2.captionSemibold12,
color = TangemTheme.colors2.text.neutral.secondary,
maxLines = 1,
modifier = Modifier
.testTag(tag = TokenElementsTestTags.TOKEN_CRYPTO_AMOUNT)
.padding(start = TangemTheme.dimens2.x1),
)
}
}
SpacerWMax()
TangemRowTail(tangemRowTailUM = tailUM)
@ -165,16 +166,17 @@ fun TangemHeaderRow(
AnimatedVisibility(
visible = subtitle != null,
) {
val wrappedSubtitle = remember(this) { requireNotNull(subtitle) }
Text(
text = wrappedSubtitle.orMaskWithStars(isBalanceHidden).resolveAnnotatedReference(),
style = TangemTheme.typography2.captionSemibold12,
color = TangemTheme.colors2.text.neutral.secondary,
maxLines = 1,
modifier = Modifier
.testTag(tag = TokenElementsTestTags.TOKEN_CRYPTO_AMOUNT)
.padding(start = TangemTheme.dimens2.x1),
)
if (subtitle != null) {
Text(
text = subtitle.orMaskWithStars(isBalanceHidden).resolveAnnotatedReference(),
style = TangemTheme.typography2.captionSemibold12,
color = TangemTheme.colors2.text.neutral.secondary,
maxLines = 1,
modifier = Modifier
.testTag(tag = TokenElementsTestTags.TOKEN_CRYPTO_AMOUNT)
.padding(start = TangemTheme.dimens2.x1),
)
}
}
SpacerWMax()
TangemRowTail(

View file

@ -263,41 +263,41 @@ private fun TangemTopBarTitle(title: TextReference?, @DrawableRes titleIconRes:
enter = slideInVertically(initialOffsetY = { it / 2 }) + fadeIn(),
exit = slideOutVertically(targetOffsetY = { it / 2 }) + fadeOut(),
) {
val wrappedTitle = remember(this) { requireNotNull(title) }
Row(
horizontalArrangement = Arrangement.spacedBy(
space = TangemTheme.dimens2.x1,
alignment = Alignment.CenterHorizontally,
),
verticalAlignment = Alignment.CenterVertically,
) {
AnimatedVisibility(
visible = titleIconRes != null,
label = "Title Icon Visibility",
if (title != null) {
Row(
horizontalArrangement = Arrangement.spacedBy(
space = TangemTheme.dimens2.x1,
alignment = Alignment.CenterHorizontally,
),
verticalAlignment = Alignment.CenterVertically,
) {
val wrappedTitleIconRes = remember(this) {
requireNotNull(titleIconRes)
AnimatedVisibility(
visible = titleIconRes != null,
label = "Title Icon Visibility",
) {
val wrappedTitleIconRes = remember(this) {
requireNotNull(titleIconRes)
}
Icon(
imageVector = ImageVector.vectorResource(id = wrappedTitleIconRes),
contentDescription = null,
tint = TangemTheme.colors2.graphic.neutral.primary,
modifier = Modifier.size(TangemTheme.dimens2.x4),
)
}
Icon(
imageVector = ImageVector.vectorResource(id = wrappedTitleIconRes),
contentDescription = null,
tint = TangemTheme.colors2.graphic.neutral.primary,
modifier = Modifier.size(TangemTheme.dimens2.x4),
Text(
text = title.resolveAnnotatedReference(),
color = TangemTheme.colors2.text.neutral.primary,
style = TangemTheme.typography2.headingSemibold17,
textAlign = TextAlign.Center,
maxLines = 1,
autoSize = TextAutoSize.StepBased(
minFontSize = TangemTheme.typography2.captionRegular12.fontSize,
maxFontSize = TangemTheme.typography2.headingSemibold17.fontSize,
),
)
}
Text(
text = wrappedTitle.resolveAnnotatedReference(),
color = TangemTheme.colors2.text.neutral.primary,
style = TangemTheme.typography2.headingSemibold17,
textAlign = TextAlign.Center,
maxLines = 1,
autoSize = TextAutoSize.StepBased(
minFontSize = TangemTheme.typography2.captionRegular12.fontSize,
maxFontSize = TangemTheme.typography2.headingSemibold17.fontSize,
),
)
}
}
}

View file

@ -13,8 +13,8 @@ object StringsSigns {
const val INFINITY_SIGN = ""
const val NON_BREAKING_SPACE = '\u00A0'
const val PERCENT = "%"
const val THREE_STARS = "\u2217\u2217\u2217"
const val ASTERISK = "\u2217"
const val THREE_STARS = "***"
const val ASTERISK = "*"
const val PASSWORD_VISUAL_CHAR = '\u2022'
const val APPROXIMATE = ""
const val WHITE_SPACE = " "

View file

@ -99,7 +99,6 @@ internal class DefaultSearchComponent(
params = SearchTokenSelectorComponent.Params(
entries = config.entries,
appCurrency = config.appCurrency,
isBalanceHidden = config.isBalanceHidden,
onTokenSelected = config.onTokenSelected,
onDismiss = config.onDismiss,
),

View file

@ -8,7 +8,6 @@ internal sealed interface SearchBottomSheetRoute {
data class TokenSelector(
val entries: List<UserAssetEntry>,
val appCurrency: AppCurrency,
val isBalanceHidden: Boolean,
val onTokenSelected: (UserAssetEntry) -> Unit,
val onDismiss: () -> Unit,
) : SearchBottomSheetRoute

View file

@ -39,7 +39,6 @@ internal class SearchTokenSelectorComponent @AssistedInject constructor(
data class Params(
val entries: List<UserAssetEntry>,
val appCurrency: AppCurrency,
val isBalanceHidden: Boolean,
val onTokenSelected: (UserAssetEntry) -> Unit,
val onDismiss: () -> Unit,
)

View file

@ -229,7 +229,6 @@ internal class SearchModel @Inject constructor(
SearchBottomSheetRoute.TokenSelector(
entries = grouped.entries,
appCurrency = currentAppCurrency.value,
isBalanceHidden = isBalanceHidden.value,
onTokenSelected = ::onTokenSelectedFromGroup,
onDismiss = { bottomSheetNavigation.dismiss() },
),

View file

@ -6,25 +6,31 @@ import com.tangem.common.ui.userwallet.converter.WalletIconUMConverter
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.ui.ds.image.DeviceIconUM
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.usecase.GetWalletIconUseCase
import com.tangem.features.feed.components.search.SearchTokenSelectorComponent
import com.tangem.features.feed.model.search.state.TokenSelectorStateController
import com.tangem.features.feed.model.search.state.transformers.BuildTokenSelectorSectionsTransformer
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import javax.inject.Inject
@Suppress("LongParameterList")
@Stable
@ModelScoped
internal class SearchTokenSelectorModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
paramsContainer: ParamsContainer,
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
userWalletsListRepository: UserWalletsListRepository,
private val stateController: TokenSelectorStateController,
private val userWalletsListRepository: UserWalletsListRepository,
private val getWalletIconUseCase: GetWalletIconUseCase,
private val walletIconUMConverter: WalletIconUMConverter,
) : Model() {
@ -35,25 +41,37 @@ internal class SearchTokenSelectorModel @Inject constructor(
get() = stateController.uiState
init {
modelScope.launch(dispatchers.default) {
val requiredWalletIds = params.entries.map { it.userWalletId }.toSet()
val walletIcons = userWalletsListRepository.userWallets
.filterNotNull()
.first()
.filter { it.walletId in requiredWalletIds }
.associate { wallet ->
wallet.walletId to walletIconUMConverter.convert(getWalletIconUseCase(wallet))
}
val requiredWalletIds = params.entries.map { it.userWalletId }.toSet()
val walletIconsFlow = userWalletsListRepository.userWallets
.filterNotNull()
.map { wallets ->
wallets
.filter { it.walletId in requiredWalletIds }
.associate { wallet ->
wallet.walletId to walletIconUMConverter.convert(getWalletIconUseCase(wallet))
}
}
stateController.update(
BuildTokenSelectorSectionsTransformer(
entries = params.entries,
appCurrency = params.appCurrency,
isBalanceHidden = params.isBalanceHidden,
walletIcons = walletIcons,
onTokenSelected = params.onTokenSelected,
),
)
modelScope.launch(dispatchers.default) {
combine(
walletIconsFlow,
getBalanceHidingSettingsUseCase.isBalanceHidden(),
::Pair,
).collect { (walletIcons, isBalanceHidden) ->
rebuildSections(isBalanceHidden, walletIcons)
}
}
}
private fun rebuildSections(isBalanceHidden: Boolean, walletIcons: Map<UserWalletId, DeviceIconUM>) {
stateController.update(
BuildTokenSelectorSectionsTransformer(
entries = params.entries,
appCurrency = params.appCurrency,
isBalanceHidden = isBalanceHidden,
walletIcons = walletIcons,
onTokenSelected = params.onTokenSelected,
),
)
}
}

View file

@ -187,6 +187,7 @@ private fun WalletContent2(
WalletTopBar(
topBarConfig = state.topBarConfig,
walletBalance = walletBalance,
isBalanceHidden = state.isHidingMode,
behavior = behavior,
)
},

View file

@ -26,6 +26,7 @@ import com.tangem.core.ui.ds.topbar.TangemTopBarActionUM
import com.tangem.core.ui.ds.topbar.collapsing.TangemCollapsingAppBarBehavior
import com.tangem.core.ui.ds.topbar.collapsing.rememberTangemExitUntilCollapsedScrollBehavior
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.orMaskWithStars
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.LocalRootBackgroundColor
import com.tangem.core.ui.res.TangemTheme
@ -46,12 +47,14 @@ private const val VISIBILITY_THRESHOLD = 0.5f
*
* @param topBarConfig top bar config
* @param walletBalance wallet balance text reference
* @param isBalanceHidden whether the balance must be masked with stars
* @param behavior collapsing behavior
*/
@Composable
internal fun WalletTopBar(
topBarConfig: WalletTopBarConfig,
walletBalance: TextReference?,
isBalanceHidden: Boolean,
behavior: TangemCollapsingAppBarBehavior,
) {
Surface(
@ -63,8 +66,8 @@ internal fun WalletTopBar(
derivedStateOf { behavior.state.collapsedFraction > VISIBILITY_THRESHOLD }
}
val wrappedBalance = remember(walletBalance, isWrappedBalanceShown) {
walletBalance.takeIf { isWrappedBalanceShown }
val wrappedBalance = remember(walletBalance, isWrappedBalanceShown, isBalanceHidden) {
walletBalance?.orMaskWithStars(isBalanceHidden).takeIf { isWrappedBalanceShown }
}
TangemTopBar(
@ -173,6 +176,7 @@ private fun WalletTopBar_Preview() {
WalletTopBar(
topBarConfig = WalletTopBarConfig(),
walletBalance = stringReference("$ 8923,05"),
isBalanceHidden = false,
behavior = rememberTangemExitUntilCollapsedScrollBehavior(),
)
}
@ -197,6 +201,7 @@ private fun WalletTopBar_WithQrButton_Preview() {
),
),
walletBalance = stringReference("$ 8923,05"),
isBalanceHidden = false,
behavior = rememberTangemExitUntilCollapsedScrollBehavior(),
)
}