Updated on 2026-08-14
This commit is contained in:
parent
0ff4768da9
commit
b9bb9e1086
21 changed files with 789 additions and 3 deletions
|
|
@ -1,28 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="90dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="90"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M20,7.5H4V5.5C4,3.843 5.343,2.5 7,2.5H17C18.657,2.5 20,3.843 20,5.5V7.5ZM9,21.5H7C5.343,21.5 4,20.157 4,18.5V11.5H9V21.5ZM15,21.5H17C18.657,21.5 20,20.157 20,18.5V11.5H15V21.5Z" />
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M32.063,18.693C31.148,18.693 30.447,18.452 29.96,17.97C29.485,17.488 29.247,16.795 29.247,15.892V10.723H28V9.078H29.247V7H31.404V9.078H33.453V10.723H31.404V15.693C31.404,16.5 31.76,16.904 32.473,16.904C32.889,16.904 33.239,16.843 33.525,16.723V18.458C33.109,18.615 32.622,18.693 32.063,18.693Z" />
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M37.642,18.711C36.727,18.711 35.978,18.482 35.396,18.024C34.814,17.554 34.523,16.849 34.523,15.91C34.523,14.873 34.957,14.115 35.824,13.632C36.691,13.151 37.85,12.91 39.299,12.91H40.457V12.422C40.457,11.771 40.327,11.295 40.065,10.994C39.816,10.681 39.382,10.524 38.764,10.524C37.683,10.524 37.089,11.018 36.982,12.006H34.897C34.98,10.958 35.384,10.175 36.109,9.657C36.846,9.139 37.772,8.88 38.889,8.88C41.372,8.88 42.614,10.03 42.614,12.331V18.53H40.493V17.374C39.828,18.265 38.877,18.711 37.642,18.711ZM38.159,17.139C38.836,17.139 39.388,16.97 39.816,16.632C40.244,16.283 40.457,15.813 40.457,15.223V14.283H39.353C37.558,14.283 36.661,14.795 36.661,15.819C36.661,16.699 37.16,17.139 38.159,17.139Z" />
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M44.729,18.53V9.078H46.885V10.578C47.123,10.084 47.503,9.681 48.026,9.367C48.549,9.042 49.172,8.88 49.897,8.88C50.883,8.88 51.661,9.187 52.232,9.801C52.802,10.404 53.087,11.331 53.087,12.584V18.53H50.931V12.783C50.931,12.072 50.782,11.548 50.485,11.211C50.188,10.873 49.719,10.705 49.077,10.705C48.436,10.705 47.907,10.904 47.491,11.301C47.087,11.687 46.885,12.241 46.885,12.964V18.53H44.729Z" />
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M59.189,22C57.799,22 56.736,21.717 55.999,21.151C55.263,20.584 54.829,19.819 54.698,18.855H56.872C57.062,19.843 57.829,20.337 59.171,20.337C60.906,20.337 61.773,19.488 61.773,17.789V16.398C61.5,16.879 61.084,17.295 60.526,17.645C59.979,17.982 59.379,18.151 58.726,18.151C57.538,18.151 56.551,17.735 55.767,16.904C54.983,16.072 54.591,14.988 54.591,13.651V13.524C54.591,12.199 54.989,11.096 55.785,10.217C56.581,9.325 57.579,8.88 58.779,8.88C60.157,8.88 61.155,9.428 61.773,10.524V9.078H63.93V17.825C63.918,19.187 63.49,20.223 62.646,20.934C61.815,21.645 60.662,22 59.189,22ZM59.26,16.47C60.021,16.47 60.639,16.211 61.114,15.693C61.601,15.175 61.844,14.47 61.844,13.578V13.452C61.844,12.56 61.619,11.861 61.167,11.355C60.716,10.849 60.104,10.596 59.332,10.596C58.548,10.596 57.93,10.861 57.478,11.392C57.027,11.922 56.801,12.62 56.801,13.488V13.632C56.801,14.488 57.033,15.175 57.496,15.693C57.959,16.211 58.548,16.47 59.26,16.47Z" />
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M70.235,18.711C68.845,18.711 67.71,18.277 66.831,17.41C65.952,16.542 65.512,15.373 65.512,13.904V13.759C65.512,12.313 65.946,11.139 66.813,10.235C67.692,9.331 68.803,8.88 70.146,8.88C71.405,8.88 72.451,9.277 73.282,10.072C74.126,10.868 74.548,12.06 74.548,13.651V14.283H67.722C67.758,15.187 67.995,15.88 68.435,16.361C68.874,16.843 69.492,17.084 70.288,17.084C71.548,17.084 72.261,16.602 72.427,15.639H74.512C74.357,16.626 73.906,17.385 73.158,17.916C72.409,18.446 71.435,18.711 70.235,18.711ZM67.758,12.819H72.409C72.302,11.253 71.548,10.47 70.146,10.47C69.504,10.47 68.97,10.681 68.542,11.102C68.126,11.512 67.865,12.084 67.758,12.819Z" />
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M76.117,18.53V9.078H78.274V10.524C78.511,10.054 78.88,9.663 79.379,9.349C79.878,9.036 80.454,8.88 81.107,8.88C82.414,8.88 83.293,9.446 83.745,10.578C84.089,10.024 84.547,9.602 85.117,9.313C85.687,9.024 86.281,8.88 86.899,8.88C87.814,8.88 88.557,9.187 89.127,9.801C89.709,10.404 90,11.319 90,12.548V18.53H87.844V12.693C87.844,11.368 87.291,10.705 86.186,10.705C85.616,10.705 85.129,10.898 84.725,11.283C84.333,11.657 84.137,12.187 84.137,12.873V18.53H81.98V12.693C81.98,11.368 81.428,10.705 80.323,10.705C79.753,10.705 79.266,10.898 78.862,11.283C78.47,11.657 78.274,12.187 78.274,12.873V18.53H76.117Z" />
|
||||
</vector>
|
||||
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()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue