Updated on 2026-08-14

This commit is contained in:
Tangem 2025-06-25 12:48:32 +03:00
parent 0ff4768da9
commit b9bb9e1086
21 changed files with 789 additions and 3 deletions

View file

@ -224,6 +224,8 @@ dependencies {
implementation(projects.features.hotWallet.impl) implementation(projects.features.hotWallet.impl)
implementation(projects.features.kyc.api) implementation(projects.features.kyc.api)
implementation(projects.features.kyc.impl) implementation(projects.features.kyc.impl)
implementation(projects.features.welcome.api)
implementation(projects.features.welcome.impl)
/** AndroidX libraries */ /** AndroidX libraries */
implementation(deps.androidx.core.ktx) implementation(deps.androidx.core.ktx)

View file

@ -164,7 +164,7 @@ private fun NameAndInfo(
@Suppress("MagicNumber") @Suppress("MagicNumber")
@Composable @Composable
private fun CardImage(imageState: UserWalletItemUM.ImageState, modifier: Modifier = Modifier) { fun CardImage(imageState: UserWalletItemUM.ImageState, modifier: Modifier = Modifier) {
val imageModifier = modifier val imageModifier = modifier
.width(TangemTheme.dimens.size36) .width(TangemTheme.dimens.size36)
.height(TangemTheme.dimens.size24) .height(TangemTheme.dimens.size24)

View file

@ -28,6 +28,7 @@ import androidx.compose.ui.unit.sp
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.utils.StringsSigns.PASSWORD_VISUAL_CHAR import com.tangem.utils.StringsSigns.PASSWORD_VISUAL_CHAR
import kotlinx.coroutines.delay
import kotlin.random.Random import kotlin.random.Random
@Composable @Composable
@ -74,6 +75,11 @@ fun PinTextField(
) )
}, },
) )
LaunchedEffect(Unit) {
delay(timeMillis = 200)
focusRequester.requestFocus()
}
} }
@Suppress("MagicNumber") @Suppress("MagicNumber")

1
features/welcome/api/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,22 @@
plugins {
alias(deps.plugins.android.library)
alias(deps.plugins.kotlin.android)
id("configuration")
}
android {
namespace = "com.tangem.features.welcome.api"
}
dependencies {
/** Core */
implementation(projects.core.decompose)
implementation(projects.core.ui)
implementation(projects.common.routing)
/** Domain models */
implementation(projects.domain.wallets.models)
/** Compose */
implementation(deps.compose.runtime)
}

View file

@ -0,0 +1,14 @@
package com.tangem.features.welcome
import com.tangem.common.routing.entity.SerializableIntent
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
interface WelcomeComponent : ComposableContentComponent {
data class Params(
val intent: SerializableIntent?,
)
interface Factory : ComponentFactory<Params, WelcomeComponent>
}

1
features/welcome/impl/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,57 @@
plugins {
alias(deps.plugins.android.library)
alias(deps.plugins.kotlin.android)
alias(deps.plugins.kotlin.serialization)
alias(deps.plugins.kotlin.kapt)
alias(deps.plugins.hilt.android)
id("configuration")
}
android {
namespace = "com.tangem.features.welcome.impl"
}
dependencies {
implementation(projects.features.welcome.api)
/** Core */
implementation(projects.core.configToggles)
implementation(projects.core.decompose)
implementation(projects.core.ui)
implementation(projects.common.routing)
implementation(projects.common.ui)
/** Domain models */
implementation(projects.domain.appCurrency.models)
implementation(projects.domain.models)
implementation(projects.domain.tokens.models)
implementation(projects.domain.wallets.models)
/** Domain */
implementation(projects.domain.appCurrency)
implementation(projects.domain.wallets)
/** DI */
implementation(deps.hilt.android)
kapt(deps.hilt.kapt)
/** AndroidX */
implementation(deps.androidx.activity.compose)
/** Compose */
implementation(deps.compose.accompanist.systemUiController)
implementation(deps.compose.coil)
implementation(deps.compose.foundation)
implementation(deps.compose.material3)
implementation(deps.compose.ui)
implementation(deps.compose.ui.tooling)
/** Other */
implementation(deps.arrow.core)
implementation(deps.decompose)
implementation(deps.decompose.ext.compose)
implementation(deps.kotlin.immutable.collections)
implementation(deps.timber)
implementation(tangemDeps.card.core)
implementation(tangemDeps.blockchain)
}

View file

@ -0,0 +1,37 @@
package com.tangem.features.welcome.impl
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.features.welcome.WelcomeComponent
import com.tangem.features.welcome.impl.model.WelcomeModel
import com.tangem.features.welcome.impl.ui.Welcome
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
internal class DefaultWelcomeComponent @AssistedInject constructor(
@Assisted context: AppComponentContext,
@Assisted params: WelcomeComponent.Params,
) : WelcomeComponent, AppComponentContext by context {
private val model: WelcomeModel = getOrCreateModel(params)
@Composable
override fun Content(modifier: Modifier) {
val state by model.uiState.collectAsStateWithLifecycle()
Welcome(
modifier = modifier,
state = state,
)
}
@AssistedFactory
interface Factory : WelcomeComponent.Factory {
override fun create(context: AppComponentContext, params: WelcomeComponent.Params): DefaultWelcomeComponent
}
}

View file

@ -0,0 +1,31 @@
package com.tangem.features.welcome.impl.di
import com.tangem.core.decompose.di.ModelComponent
import com.tangem.core.decompose.model.Model
import com.tangem.features.welcome.WelcomeComponent
import com.tangem.features.welcome.impl.DefaultWelcomeComponent
import com.tangem.features.welcome.impl.model.WelcomeModel
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
@Module
@InstallIn(SingletonComponent::class)
internal interface FeatureModule {
@Binds
fun bindComponentFactory(impl: DefaultWelcomeComponent.Factory): WelcomeComponent.Factory
}
@Module
@InstallIn(ModelComponent::class)
internal interface ModelModule {
@Binds
@IntoMap
@ClassKey(WelcomeModel::class)
fun provideModel(model: WelcomeModel): Model
}

View file

@ -0,0 +1,18 @@
package com.tangem.features.welcome.impl.model
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.features.welcome.impl.ui.state.WelcomeUM
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
@ModelScoped
internal class WelcomeModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
) : Model() {
val uiState: StateFlow<WelcomeUM>
field = MutableStateFlow(WelcomeUM.Plain)
}

View file

@ -0,0 +1,89 @@
package com.tangem.features.welcome.impl.ui
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
import com.tangem.core.ui.components.inputrow.InputRowDefault
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.welcome.impl.ui.state.AddWalletBottomSheetContentUM
@Composable
fun AddWalletBottomSheet(config: TangemBottomSheetConfig) {
TangemBottomSheet<AddWalletBottomSheetContentUM>(
config = config,
titleText = TextReference.Str("Add Wallet"),
containerColor = TangemTheme.colors.background.tertiary,
content = { Content(it) },
)
}
@Composable
private fun Content(content: AddWalletBottomSheetContentUM) {
Column(
modifier = Modifier
.padding(
start = TangemTheme.dimens.spacing16,
end = TangemTheme.dimens.spacing16,
bottom = TangemTheme.dimens.spacing16,
),
) {
InputRowDefault(
text = TextReference.Str("Create New Wallet"),
modifier = Modifier
.roundedShapeItemDecoration(
currentIndex = 0,
lastIndex = 3,
addDefaultPadding = false,
)
.background(TangemTheme.colors.background.action)
.clickable { content.onOptionClick(AddWalletBottomSheetContentUM.Option.Create) },
)
InputRowDefault(
text = TextReference.Str("Add Existing Wallet"),
modifier = Modifier
.roundedShapeItemDecoration(
currentIndex = 1,
lastIndex = 2,
addDefaultPadding = false,
)
.background(TangemTheme.colors.background.action)
.clickable { content.onOptionClick(AddWalletBottomSheetContentUM.Option.Add) },
)
InputRowDefault(
text = TextReference.Str("Buy Tangem Wallet"),
modifier = Modifier
.roundedShapeItemDecoration(
currentIndex = 2,
lastIndex = 2,
addDefaultPadding = false,
)
.background(TangemTheme.colors.background.action)
.clickable { content.onOptionClick(AddWalletBottomSheetContentUM.Option.Buy) },
)
}
}
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun SecurityScoreBottomSheetPreview() {
TangemThemePreview {
AddWalletBottomSheet(
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = {},
content = AddWalletBottomSheetContentUM(),
),
)
}
}

View file

@ -0,0 +1,89 @@
package com.tangem.features.welcome.impl.ui
import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.welcome.impl.ui.state.WalletUM
import com.tangem.features.welcome.impl.ui.state.WelcomeUM
import kotlinx.collections.immutable.persistentListOf
@Composable
internal fun Welcome(state: WelcomeUM, modifier: Modifier = Modifier) {
Box(
modifier
.fillMaxSize()
.background(TangemTheme.colors.background.primary),
) {
AnimatedContent(
targetState = state,
contentKey = { it::class.java.simpleName },
) { st ->
when (st) {
is WelcomeUM.Plain -> WelcomePlain(modifier = modifier)
is WelcomeUM.SelectWallet -> WelcomeSelectWallet(
state = st,
modifier = modifier,
)
is WelcomeUM.EnterAccessCode -> WelcomeEnterAccessCode(
state = st,
modifier = modifier,
)
}
}
}
}
@Preview
@Composable
private fun Preview() {
TangemThemePreview {
val state = WelcomeUM.SelectWallet(
wallets = persistentListOf(
WalletUM(
name = TextReference.Str("Wallet 1"),
subtitle = TextReference.Str("3 cards"),
imageState = WalletUM.ImageState.Loading,
onClick = {},
),
WalletUM(
name = TextReference.Str("Wallet 1"),
subtitle = TextReference.Str("Mobile wallet"),
imageState = WalletUM.ImageState.MobileWallet,
onClick = {},
),
),
)
var currentState by remember { mutableStateOf<WelcomeUM>(WelcomeUM.EnterAccessCode()) }
Box {
Welcome(currentState)
Button(
modifier = Modifier.align(Alignment.BottomStart),
onClick = {
currentState = when (currentState) {
is WelcomeUM.Plain -> state
is WelcomeUM.SelectWallet -> WelcomeUM.EnterAccessCode(
value = "",
onValueChange = {},
)
is WelcomeUM.EnterAccessCode -> WelcomeUM.Plain
}
},
) {
Text("Switch")
}
}
}
}

View file

@ -0,0 +1,94 @@
package com.tangem.features.welcome.impl.ui
import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.SecondaryButton
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.SpacerH24
import com.tangem.core.ui.components.appbar.TopAppBarButton
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
import com.tangem.core.ui.components.fields.PinTextField
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.welcome.impl.ui.state.WelcomeUM
@Suppress("MagicNumber")
@Composable
internal fun AnimatedContentScope.WelcomeEnterAccessCode(
state: WelcomeUM.EnterAccessCode,
modifier: Modifier = Modifier,
) {
Box(
modifier = modifier
.fillMaxSize()
.statusBarsPadding(),
) {
Column {
TopAppBarButton(
modifier = Modifier
.padding(12.dp),
button = TopAppBarButtonUM.Back(onBackClicked = state.onBackClick),
tint = TangemTheme.colors.icon.primary1,
)
SpacerH(68.dp)
Text(
modifier = Modifier
.animateEnterExit(
enter = slideInVertically(
tween(delayMillis = 300),
initialOffsetY = { it + 200 },
) + fadeIn(tween(delayMillis = 300)),
exit = fadeOut(),
)
.align(Alignment.CenterHorizontally),
text = "Enter Access Code",
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
)
SpacerH24()
Box(
modifier = Modifier
.animateEnterExit(
enter = slideInVertically(
tween(delayMillis = 300),
initialOffsetY = { it + 200 },
) + fadeIn(tween(delayMillis = 300)),
exit = fadeOut(),
)
.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
PinTextField(
length = 6,
isPasswordVisual = true,
value = state.value,
onValueChange = state.onValueChange,
)
}
}
SecondaryButton(
modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth()
.padding(16.dp)
.navigationBarsPadding()
.imePadding()
.animateEnterExit(fadeIn(), fadeOut()),
text = "Log in with biometric",
onClick = state.onUnlockWithBiometricClick,
)
}
}

