Updated on 2026-08-14
This commit is contained in:
parent
0ff4768da9
commit
b9bb9e1086
21 changed files with 789 additions and 3 deletions
|
|
@ -224,6 +224,8 @@ dependencies {
|
|||
implementation(projects.features.hotWallet.impl)
|
||||
implementation(projects.features.kyc.api)
|
||||
implementation(projects.features.kyc.impl)
|
||||
implementation(projects.features.welcome.api)
|
||||
implementation(projects.features.welcome.impl)
|
||||
|
||||
/** AndroidX libraries */
|
||||
implementation(deps.androidx.core.ktx)
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ private fun NameAndInfo(
|
|||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
private fun CardImage(imageState: UserWalletItemUM.ImageState, modifier: Modifier = Modifier) {
|
||||
fun CardImage(imageState: UserWalletItemUM.ImageState, modifier: Modifier = Modifier) {
|
||||
val imageModifier = modifier
|
||||
.width(TangemTheme.dimens.size36)
|
||||
.height(TangemTheme.dimens.size24)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import androidx.compose.ui.unit.sp
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.utils.StringsSigns.PASSWORD_VISUAL_CHAR
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlin.random.Random
|
||||
|
||||
@Composable
|
||||
|
|
@ -74,6 +75,11 @@ fun PinTextField(
|
|||
)
|
||||
},
|
||||
)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
delay(timeMillis = 200)
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
|
|
|
|||
1
features/welcome/api/.gitignore
vendored
Normal file
1
features/welcome/api/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
22
features/welcome/api/build.gradle.kts
Normal file
22
features/welcome/api/build.gradle.kts
Normal 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)
|
||||
}
|
||||
|
|
@ -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
1
features/welcome/impl/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
57
features/welcome/impl/build.gradle.kts
Normal file
57
features/welcome/impl/build.gradle.kts
Normal 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)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -254,6 +254,9 @@ include(":features:kyc:impl")
|
|||
|
||||
include(":features:create-wallet-selection:api")
|
||||
include(":features:create-wallet-selection:impl")
|
||||
|
||||
include(":features:welcome:api")
|
||||
include(":features:welcome:impl")
|
||||
// endregion Feature modules
|
||||
|
||||
// region Domain modules
|
||||
|
|
@ -341,6 +344,6 @@ include(":data:onramp")
|
|||
include(":data:quotes")
|
||||
include(":data:notifications")
|
||||
include(":data:blockaid")
|
||||
// endregion Data modules
|
||||
include(":data:swap")
|
||||
include(":data:express")
|
||||
// endregion Data modules
|
||||
Loading…
Add table
Add a link
Reference in a new issue