Updated on 2026-08-14

This commit is contained in:
Tangem 2025-06-24 15:44:11 +05:00
parent 281b5ec956
commit 37efabe359
11 changed files with 454 additions and 0 deletions

View file

@ -0,0 +1 @@
build/

View file

@ -0,0 +1,70 @@
plugins {
alias(deps.plugins.android.library)
alias(deps.plugins.kotlin.android)
alias(deps.plugins.kotlin.kapt)
alias(deps.plugins.kotlin.serialization)
alias(deps.plugins.hilt.android)
id("configuration")
}
android {
namespace = "com.tangem.features.createwalletselection.impl"
}
dependencies {
/** Api */
implementation(projects.features.createWalletSelection.api)
/** Hot Wallet Feature */
implementation(projects.features.hotWallet.api)
/** Core modules */
implementation(projects.core.configToggles)
implementation(projects.core.analytics)
implementation(projects.core.analytics.models)
implementation(projects.core.utils)
implementation(projects.core.ui)
implementation(projects.core.res)
implementation(projects.core.decompose)
implementation(projects.core.navigation)
implementation(projects.core.datasource)
/** Common */
implementation(projects.common.ui)
implementation(projects.common.routing)
/** Tangem libraries */
implementation(projects.libs.tangemSdkApi)
implementation(tangemDeps.card.core)
implementation(tangemDeps.card.android) {
exclude(module = "joda-time")
}
/** AndroidX libraries */
implementation(deps.androidx.core.ktx)
implementation(deps.lifecycle.runtime.ktx)
/** Compose libraries */
implementation(deps.compose.material)
implementation(deps.compose.material3)
implementation(deps.compose.animation)
implementation(deps.compose.foundation)
implementation(deps.compose.ui)
implementation(deps.compose.ui.tooling)
implementation(deps.compose.coil)
implementation(deps.lottie.compose)
implementation(deps.decompose.ext.compose)
implementation(deps.androidx.activity.compose)
implementation(deps.androidx.datastore)
/** Other libraries */
implementation(deps.arrow.core)
implementation(deps.kotlin.immutable.collections)
implementation(deps.kotlin.serialization)
implementation(deps.timber)
implementation(deps.firebase.crashlytics)
/** DI */
implementation(deps.hilt.android)
kapt(deps.hilt.kapt)
}

View file

@ -0,0 +1,240 @@
package com.tangem.features.createwalletselection
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.buttons.common.TangemButton
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
import com.tangem.core.ui.components.buttons.common.TangemButtonSize
import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.createwalletselection.entity.CreateWalletSelectionUM
import com.tangem.features.createwalletselection.impl.R
@Suppress("LongMethod")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
internal fun CreateWalletSelectionContent(state: CreateWalletSelectionUM, modifier: Modifier = Modifier) {
Column(
modifier = Modifier
.background(TangemTheme.colors.background.primary)
.fillMaxSize()
.systemBarsPadding(),
) {
TopAppBar(
modifier = modifier
.statusBarsPadding(),
colors = TopAppBarDefaults.topAppBarColors(
containerColor = TangemTheme.colors.background.primary,
),
navigationIcon = {
IconButton(onClick = state.onBackClick) {
Icon(
painter = painterResource(R.drawable.ic_back_24),
tint = TangemTheme.colors.icon.primary1,
contentDescription = null,
)
}
},
title = { },
actions = {
Text(
modifier = Modifier
.padding(16.dp),
// TODO update and extract this string
text = "What to choose?",
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.primary1,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
},
)
Column(
modifier = Modifier
.weight(1f)
.padding(
start = 16.dp,
top = 24.dp,
end = 16.dp,
),
) {
Text(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
// TODO update and extract this string
text = "Choose how you want to create your wallet",
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
)
WalletBlock(
modifier = Modifier
.padding(top = 24.dp),
// TODO update and extract this string
title = "Mobile Wallet",
// TODO update and extract this string
description = "A secure wallet is created on your phone in seconds.",
badge = {
Box(
modifier = Modifier
.background(
color = TangemTheme.colors.field.focused,
shape = TangemTheme.shapes.roundedCorners8,
)
.padding(horizontal = 8.dp, vertical = 4.dp),
) {
Text(
// TODO update and extract this string
text = "Free",
style = TangemTheme.typography.caption1,
color = TangemTheme.colors.text.secondary,
)
}
},
onClick = state.onMobileWalletClick,
)
WalletBlock(
// TODO update and extract this string
title = "Hardware Wallet",
// TODO update and extract this string
description = "Buy Tangem Wallet — physical cards that securely store your crypto offline.",
badge = {
Box(
modifier = Modifier
.background(
color = TangemTheme.colors.text.accent.copy(alpha = 0.1f),
shape = TangemTheme.shapes.roundedCorners8,
)
.padding(horizontal = 8.dp, vertical = 4.dp),
) {
Text(
// TODO update and extract this string
text = "From $54.90",
style = TangemTheme.typography.caption1,
color = TangemTheme.colors.text.accent,
)
}
},
onClick = state.onHardwareWalletClick,
)
}
AlreadyHaveTangemWalletBlock(
onScanClick = state.onScanClick,
isScanInProgress = state.isScanInProgress,
)
}
}
@Composable
private fun WalletBlock(
modifier: Modifier = Modifier,
title: String,
description: String,
badge: @Composable () -> Unit,
onClick: () -> Unit,
) {
Column(
modifier = modifier
.fillMaxWidth()
.padding(top = 8.dp)
.background(
color = TangemTheme.colors.background.secondary,
shape = TangemTheme.shapes.roundedCornersXMedium,
)
.padding(16.dp)
.clickable(onClick = onClick),
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
modifier = Modifier
.padding(end = 8.dp),
text = title,
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
)
badge()
}
Text(
modifier = Modifier
.padding(top = 4.dp),
text = description,
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.tertiary,
)
}
}
@Composable
private fun AlreadyHaveTangemWalletBlock(
onScanClick: () -> Unit,
isScanInProgress: Boolean,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier
.fillMaxWidth()
.padding(16.dp)
.background(
color = TangemTheme.colors.field.primary,
shape = TangemTheme.shapes.roundedCornersXMedium,
)
.padding(
horizontal = 20.dp,
vertical = 16.dp,
),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
modifier = Modifier
.weight(1f)
.padding(end = 16.dp),
// TODO update and extract this string
text = "Do you already have Tangem Wallet?",
style = TangemTheme.typography.button,
color = TangemTheme.colors.text.primary1,
)
TangemButton(
modifier = Modifier
.wrapContentWidth(),
// TODO update and extract this string
text = "Scan Now",
onClick = onScanClick,
icon = TangemButtonIconPosition.End(iconResId = R.drawable.ic_tangem_24),
size = TangemButtonSize.RoundedAction,
showProgress = isScanInProgress,
colors = TangemButtonsDefaults.secondaryButtonColors,
textStyle = TangemTheme.typography.subtitle1,
enabled = true,
animateContentChange = true,
)
}
}
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun PreviewCreateWalletContent() {
TangemThemePreview {
CreateWalletSelectionContent(
state = CreateWalletSelectionUM(
onBackClick = {},
onMobileWalletClick = {},
onHardwareWalletClick = {},
onScanClick = {},
),
)
}
}

View file

@ -0,0 +1,30 @@
package com.tangem.features.createwalletselection
import com.tangem.core.decompose.model.Model
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object CreateWalletSelectionFeatureModule
@Module
@InstallIn(SingletonComponent::class)
internal interface CreateWalletSelectionFeatureModuleBinds {
@Binds
@Singleton
fun bindCreateWalletSelectionComponentFactory(
impl: DefaultCreateWalletSelectionComponent.Factory,
): CreateWalletSelectionComponent.Factory
@Binds
@IntoMap
@ClassKey(CreateWalletSelectionModel::class)
fun bindCreateWalletSelectionModel(model: CreateWalletSelectionModel): Model
}

View file

@ -0,0 +1,37 @@
package com.tangem.features.createwalletselection
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.features.createwalletselection.entity.CreateWalletSelectionUM
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
@ModelScoped
internal class CreateWalletSelectionModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
) : Model() {
internal val uiState: StateFlow<CreateWalletSelectionUM>
field = MutableStateFlow(
CreateWalletSelectionUM(
onBackClick = { /* [REDACTED_TODO_COMMENT] */ },
onMobileWalletClick = ::onMobileWalletClick,
onHardwareWalletClick = ::onHardwareWalletClick,
onScanClick = ::onScanClick,
),
)
private fun onMobileWalletClick() {
// TODO navigate to mobile wallet creation
}
private fun onHardwareWalletClick() {
// TODO open card order web page
}
private fun onScanClick() {
// TODO open card scanning
}
}

View file

@ -0,0 +1,33 @@
package com.tangem.features.createwalletselection
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 dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
internal class DefaultCreateWalletSelectionComponent @AssistedInject constructor(
@Assisted private val context: AppComponentContext,
@Assisted private val params: Unit,
) : CreateWalletSelectionComponent, AppComponentContext by context {
private val model: CreateWalletSelectionModel = getOrCreateModel(params)
@Composable
override fun Content(modifier: Modifier) {
val state by model.uiState.collectAsStateWithLifecycle()
CreateWalletSelectionContent(
state = state,
modifier = modifier,
)
}
@AssistedFactory
interface Factory : CreateWalletSelectionComponent.Factory {
override fun create(context: AppComponentContext, params: Unit): DefaultCreateWalletSelectionComponent
}
}

View file

@ -0,0 +1,9 @@
package com.tangem.features.createwalletselection.entity
internal data class CreateWalletSelectionUM(
val isScanInProgress: Boolean = false,
val onBackClick: () -> Unit,
val onMobileWalletClick: () -> Unit,
val onHardwareWalletClick: () -> Unit,
val onScanClick: () -> Unit,
)