Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-04 14:47:17 +03:00
parent 3a754e96a6
commit 34609aeb8c
10 changed files with 123 additions and 38 deletions

View file

@ -26,6 +26,8 @@ import com.tangem.core.ui.R
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
/**
* A top bar composable that displays a title and optional start and end icons.
@ -34,8 +36,8 @@ import com.tangem.core.ui.res.TangemThemePreviewRedesign
* @param title The title text to be displayed in the center of the top bar.
* @param modifier Modifier to be applied to the top bar.
* @param subtitle Optional subtitle text to be displayed below the title.
* @param startActionUM Optional action data for the start action icon.
* @param endActionUM Optional action data for the end action icon.
* @param startAction Optional action data for the start action icon.
* @param endActions List of end action icons to display on the right side.
* @param titleIconRes Optional drawable resource ID for the icon to be displayed next to the title.
*
[REDACTED_AUTHOR]
@ -45,8 +47,8 @@ fun TangemTopBar(
modifier: Modifier = Modifier,
title: TextReference? = null,
subtitle: TextReference? = null,
startActionUM: TangemTopBarActionUM? = null,
endActionUM: TangemTopBarActionUM? = null,
startAction: TangemTopBarActionUM? = null,
endActions: ImmutableList<TangemTopBarActionUM> = persistentListOf(),
@DrawableRes titleIconRes: Int? = null,
) {
TangemTopBar(
@ -54,13 +56,19 @@ fun TangemTopBar(
subtitle = subtitle,
titleIconRes = titleIconRes,
modifier = modifier,
startContent = if (startActionUM != null) {
{ TangemTopBarActionContent(startActionUM) }
startContent = if (startAction != null) {
{ TangemTopBarActionContent(startAction) }
} else {
null
},
endContent = if (endActionUM != null) {
{ TangemTopBarActionContent(endActionUM) }
endContent = if (endActions.isNotEmpty()) {
{
Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1)) {
endActions.forEach { action ->
TangemTopBarActionContent(action)
}
}
}
} else {
null
},
@ -215,7 +223,17 @@ private fun TangemTopBar_Preview(@PreviewParameter(PreviewProvider::class) param
titleIconRes = params.titleIconRes,
modifier = Modifier.background(TangemTheme.colors2.surface.level1),
startContent = params.startActionUM?.let { { TangemTopBarActionContent(it) } },
endContent = params.endActionUM?.let { { TangemTopBarActionContent(it) } },
endContent = if (params.endActions.isNotEmpty()) {
{
Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1)) {
params.endActions.forEach { action ->
TangemTopBarActionContent(action)
}
}
}
} else {
null
},
)
}
}
@ -225,7 +243,7 @@ private class TangemTopBarPreviewData(
val subtitle: TextReference? = null,
val titleIconRes: Int? = null,
val startActionUM: TangemTopBarActionUM? = null,
val endActionUM: TangemTopBarActionUM? = null,
val endActions: ImmutableList<TangemTopBarActionUM> = persistentListOf(),
)
private class PreviewProvider : PreviewParameterProvider<TangemTopBarPreviewData> {
@ -238,11 +256,11 @@ private class PreviewProvider : PreviewParameterProvider<TangemTopBarPreviewData
onClick = {},
isActionable = false,
),
endActionUM = TangemTopBarActionUM(
endActions = persistentListOf(TangemTopBarActionUM(
iconRes = R.drawable.ic_more_default_24,
onClick = {},
isActionable = false,
),
)),
),
TangemTopBarPreviewData(
title = stringReference("Title"),
@ -253,12 +271,12 @@ private class PreviewProvider : PreviewParameterProvider<TangemTopBarPreviewData
isActionable = true,
ghostModeProgress = 1f,
),
endActionUM = TangemTopBarActionUM(
endActions = persistentListOf(TangemTopBarActionUM(
iconRes = R.drawable.ic_more_default_24,
onClick = {},
isActionable = true,
ghostModeProgress = 1f,
),
)),
),
TangemTopBarPreviewData(
title = stringReference("Title"),
@ -269,12 +287,12 @@ private class PreviewProvider : PreviewParameterProvider<TangemTopBarPreviewData
onClick = {},
isActionable = false,
),
endActionUM = TangemTopBarActionUM(
endActions = persistentListOf(TangemTopBarActionUM(
iconRes = R.drawable.ic_more_default_24,
onClick = {},
isActionable = true,
ghostModeProgress = 1f,
),
)),
),
TangemTopBarPreviewData(
subtitle = stringReference("Subtitle"),
@ -284,19 +302,19 @@ private class PreviewProvider : PreviewParameterProvider<TangemTopBarPreviewData
onClick = {},
isActionable = true,
),
endActionUM = TangemTopBarActionUM(
endActions = persistentListOf(TangemTopBarActionUM(
iconRes = R.drawable.ic_more_default_24,
onClick = {},
isActionable = false,
),
)),
),
TangemTopBarPreviewData(
title = stringReference("Title"),
endActionUM = TangemTopBarActionUM(
endActions = persistentListOf(TangemTopBarActionUM(
iconRes = R.drawable.ic_more_default_24,
onClick = {},
isActionable = false,
),
)),
),
TangemTopBarPreviewData(
title = stringReference("Title"),
@ -335,11 +353,11 @@ private class PreviewProvider : PreviewParameterProvider<TangemTopBarPreviewData
onClick = {},
isActionable = true,
),
endActionUM = TangemTopBarActionUM(
endActions = persistentListOf(TangemTopBarActionUM(
iconRes = R.drawable.ic_more_default_24,
onClick = {},
isActionable = false,
),
)),
),
)
}

View file

@ -50,6 +50,7 @@ import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvid
import com.tangem.features.biometry.AskBiometryComponent
import com.tangem.features.pushnotifications.api.PushNotificationsModelCallbacks
import com.tangem.features.wallet.deeplink.WalletDeepLinkActionListener
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
import com.tangem.utils.Provider
import com.tangem.utils.coroutines.*
import kotlinx.coroutines.*
@ -102,6 +103,7 @@ internal class WalletModel @Inject constructor(
private val appsFlyerStore: AppsFlyerStore,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getWalletIconUseCase: GetWalletIconUseCase,
private val walletFeatureToggles: WalletFeatureToggles,
val screenLifecycleProvider: ScreenLifecycleProvider,
val innerWalletRouter: InnerWalletRouter,
) : Model() {
@ -522,6 +524,7 @@ internal class WalletModel @Inject constructor(
wallets = action.wallets,
clickIntents = clickIntents,
walletImageResolver = walletImageResolver,
isMainScreenQrScanningEnabled = walletFeatureToggles.isMainScreenQrScanningEnabled,
getWalletIconUseCase = getWalletIconUseCase,
),
)

View file

@ -82,9 +82,11 @@ internal interface WalletContentClickIntents {
fun onDisposeExpressStatus()
fun onNFTClick(userWallet: UserWallet)
fun onScanQrClick()
}
@Suppress("LongParameterList", "LargeClass")
@Suppress("LongParameterList", "LargeClass", "TooManyFunctions")
@ModelScoped
internal class WalletContentClickIntentsImplementor @Inject constructor(
private val stateHolder: WalletStateController,
@ -369,4 +371,8 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
router.openNFT(userWallet)
}
override fun onScanQrClick() {
router.openQrScanner()
}
}

View file

@ -11,7 +11,7 @@ import kotlinx.collections.immutable.persistentListOf
@Suppress("LargeClass")
internal object WalletPreviewDataLegacy {
val topBarConfig by lazy { WalletTopBarConfig(onDetailsClick = {}) }
val topBarConfig by lazy { WalletTopBarConfig() }
val walletCardContentState by lazy {
WalletCardState.Content(

View file

@ -161,4 +161,8 @@ internal class DefaultWalletRouter @Inject constructor(
),
)
}
override fun openQrScanner() {
router.push(AppRoute.QrScanning(source = AppRoute.QrScanning.Source.MainScreen))
}
}

View file

@ -86,4 +86,7 @@ internal interface InnerWalletRouter {
/** Open token action sheet */
fun openTokenActionSheet(userWallet: UserWallet, tokenActionList: ImmutableList<TokenActionButtonUM>)
/** Open QR scanner screen */
fun openQrScanner()
}

