Updated on 2026-08-14

This commit is contained in:
Tangem 2025-07-01 14:35:52 +05:00
parent 45cf551d3f
commit 9ebfdb5b31
23 changed files with 379 additions and 89 deletions

View file

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

View file

@ -11,6 +11,7 @@ import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.card.repository.CardSdkConfigRepository
@ -54,6 +55,7 @@ internal class HomeModel @Inject constructor(
private val urlOpener: UrlOpener,
private val analyticsEventHandler: AnalyticsEventHandler,
private val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory,
private val router: Router,
getUserCountryUseCase: GetUserCountryUseCase,
) : Model() {
@ -72,11 +74,11 @@ internal class HomeModel @Inject constructor(
}
fun onCreateNewWalletScreen() {
// TODO implement navigation to create new wallet
router.push(AppRoute.CreateWalletSelection)
}
fun onAddExistingWalletScreen() {
// TODO implement navigation to add existing wallet
router.push(AppRoute.AddExistingWallet)
}
fun onScanClick() {

View file

@ -8,8 +8,11 @@ import com.tangem.feature.referral.api.ReferralComponent
import com.tangem.feature.stories.api.StoriesComponent
import com.tangem.feature.usedesk.api.UsedeskComponent
import com.tangem.feature.walletsettings.component.WalletSettingsComponent
import com.tangem.features.createwalletselection.CreateWalletSelectionComponent
import com.tangem.features.details.component.DetailsComponent
import com.tangem.features.disclaimer.api.components.DisclaimerComponent
import com.tangem.features.hotwallet.AddExistingWalletComponent
import com.tangem.features.hotwallet.CreateMobileWalletComponent
import com.tangem.features.managetokens.component.ChooseManagedTokensComponent
import com.tangem.features.managetokens.component.ManageTokensComponent
import com.tangem.features.managetokens.component.ManageTokensSource
@ -80,6 +83,9 @@ internal class ChildFactory @Inject constructor(
private val nftSendComponentFactory: NFTSendComponent.Factory,
private val usedeskComponentFactory: UsedeskComponent.Factory,
private val chooseManagedTokensComponentFactory: ChooseManagedTokensComponent.Factory,
private val createWalletSelectionComponentFactory: CreateWalletSelectionComponent.Factory,
private val createMobileWalletComponentFactory: CreateMobileWalletComponent.Factory,
private val addExistingWalletComponentFactory: AddExistingWalletComponent.Factory,
private val testerRouter: TesterRouter,
private val walletConnectFeatureToggles: WalletConnectFeatureToggles,
) {
@ -420,6 +426,27 @@ internal class ChildFactory @Inject constructor(
componentFactory = chooseManagedTokensComponentFactory,
)
}
is AppRoute.CreateWalletSelection -> {
createComponentChild(
context = context,
params = Unit,
componentFactory = createWalletSelectionComponentFactory,
)
}
is AppRoute.CreateMobileWallet -> {
createComponentChild(
context = context,
params = Unit,
componentFactory = createMobileWalletComponentFactory,
)
}
is AppRoute.AddExistingWallet -> {
createComponentChild(
context = context,
params = Unit,
componentFactory = addExistingWalletComponentFactory,
)
}
}
}
}

View file

@ -286,4 +286,13 @@ sealed class AppRoute(val path: String) : Route {
val nftAsset: NFTAsset,
val nftCollectionName: String,
) : AppRoute(path = "/send/nft/${userWalletId.stringValue}/$nftCollectionName/${nftAsset.id}")
@Serializable
object CreateWalletSelection : AppRoute(path = "/create_wallet_selection")
@Serializable
object CreateMobileWallet : AppRoute(path = "/create_mobile_wallet")
@Serializable
object AddExistingWallet : AppRoute(path = "/add_existing_wallet")
}

View file

@ -119,6 +119,7 @@
<string name="common_claim">Claim</string>
<string name="common_claim_rewards">Claim rewards</string>
<string name="common_close">Close</string>
<string name="common_coming_soon">Coming Soon</string>
<string name="common_confirm">Confirm</string>
<string name="common_contact_tangem_support">Contact Tangem Support</string>
<string name="common_contact_visa_support">Contact Visa Support</string>
@ -154,6 +155,7 @@
<string name="common_fee_selector_option_slow">Slow</string>
<string name="common_fee_selector_title">Speed and fee</string>
<string name="common_finish">Finish</string>
<string name="common_free">Free</string>
<string name="common_generate_addresses">Synchronize addresses</string>
<string name="common_go_to_provider">Go to provider</string>
<string name="common_go_to_token">Go to token</string>
@ -387,6 +389,19 @@
<string name="home_button_scan">Scan Tangem</string>
<string name="hot_crypto_add_token_subtitle">to %s</string>
<string name="hot_crypto_token_network">On %s network</string>
<string name="hw_backup_icloud_description">Recover an existing wallet stored in your iCloud backup</string>
<string name="hw_backup_icloud_title">iCloud backup</string>
<string name="hw_backup_need_action">Go to backup</string>
<string name="hw_backup_need_description">To secure your wallet with a Access Code, complete the backup first.</string>
<string name="hw_backup_need_title">Finish Backup First</string>
<string name="hw_backup_no_backup">No backup</string>
<string name="hw_backup_seed_description">Physical cards that securely store your crypto offline.</string>
<string name="hw_backup_seed_title">Recovery phrase</string>
<string name="hw_create_keys_description">Get notified of incoming transactions</string>
<string name="hw_create_keys_title">Keys are stored in the app</string>
<string name="hw_create_seed_description">Stay up to date with the latest features and news</string>
<string name="hw_create_seed_title">Seed phrase backup</string>
<string name="hw_create_title">Create Mobile Wallet</string>
<string name="information_generated_with_ai">This information was generated with AI.\nTap here, if you find any errors.</string>
<string name="initial_message_change_access_code_body">To change the access code tap the card or ring as shown above and do not remove until the end of the operation</string>
<string name="initial_message_change_passcode_body">To change the passcode tap the card as shown above and do not remove until the end of the operation</string>
@ -1246,6 +1261,29 @@
<string name="wallet_connect_subtitle">Connect to dApps</string>
<string name="wallet_connect_title">WalletConnect</string>
<string name="wallet_connect_toast_awaiting_session_proposal">Connecting may take a few seconds</string>
<string name="wallet_create_hardware_badge">From %s</string>
<string name="wallet_create_hardware_description">Buy Tangem Wallet — physical cards that securely store your crypto offline.</string>
<string name="wallet_create_hardware_title">Hardware Wallet</string>
<string name="wallet_create_mobile_description">A secure wallet is created on your phone in seconds.</string>
<string name="wallet_create_mobile_title">Mobile Wallet</string>
<string name="wallet_create_nav_info_title">What to choose?</string>
<string name="wallet_create_scan_question">Do you already have Tangem Wallet?</string>
<string name="wallet_create_scan_title">Scan Now</string>
<string name="wallet_create_title">Choose how you want to create your wallet</string>
<string name="wallet_import_buy_question">Want to purchase a Tangem Wallet?</string>
<string name="wallet_import_buy_title">Buy Now</string>
<string name="wallet_import_google_drive_description">Recover an existing wallet stored in your Google Drive backup</string>
<string name="wallet_import_google_drive_title">Import from Google Drive</string>
<string name="wallet_import_navtitle">Add existing wallet</string>
<string name="wallet_import_scan_description">Physical cards that securely store your crypto offline.</string>
<string name="wallet_import_scan_title">Scan a Tangem Wallet</string>
<string name="wallet_import_seed_description">Your seed phrase to recover your wallet</string>
<string name="wallet_import_seed_navtitle">Import wallet</string>
<string name="wallet_import_seed_title">Import a recovery phrase</string>
<string name="wallet_import_success_description">You successfully backed up your wallet.</string>
<string name="wallet_import_success_navtitle">Import wallet</string>
<string name="wallet_import_success_title">Import Completed</string>
<string name="wallet_import_title">Import wallet</string>
<string name="wallet_marketplace_block_title">%s Market Price</string>
<string name="wallet_marketprice_block_update_time">last 24h</string>
<string name="wallet_network_group_title">%s network</string>

View file

@ -1,7 +1,9 @@
package com.tangem.features.createwalletselection
import com.tangem.common.routing.AppRoute
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.navigation.Router
import com.tangem.features.createwalletselection.entity.CreateWalletSelectionUM
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.MutableStateFlow
@ -11,12 +13,13 @@ import javax.inject.Inject
@ModelScoped
internal class CreateWalletSelectionModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
) : Model() {
internal val uiState: StateFlow<CreateWalletSelectionUM>
field = MutableStateFlow(
CreateWalletSelectionUM(
onBackClick = { /* [REDACTED_TODO_COMMENT] */ },
onBackClick = { router.pop() },
onMobileWalletClick = ::onMobileWalletClick,
onHardwareWalletClick = ::onHardwareWalletClick,
onScanClick = ::onScanClick,
@ -24,7 +27,7 @@ internal class CreateWalletSelectionModel @Inject constructor(
)
private fun onMobileWalletClick() {
// TODO navigate to mobile wallet creation
router.push(AppRoute.CreateMobileWallet)
}
private fun onHardwareWalletClick() {

View file

@ -1,6 +1,9 @@
package com.tangem.features.createwalletselection
package com.tangem.features.createwalletselection.di
import com.tangem.core.decompose.model.Model
import com.tangem.features.createwalletselection.CreateWalletSelectionComponent
import com.tangem.features.createwalletselection.CreateWalletSelectionModel
import com.tangem.features.createwalletselection.DefaultCreateWalletSelectionComponent
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
@ -11,11 +14,11 @@ import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal object CreateWalletSelectionFeatureModule
internal object CreateWalletSelectionModule
@Module
@InstallIn(SingletonComponent::class)
internal interface CreateWalletSelectionFeatureModuleBinds {
internal interface CreateWalletSelectionModuleBinds {
@Binds
@Singleton

View file

@ -2,6 +2,7 @@ package com.tangem.features.createwalletselection.entity
internal data class CreateWalletSelectionUM(
val isScanInProgress: Boolean = false,
val hardwareWalletPrice: String = "$54.90",
val onBackClick: () -> Unit,
val onMobileWalletClick: () -> Unit,
val onHardwareWalletClick: () -> Unit,

View file

@ -8,6 +8,7 @@ import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
@ -17,6 +18,7 @@ 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.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.createwalletselection.entity.CreateWalletSelectionUM
@ -27,13 +29,13 @@ import com.tangem.features.createwalletselection.impl.R
@Composable
internal fun CreateWalletSelectionContent(state: CreateWalletSelectionUM, modifier: Modifier = Modifier) {
Column(
modifier = Modifier
modifier = modifier
.background(TangemTheme.colors.background.primary)
.fillMaxSize()
.systemBarsPadding(),
) {
TopAppBar(
modifier = modifier
modifier = Modifier
.statusBarsPadding(),
colors = TopAppBarDefaults.topAppBarColors(
containerColor = TangemTheme.colors.background.primary,
@ -52,8 +54,7 @@ internal fun CreateWalletSelectionContent(state: CreateWalletSelectionUM, modifi
Text(
modifier = Modifier
.padding(16.dp),
// TODO [REDACTED_TASK_KEY] update and extract this string
text = "What to choose?",
text = stringResourceSafe(R.string.wallet_create_nav_info_title),
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.primary1,
maxLines = 1,
@ -74,8 +75,7 @@ internal fun CreateWalletSelectionContent(state: CreateWalletSelectionUM, modifi
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
// TODO [REDACTED_TASK_KEY] update and extract this string
text = "Choose how you want to create your wallet",
text = stringResourceSafe(R.string.wallet_create_title),
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
@ -83,10 +83,8 @@ internal fun CreateWalletSelectionContent(state: CreateWalletSelectionUM, modifi
WalletBlock(
modifier = Modifier
.padding(top = 24.dp),
// TODO [REDACTED_TASK_KEY] update and extract this string
title = "Mobile Wallet",
// TODO [REDACTED_TASK_KEY] update and extract this string
description = "A secure wallet is created on your phone in seconds.",
title = stringResourceSafe(R.string.wallet_create_mobile_title),
description = stringResourceSafe(R.string.wallet_create_mobile_description),
badge = {
Box(
modifier = Modifier
@ -97,8 +95,7 @@ internal fun CreateWalletSelectionContent(state: CreateWalletSelectionUM, modifi
.padding(horizontal = 8.dp, vertical = 4.dp),
) {
Text(
// TODO [REDACTED_TASK_KEY] update and extract this string
text = "Free",
text = stringResourceSafe(R.string.common_free),
style = TangemTheme.typography.caption1,
color = TangemTheme.colors.text.secondary,
)
@ -107,10 +104,8 @@ internal fun CreateWalletSelectionContent(state: CreateWalletSelectionUM, modifi
onClick = state.onMobileWalletClick,
)
WalletBlock(
// TODO [REDACTED_TASK_KEY] update and extract this string
title = "Hardware Wallet",
// TODO [REDACTED_TASK_KEY] update and extract this string
description = "Buy Tangem Wallet — physical cards that securely store your crypto offline.",
title = stringResourceSafe(R.string.wallet_create_hardware_title),
description = stringResourceSafe(R.string.wallet_create_hardware_description),
badge = {
Box(
modifier = Modifier
@ -121,8 +116,7 @@ internal fun CreateWalletSelectionContent(state: CreateWalletSelectionUM, modifi
.padding(horizontal = 8.dp, vertical = 4.dp),
) {
Text(
// TODO [REDACTED_TASK_KEY] update and extract this string
text = "From $54.90",
text = stringResourceSafe(R.string.wallet_create_hardware_badge, state.hardwareWalletPrice),
style = TangemTheme.typography.caption1,
color = TangemTheme.colors.text.accent,
)
@ -150,12 +144,13 @@ private fun WalletBlock(
modifier = modifier
.fillMaxWidth()
.padding(top = 8.dp)
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(
color = TangemTheme.colors.background.secondary,
shape = TangemTheme.shapes.roundedCornersXMedium,
)
.padding(16.dp)
.clickable(onClick = onClick),
.clickable(onClick = onClick)
.padding(16.dp),
) {
Row {
Text(
@ -202,16 +197,14 @@ private fun AlreadyHaveTangemWalletBlock(
modifier = Modifier
.weight(1f)
.padding(end = 16.dp),
// TODO [REDACTED_TASK_KEY] update and extract this string
text = "Do you already have Tangem Wallet?",
text = stringResourceSafe(R.string.wallet_create_scan_question),
style = TangemTheme.typography.button,
color = TangemTheme.colors.text.primary1,
)
TangemButton(
modifier = Modifier
.wrapContentWidth(),
// TODO [REDACTED_TASK_KEY] update and extract this string
text = "Scan Now",
text = stringResourceSafe(R.string.wallet_create_scan_title),
onClick = onScanClick,
icon = TangemButtonIconPosition.End(iconResId = R.drawable.ic_tangem_24),
size = TangemButtonSize.RoundedAction,

View file

@ -0,0 +1,36 @@
package com.tangem.features.hotwallet.addexistingwallet.im.port
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.core.ui.decompose.ComposableContentComponent
import com.tangem.features.hotwallet.addexistingwallet.im.port.ui.AddExistingWalletImportContent
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
internal class AddExistingWalletImportComponent @AssistedInject constructor(
@Assisted private val context: AppComponentContext,
@Assisted private val params: Params,
) : ComposableContentComponent, AppComponentContext by context {
private val model: AddExistingWalletImportModel = getOrCreateModel(params)
@Composable
override fun Content(modifier: Modifier) {
val state by model.uiState.collectAsStateWithLifecycle()
AddExistingWalletImportContent(
state = state,
modifier = modifier,
)
}
interface ModelCallbacks {
fun onBackClick()
}
data class Params(
val callbacks: ModelCallbacks,
)
}

View file

@ -0,0 +1,26 @@
package com.tangem.features.hotwallet.addexistingwallet.im.port
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.features.hotwallet.addexistingwallet.im.port.entity.AddExistingWalletImportUM
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
@ModelScoped
internal class AddExistingWalletImportModel @Inject constructor(
paramsContainer: ParamsContainer,
override val dispatchers: CoroutineDispatcherProvider,
) : Model() {
private val params: AddExistingWalletImportComponent.Params = paramsContainer.require()
internal val uiState: StateFlow<AddExistingWalletImportUM>
field = MutableStateFlow(
AddExistingWalletImportUM(
onBackClick = params.callbacks::onBackClick,
),
)
}

View file

@ -0,0 +1,5 @@
package com.tangem.features.hotwallet.addexistingwallet.im.port.entity
internal data class AddExistingWalletImportUM(
val onBackClick: () -> Unit,
)

View file

@ -0,0 +1,47 @@
package com.tangem.features.hotwallet.addexistingwallet.im.port.ui
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.appbar.TangemTopAppBar
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.hotwallet.addexistingwallet.im.port.entity.AddExistingWalletImportUM
import com.tangem.core.ui.R
@OptIn(ExperimentalMaterial3Api::class)
@Composable
internal fun AddExistingWalletImportContent(state: AddExistingWalletImportUM, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.background(TangemTheme.colors.background.primary)
.fillMaxSize()
.systemBarsPadding(),
) {
TangemTopAppBar(
modifier = Modifier
.statusBarsPadding(),
startButton = TopAppBarButtonUM.Back(state.onBackClick),
title = stringResourceSafe(R.string.wallet_import_title),
)
}
}
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun PreviewAddExistingWalletImportContent() {
TangemThemePreview {
AddExistingWalletImportContent(
state = AddExistingWalletImportUM(
onBackClick = {},
),
)
}
}

View file

@ -1,25 +1,41 @@
package com.tangem.features.hotwallet.addexistingwallet.root
import com.arkivanov.decompose.router.stack.StackNavigation
import com.arkivanov.decompose.router.stack.pop
import com.arkivanov.decompose.router.stack.push
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.navigation.Router
import com.tangem.features.hotwallet.addexistingwallet.start.AddExistingWalletStartComponent
import com.tangem.features.hotwallet.addexistingwallet.im.port.AddExistingWalletImportComponent
import com.tangem.features.hotwallet.addexistingwallet.root.routing.AddExistingWalletRoute
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import javax.inject.Inject
@ModelScoped
internal class AddExistingWalletModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
) : Model() {
val addExistingWalletStartModelCallbacks = AddExistingWalletStartModelCallbacks()
val addExistingWalletImportModelCallbacks = AddExistingWalletImportModelCallbacks()
val stackNavigation = StackNavigation<AddExistingWalletRoute>()
inner class AddExistingWalletStartModelCallbacks : AddExistingWalletStartComponent.ModelCallbacks {
override fun onBackClick() {
// TODO implement in [REDACTED_TASK_KEY]
router.pop()
}
override fun onImportPhraseClick() {
// TODO implement in [REDACTED_TASK_KEY]
stackNavigation.push(AddExistingWalletRoute.Import)
}
}
inner class AddExistingWalletImportModelCallbacks : AddExistingWalletImportComponent.ModelCallbacks {
override fun onBackClick() {
stackNavigation.pop()
}
}
}

View file

@ -5,7 +5,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import com.arkivanov.decompose.extensions.compose.subscribeAsState
import com.arkivanov.decompose.router.stack.StackNavigation
import com.arkivanov.decompose.router.stack.childStack
import com.arkivanov.decompose.router.stack.pop
import com.tangem.core.decompose.context.AppComponentContext
@ -27,12 +26,11 @@ internal class DefaultAddExistingWalletComponent @AssistedInject constructor(
private val model: AddExistingWalletModel = getOrCreateModel(params)
private val stackNavigation = StackNavigation<AddExistingWalletRoute>()
private val startRoute = AddExistingWalletRoute.Start
private val innerStack = childStack(
key = "addExistingWalletInnerStack",
source = stackNavigation,
source = model.stackNavigation,
serializer = null,
initialConfiguration = startRoute,
handleBackButton = true,
@ -61,7 +59,7 @@ internal class DefaultAddExistingWalletComponent @AssistedInject constructor(
if (isEmptyStack) {
router.pop()
} else {
stackNavigation.pop()
model.stackNavigation.pop()
}
}

View file

@ -0,0 +1,45 @@
package com.tangem.features.hotwallet.addexistingwallet.root.di
import com.tangem.core.decompose.model.Model
import com.tangem.features.hotwallet.AddExistingWalletComponent
import com.tangem.features.hotwallet.addexistingwallet.root.AddExistingWalletModel
import com.tangem.features.hotwallet.addexistingwallet.root.DefaultAddExistingWalletComponent
import com.tangem.features.hotwallet.addexistingwallet.start.AddExistingWalletStartModel
import com.tangem.features.hotwallet.addexistingwallet.im.port.AddExistingWalletImportModel
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 AddExistingWalletModule
@Module
@InstallIn(SingletonComponent::class)
internal interface AddExistingWalletModuleBinds {
@Binds
@Singleton
fun bindAddExistingWalletComponentFactory(
impl: DefaultAddExistingWalletComponent.Factory,
): AddExistingWalletComponent.Factory
@Binds
@IntoMap
@ClassKey(AddExistingWalletModel::class)
fun bindAddExistingWalletModel(model: AddExistingWalletModel): Model
@Binds
@IntoMap
@ClassKey(AddExistingWalletStartModel::class)
fun bindAddExistingWalletStartModel(model: AddExistingWalletStartModel): Model
@Binds
@IntoMap
@ClassKey(AddExistingWalletImportModel::class)
fun bindAddExistingWalletImportModel(model: AddExistingWalletImportModel): Model
}

View file

@ -4,6 +4,7 @@ import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.features.hotwallet.addexistingwallet.root.AddExistingWalletModel
import com.tangem.features.hotwallet.addexistingwallet.start.AddExistingWalletStartComponent
import com.tangem.features.hotwallet.addexistingwallet.im.port.AddExistingWalletImportComponent
import javax.inject.Inject
internal class AddExistingWalletChildFactory @Inject constructor() {
@ -20,6 +21,12 @@ internal class AddExistingWalletChildFactory @Inject constructor() {
callbacks = model.addExistingWalletStartModelCallbacks,
),
)
is AddExistingWalletRoute.Import -> AddExistingWalletImportComponent(
context = childContext,
params = AddExistingWalletImportComponent.Params(
callbacks = model.addExistingWalletImportModelCallbacks,
),
)
}
}
}

View file

@ -7,4 +7,7 @@ internal sealed class AddExistingWalletRoute : Route {
@Serializable
object Start : AddExistingWalletRoute()
@Serializable
object Import : AddExistingWalletRoute()
}

View file

@ -1,12 +1,10 @@
package com.tangem.features.hotwallet.addexistingwallet.root.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import com.arkivanov.decompose.extensions.compose.stack.Children
import com.arkivanov.decompose.extensions.compose.stack.animation.slide
@ -18,20 +16,15 @@ import com.tangem.features.hotwallet.addexistingwallet.root.routing.AddExistingW
@Composable
internal fun AddExistingWalletContent(stackState: ChildStack<AddExistingWalletRoute, ComposableContentComponent>) {
Column(
modifier = Modifier.Companion
Children(
stack = stackState,
animation = stackAnimation(slide()),
modifier = Modifier
.background(color = TangemTheme.colors.background.primary)
.fillMaxSize()
.imePadding()
.systemBarsPadding(),
horizontalAlignment = Alignment.Companion.CenterHorizontally,
) {
Children(
stack = stackState,
animation = stackAnimation(slide()),
modifier = Modifier.weight(1f),
) {
it.instance.Content(Modifier.weight(1f))
}
it.instance.Content(Modifier.fillMaxSize())
}
}

View file

@ -7,6 +7,8 @@ import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@ -20,19 +22,21 @@ import com.tangem.core.ui.extensions.conditional
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.hotwallet.addexistingwallet.start.entity.AddExistingWalletStartUM
import com.tangem.core.ui.R
import com.tangem.core.ui.extensions.stringResourceSafe
@Suppress("LongMethod")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
internal fun AddExistingWalletStartContent(state: AddExistingWalletStartUM, modifier: Modifier = Modifier) {
Column(
modifier = Modifier
modifier = modifier
.background(TangemTheme.colors.background.primary)
.fillMaxSize()
.systemBarsPadding(),
) {
TangemTopAppBar(
modifier = modifier
modifier = Modifier
.statusBarsPadding(),
startButton = TopAppBarButtonUM.Back(state.onBackClick),
title = "Add existing wallet",
@ -50,8 +54,7 @@ internal fun AddExistingWalletStartContent(state: AddExistingWalletStartUM, modi
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
// TODO [REDACTED_TASK_KEY] update and extract this string
text = "Import wallet",
text = stringResourceSafe(R.string.wallet_import_seed_navtitle),
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
@ -59,31 +62,35 @@ internal fun AddExistingWalletStartContent(state: AddExistingWalletStartUM, modi
OptionBlock(
modifier = Modifier
.padding(top = 24.dp),
// TODO [REDACTED_TASK_KEY] update and extract this string
title = "Import a recovery phrase",
// TODO [REDACTED_TASK_KEY] update and extract this string
description = "Your 12-word seed phrase to recover your wallet",
title = stringResourceSafe(R.string.wallet_import_seed_title),
description = stringResourceSafe(R.string.wallet_import_seed_description),
badge = null,
onClick = state.onImportPhraseClick,
enabled = true,
)
OptionBlock(
// TODO [REDACTED_TASK_KEY] update and extract this string
title = "Scan a Tangem Wallet",
// TODO [REDACTED_TASK_KEY] update and extract this string
description = "Physical cards that securely store your crypto offline.",
badge = null,
title = stringResourceSafe(R.string.wallet_import_scan_title),
description = stringResourceSafe(R.string.wallet_import_scan_description),
badge = {
Icon(
modifier = Modifier
.padding(top = 2.dp)
.size(20.dp),
painter = painterResource(R.drawable.ic_tangem_24),
contentDescription = null,
tint = TangemTheme.colors.icon.secondary,
)
},
onClick = state.onScanCardClick,
enabled = true,
)
OptionBlock(
// TODO [REDACTED_TASK_KEY] update and extract this string
title = "Import from Google Drive",
// TODO [REDACTED_TASK_KEY] update and extract this string
description = "Recover an existing wallet stored in your Google Drive backup",
title = stringResourceSafe(R.string.wallet_import_google_drive_title),
description = stringResourceSafe(R.string.wallet_import_google_drive_description),
badge = {
Box(
modifier = Modifier
.padding(horizontal = 4.dp)
.background(
color = TangemTheme.colors.field.focused,
shape = TangemTheme.shapes.roundedCorners8,
@ -91,8 +98,7 @@ internal fun AddExistingWalletStartContent(state: AddExistingWalletStartUM, modi
.padding(horizontal = 8.dp, vertical = 4.dp),
) {
Text(
// TODO [REDACTED_TASK_KEY] update and extract this string
text = "Coming Soon",
text = stringResourceSafe(R.string.common_coming_soon),
style = TangemTheme.typography.caption1,
color = TangemTheme.colors.text.tertiary,
)
@ -121,20 +127,21 @@ private fun OptionBlock(
modifier = modifier
.fillMaxWidth()
.padding(top = 8.dp)
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(
color = TangemTheme.colors.background.secondary,
shape = TangemTheme.shapes.roundedCornersXMedium,
)
.padding(16.dp)
.conditional(onClick != null) {
onClick?.let { clickableSingle(onClick = it) } ?: Modifier
},
}
.padding(16.dp),
) {
Row {
Text(
modifier = Modifier
.weight(1f)
.padding(end = 8.dp),
.weight(1f, fill = false)
.padding(end = 4.dp),
text = title,
style = TangemTheme.typography.subtitle1,
color = if (enabled) {
@ -179,16 +186,14 @@ private fun BuyTangemWalletBlock(onScanClick: () -> Unit, modifier: Modifier = M
modifier = Modifier
.weight(1f)
.padding(end = 16.dp),
// TODO [REDACTED_TASK_KEY] update and extract this string
text = "Want to purchase a Tangem Wallet?",
text = stringResourceSafe(R.string.wallet_import_buy_question),
style = TangemTheme.typography.button,
color = TangemTheme.colors.text.primary1,
)
PrimaryButton(
modifier = Modifier
.wrapContentWidth(),
// TODO [REDACTED_TASK_KEY] update and extract this string
text = "Buy Now",
text = stringResourceSafe(R.string.wallet_import_buy_title),
onClick = onScanClick,
size = TangemButtonSize.RoundedAction,
colors = TangemButtonsDefaults.secondaryButtonColors,

View file

@ -2,6 +2,7 @@ package com.tangem.features.hotwallet.createmobilewallet
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.navigation.Router
import com.tangem.features.hotwallet.createmobilewallet.entity.CreateMobileWalletUM
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.MutableStateFlow
@ -11,12 +12,13 @@ import javax.inject.Inject
@ModelScoped
internal class CreateMobileWalletModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
) : Model() {
internal val uiState: StateFlow<CreateMobileWalletUM>
field = MutableStateFlow(
CreateMobileWalletUM(
onBackClick = { /* [REDACTED_TODO_COMMENT] */ },
onBackClick = { router.pop() },
onCreateClick = ::onCreateClick,
),
)

View file

@ -0,0 +1,33 @@
package com.tangem.features.hotwallet.createmobilewallet.di
import com.tangem.core.decompose.model.Model
import com.tangem.features.hotwallet.CreateMobileWalletComponent
import com.tangem.features.hotwallet.createmobilewallet.CreateMobileWalletModel
import com.tangem.features.hotwallet.createmobilewallet.DefaultCreateMobileWalletComponent
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 CreateMobileWalletModule
@Module
@InstallIn(SingletonComponent::class)
internal interface CreateMobileWalletModuleBinds {
@Binds
@Singleton
fun bindCreateMobileWalletComponentFactory(
impl: DefaultCreateMobileWalletComponent.Factory,
): CreateMobileWalletComponent.Factory
@Binds
@IntoMap
@ClassKey(CreateMobileWalletModel::class)
fun bindCreateMobileWalletModel(model: CreateMobileWalletModel): Model
}

View file

@ -15,6 +15,7 @@ import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.components.appbar.TangemTopAppBar
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.hotwallet.createmobilewallet.entity.CreateMobileWalletUM
@ -24,13 +25,13 @@ import com.tangem.features.hotwallet.createmobilewallet.entity.CreateMobileWalle
@Composable
internal fun CreateMobileWalletContent(state: CreateMobileWalletUM, modifier: Modifier = Modifier) {
Column(
modifier = Modifier
modifier = modifier
.background(TangemTheme.colors.background.primary)
.fillMaxSize()
.systemBarsPadding(),
) {
TangemTopAppBar(
modifier = modifier
modifier = Modifier
.statusBarsPadding(),
startButton = TopAppBarButtonUM.Back(state.onBackClick),
title = TextReference.EMPTY,
@ -59,8 +60,7 @@ internal fun CreateMobileWalletContent(state: CreateMobileWalletUM, modifier: Mo
top = 20.dp,
end = 16.dp,
),
// TODO [REDACTED_TASK_KEY] update and extract this string
text = "Create Mobile Wallet",
text = stringResourceSafe(R.string.hw_create_title),
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
@ -68,19 +68,15 @@ internal fun CreateMobileWalletContent(state: CreateMobileWalletUM, modifier: Mo
FeatureBlock(
modifier = Modifier
.padding(top = 32.dp),
// TODO [REDACTED_TASK_KEY] update and extract this string
title = "Keys are stored in the app",
// TODO [REDACTED_TASK_KEY] update and extract this string
description = "Get notified of incoming transactions",
title = stringResourceSafe(R.string.hw_create_keys_title),
description = stringResourceSafe(R.string.hw_create_keys_description),
iconRes = R.drawable.ic_lock_24,
)
FeatureBlock(
modifier = Modifier
.padding(top = 24.dp),
// TODO [REDACTED_TASK_KEY] update and extract this string
title = "Seed phrase backup",
// TODO [REDACTED_TASK_KEY] update and extract this string
description = "Stay up to date with the latest features and news",
title = stringResourceSafe(R.string.hw_create_seed_title),
description = stringResourceSafe(R.string.hw_create_seed_description),
iconRes = R.drawable.ic_settings_24,
)
}