View file

@ -0,0 +1,29 @@
package com.tangem.features.welcome.impl.ui
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.welcome.impl.R
@Composable
internal fun WelcomePlain(modifier: Modifier = Modifier) {
Box(
modifier = modifier.fillMaxSize(),
contentAlignment = Alignment.Center,
) {
Icon(
modifier = Modifier.size(84.dp),
imageVector = ImageVector.vectorResource(R.drawable.ic_tangem_24),
tint = TangemTheme.colors.icon.primary1,
contentDescription = null,
)
}
}

View file

@ -0,0 +1,230 @@
package com.tangem.features.welcome.impl.ui
import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.unit.dp
import com.tangem.common.ui.userwallet.CardImage
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.ui.components.*
import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.welcome.impl.R
import com.tangem.features.welcome.impl.ui.state.WalletUM
import com.tangem.features.welcome.impl.ui.state.WelcomeUM
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.delay
@Suppress("MagicNumber")
@Composable
internal fun AnimatedContentScope.WelcomeSelectWallet(state: WelcomeUM.SelectWallet, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.fillMaxSize()
.statusBarsPadding(),
) {
TopBar(state)
TitleText()
SpacerH12()
var actualWallets by remember { mutableStateOf<ImmutableList<WalletUM>>(persistentListOf()) }
Box(modifier = Modifier.weight(1f)) {
LazyColumn(
modifier = Modifier
.animateEnterExit(
enter = slideInVertically(
tween(delayMillis = 300),
initialOffsetY = { it / 10 },
) + fadeIn(tween(delayMillis = 300)),
exit = fadeOut(),
)
.fillMaxHeight()
.padding(horizontal = 16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
itemsIndexed(actualWallets) { index, walletState ->
WalletItem(
state = walletState,
modifier = Modifier,
)
}
}
BottomFade(modifier = Modifier.align(Alignment.BottomCenter))
SecondaryButton(
modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth()
.padding(16.dp)
.navigationBarsPadding()
.animateEnterExit(fadeIn(), fadeOut()),
text = "Unlock all with biometric",
onClick = state.onUnlockWithBiometricClick,
)
}
LaunchedEffect(state.wallets) {
delay(200)
actualWallets = state.wallets
}
}
AddWalletBottomSheet(state.addWalletBottomSheet)
}
@Suppress("MagicNumber")
@Composable
private fun AnimatedContentScope.TopBar(state: WelcomeUM.SelectWallet, modifier: Modifier = Modifier) {
Row(
modifier = modifier
.animateEnterExit(
enter = slideInVertically(
tween(delayMillis = 100),
initialOffsetY = { it + 100 },
) + fadeIn(tween(delayMillis = 100)),
exit = fadeOut(),
)
.padding(
top = 16.dp,
bottom = 16.dp,
start = 16.dp,
)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.img_tangem_logo_90_24),
tint = TangemTheme.colors.icon.primary1,
contentDescription = null,
)
TextButton(
modifier = Modifier.clip(TangemTheme.shapes.roundedCornersLarge),
text = "Add Wallet",
colors = TangemButtonsDefaults.defaultTextButtonColors.copy(
contentColor = TangemTheme.colors.text.primary1,
),
textStyle = TangemTheme.typography.body1,
onClick = state.addWalletClick,
)
}
}
@Suppress("MagicNumber")
@Composable
private fun AnimatedContentScope.TitleText(modifier: Modifier = Modifier) {
Column(
modifier.padding(16.dp),
) {
Text(
modifier = Modifier.animateEnterExit(
enter = slideInVertically(
tween(delayMillis = 300),
initialOffsetY = { it + 200 },
) + fadeIn(tween(delayMillis = 300)),
exit = fadeOut(),
),
text = "Welcome back!",
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
)
SpacerH16()
Text(
modifier = Modifier.animateEnterExit(
enter = slideInVertically(
tween(delayMillis = 300),
initialOffsetY = { it + 200 },
) + fadeIn(tween(delayMillis = 300)),
exit = fadeOut(),
),
text = "Select a wallet to log in",
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.secondary,
)
}
}
@Suppress("MagicNumber")
@Composable
private fun WalletItem(state: WalletUM, modifier: Modifier = Modifier) {
Row(
modifier = modifier
.fillMaxWidth()
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(TangemTheme.colors.background.secondary, TangemTheme.shapes.roundedCornersXMedium)
.clickable(onClick = state.onClick)
.padding(12.dp),
verticalAlignment = Alignment.CenterVertically,
) {
WalletImage(state.imageState)
SpacerW12()
Column(Modifier.weight(1f)) {
Text(
text = state.name.resolveReference(),
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
)
Text(
text = state.subtitle.resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
)
}
}
}
@Composable
private fun WalletImage(state: WalletUM.ImageState, modifier: Modifier = Modifier) {
when (state) {
WalletUM.ImageState.MobileWallet -> {
Box(
modifier = modifier
.size(36.dp)
.background(TangemTheme.colors.icon.accent.copy(alpha = 0.1f), CircleShape),
contentAlignment = Alignment.Center,
) {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_wallet_filled_24),
tint = TangemTheme.colors.icon.accent,
contentDescription = null,
)
}
}
else -> {
CardImage(
imageState = when (state) {
is WalletUM.ImageState.Image -> UserWalletItemUM.ImageState.Image(state.artwork)
WalletUM.ImageState.Loading -> UserWalletItemUM.ImageState.Loading
else -> error("")
},
modifier = modifier,
)
}
}
}