View file

@ -130,7 +130,7 @@ internal class WalletStateController @Inject constructor(
private fun getInitialState(): WalletScreenState {
return WalletScreenState(
topBarConfig = WalletTopBarConfig(onDetailsClick = {}),
topBarConfig = WalletTopBarConfig(),
selectedWalletIndex = NOT_INITIALIZED_WALLET_INDEX,
wallets = persistentListOf(),
wallets2 = persistentListOf(),

View file

@ -1,8 +1,16 @@
package com.tangem.feature.wallet.presentation.wallet.state.model
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.ds.topbar.TangemTopBarActionUM
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
/**
* Wallet screen top bar config
*
* @property onDetailsClick lambda be invoked when details button is clicked
* @property endActions list of top bar end action buttons (e.g. QR scan, More)
*/
internal data class WalletTopBarConfig(val onDetailsClick: () -> Unit)
@Immutable
internal data class WalletTopBarConfig(
val endActions: ImmutableList<TangemTopBarActionUM> = persistentListOf(),
)

View file

@ -1,7 +1,9 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import com.tangem.core.ui.R as CoreUiR
import com.tangem.common.ui.userwallet.converter.WalletIconUMConverter
import com.tangem.core.ui.ds.button.TangemButtonUM
import com.tangem.core.ui.ds.topbar.TangemTopBarActionUM
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.isLocked
@ -25,6 +27,7 @@ internal class InitializeWalletsTransformer(
private val clickIntents: WalletClickIntents,
private val walletImageResolver: WalletImageResolver,
private val getWalletIconUseCase: GetWalletIconUseCase,
private val isMainScreenQrScanningEnabled: Boolean = false,
) : WalletScreenStateTransformer {
private val walletLoadingStateFactory by lazy {
@ -60,7 +63,20 @@ internal class InitializeWalletsTransformer(
private fun createTopBarConfig(): WalletTopBarConfig {
return WalletTopBarConfig(
onDetailsClick = clickIntents::onDetailsClick,
endActions = listOfNotNull(
if (isMainScreenQrScanningEnabled) {
TangemTopBarActionUM(
iconRes = CoreUiR.drawable.ic_qrcode_scaner_24,
onClick = clickIntents::onScanQrClick,
)
} else {
null
},
TangemTopBarActionUM(
iconRes = CoreUiR.drawable.ic_more_default_24,
onClick = clickIntents::onDetailsClick,
),
).toPersistentList(),
)
}

View file

@ -25,6 +25,7 @@ import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.common.WalletPreviewDataLegacy
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTopBarConfig
import dev.chrisbanes.haze.HazeProgressive
import kotlinx.collections.immutable.persistentListOf
private const val VISIBILITY_THRESHOLD = 0.5f
@ -54,16 +55,11 @@ internal fun WalletTopBar(
TangemTopBar(
title = wrappedBalance,
startActionUM = TangemTopBarActionUM(
startAction = TangemTopBarActionUM(
iconRes = R.drawable.ic_tangem_24,
isActionable = false,
),
endActionUM = TangemTopBarActionUM(
iconRes = R.drawable.ic_more_default_24,
isActionable = true,
onClick = topBarConfig.onDetailsClick,
ghostModeProgress = behavior.state.collapsedFraction,
),
endActions = topBarConfig.endActions,
modifier = Modifier
.statusBarsPadding()
.testTag(MainScreenTestTags.TOP_BAR),
@ -85,8 +81,15 @@ internal fun WalletTopBar(config: WalletTopBarConfig) {
Icon(painter = painterResource(id = R.drawable.img_tangem_logo_90_24), contentDescription = null)
},
actions = {
IconButton(onClick = config.onDetailsClick, modifier = Modifier.testTag(MainScreenTestTags.MORE_BUTTON)) {
Icon(painter = painterResource(id = R.drawable.ic_more_vertical_24), contentDescription = null)
config.endActions.forEach { action ->
if (action.onClick != null) {
IconButton(onClick = action.onClick!!) {
Icon(
painter = painterResource(id = action.iconRes),
contentDescription = null,
)
}
}
}
},
colors = TopAppBarDefaults.topAppBarColors(
@ -115,7 +118,31 @@ private fun Preview_WalletTopBar() {
private fun WalletTopBar_Preview() {
TangemThemePreviewRedesign {
WalletTopBar(
topBarConfig = WalletTopBarConfig(onDetailsClick = {}),
topBarConfig = WalletTopBarConfig(),
walletBalance = stringReference("$ 8923,05"),
behavior = rememberTangemExitUntilCollapsedScrollBehavior(),
)
}
}
@Composable
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun WalletTopBar_WithQrButton_Preview() {
TangemThemePreviewRedesign {
WalletTopBar(
topBarConfig = WalletTopBarConfig(
endActions = persistentListOf(
TangemTopBarActionUM(
iconRes = com.tangem.core.ui.R.drawable.ic_qrcode_scaner_24,
onClick = {},
),
TangemTopBarActionUM(
iconRes = R.drawable.ic_more_default_24,
onClick = {},
),
),
),
walletBalance = stringReference("$ 8923,05"),
behavior = rememberTangemExitUntilCollapsedScrollBehavior(),
)