View file

@ -0,0 +1,14 @@
package com.tangem.features.welcome.impl.ui.state
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
internal data class AddWalletBottomSheetContentUM(
val onOptionClick: (Option) -> Unit = {},
) : TangemBottomSheetConfigContent {
enum class Option {
Create,
Add,
Buy,
}
}

View file

@ -0,0 +1,22 @@
package com.tangem.features.welcome.impl.ui.state
import com.tangem.core.ui.components.artwork.ArtworkUM
import com.tangem.core.ui.extensions.TextReference
import javax.annotation.concurrent.Immutable
internal data class WalletUM(
val name: TextReference,
val subtitle: TextReference,
val imageState: ImageState,
val onClick: () -> Unit,
) {
@Immutable
sealed class ImageState {
data object MobileWallet : ImageState()
data object Loading : ImageState()
data class Image(
val artwork: ArtworkUM,
) : ImageState()
}
}

View file

@ -0,0 +1,27 @@
package com.tangem.features.welcome.impl.ui.state
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@Immutable
internal sealed class WelcomeUM {
data object Plain : WelcomeUM()
data class SelectWallet(
val wallets: ImmutableList<WalletUM> = persistentListOf(),
val showUnlockWithBiometricButton: Boolean = false,
val addWalletBottomSheet: TangemBottomSheetConfig = TangemBottomSheetConfig.Empty,
val onUnlockWithBiometricClick: () -> Unit = {},
val addWalletClick: () -> Unit = {},
) : WelcomeUM()
data class EnterAccessCode(
val value: String = "",
val onUnlockWithBiometricClick: () -> Unit = {},
val onValueChange: (String) -> Unit = {},
val onBackClick: () -> Unit = {},
) : WelcomeUM()
}

View file

@ -254,6 +254,9 @@ include(":features:kyc:impl")
include(":features:create-wallet-selection:api") include(":features:create-wallet-selection:api")
include(":features:create-wallet-selection:impl") include(":features:create-wallet-selection:impl")
include(":features:welcome:api")
include(":features:welcome:impl")
// endregion Feature modules // endregion Feature modules
// region Domain modules // region Domain modules
@ -341,6 +344,6 @@ include(":data:onramp")
include(":data:quotes") include(":data:quotes")
include(":data:notifications") include(":data:notifications")
include(":data:blockaid") include(":data:blockaid")
// endregion Data modules
include(":data:swap") include(":data:swap")
include(":data:express") include(":data:express")
// endregion Data modules