Updated on 2026-08-14
This commit is contained in:
commit
702e0b2efc
12 changed files with 472 additions and 1 deletions
|
|
@ -157,9 +157,10 @@ private fun WalletBlock(
|
|||
.padding(16.dp)
|
||||
.clickable(onClick = onClick),
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Row {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(end = 8.dp),
|
||||
text = title,
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.features.hotwallet
|
||||
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
|
||||
interface AddExistingWalletComponent : ComposableContentComponent {
|
||||
interface Factory : ComponentFactory<Unit, AddExistingWalletComponent>
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.tangem.features.hotwallet.addexistingwallet.root
|
||||
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.hotwallet.addexistingwallet.start.AddExistingWalletStartComponent
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
internal class AddExistingWalletModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
) : Model() {
|
||||
|
||||
val addExistingWalletStartModelCallbacks = AddExistingWalletStartModelCallbacks()
|
||||
|
||||
inner class AddExistingWalletStartModelCallbacks : AddExistingWalletStartComponent.ModelCallbacks {
|
||||
override fun onBackClick() {
|
||||
// TODO implement in [REDACTED_TASK_KEY]
|
||||
}
|
||||
|
||||
override fun onImportPhraseClick() {
|
||||
// TODO implement in [REDACTED_TASK_KEY]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
package com.tangem.features.hotwallet.addexistingwallet.root
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
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
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.features.hotwallet.AddExistingWalletComponent
|
||||
import com.tangem.features.hotwallet.addexistingwallet.root.routing.AddExistingWalletChildFactory
|
||||
import com.tangem.features.hotwallet.addexistingwallet.root.routing.AddExistingWalletRoute
|
||||
import com.tangem.features.hotwallet.addexistingwallet.root.ui.AddExistingWalletContent
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
internal class DefaultAddExistingWalletComponent @AssistedInject constructor(
|
||||
@Assisted appComponentContext: AppComponentContext,
|
||||
@Assisted private val params: Unit,
|
||||
addExistingWalletChildFactory: AddExistingWalletChildFactory,
|
||||
) : AddExistingWalletComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: AddExistingWalletModel = getOrCreateModel(params)
|
||||
|
||||
private val stackNavigation = StackNavigation<AddExistingWalletRoute>()
|
||||
private val startRoute = AddExistingWalletRoute.Start
|
||||
|
||||
private val innerStack = childStack(
|
||||
key = "addExistingWalletInnerStack",
|
||||
source = stackNavigation,
|
||||
serializer = null,
|
||||
initialConfiguration = startRoute,
|
||||
handleBackButton = true,
|
||||
childFactory = { configuration, factoryContext ->
|
||||
addExistingWalletChildFactory.createChild(
|
||||
route = configuration,
|
||||
childContext = childByContext(factoryContext),
|
||||
model = model,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val stackState by innerStack.subscribeAsState()
|
||||
|
||||
BackHandler(onBack = ::onChildBack)
|
||||
AddExistingWalletContent(
|
||||
stackState = stackState,
|
||||
)
|
||||
}
|
||||
|
||||
private fun onChildBack() {
|
||||
val isEmptyStack = innerStack.value.backStack.isEmpty()
|
||||
|
||||
if (isEmptyStack) {
|
||||
router.pop()
|
||||
} else {
|
||||
stackNavigation.pop()
|
||||
}
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : AddExistingWalletComponent.Factory {
|
||||
override fun create(context: AppComponentContext, params: Unit): DefaultAddExistingWalletComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.features.hotwallet.addexistingwallet.root.entity
|
||||
|
||||
internal data class AddExistingWalletUM(
|
||||
val onBackClick: () -> Unit,
|
||||
)
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.tangem.features.hotwallet.addexistingwallet.root.routing
|
||||
|
||||
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 javax.inject.Inject
|
||||
|
||||
internal class AddExistingWalletChildFactory @Inject constructor() {
|
||||
|
||||
fun createChild(
|
||||
route: AddExistingWalletRoute,
|
||||
childContext: AppComponentContext,
|
||||
model: AddExistingWalletModel,
|
||||
): ComposableContentComponent {
|
||||
return when (route) {
|
||||
is AddExistingWalletRoute.Start -> AddExistingWalletStartComponent(
|
||||
context = childContext,
|
||||
params = AddExistingWalletStartComponent.Params(
|
||||
callbacks = model.addExistingWalletStartModelCallbacks,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.features.hotwallet.addexistingwallet.root.routing
|
||||
|
||||
import com.tangem.core.decompose.navigation.Route
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
internal sealed class AddExistingWalletRoute : Route {
|
||||
|
||||
@Serializable
|
||||
object Start : AddExistingWalletRoute()
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
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
|
||||
import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation
|
||||
import com.arkivanov.decompose.router.stack.ChildStack
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.hotwallet.addexistingwallet.root.routing.AddExistingWalletRoute
|
||||
|
||||
@Composable
|
||||
internal fun AddExistingWalletContent(stackState: ChildStack<AddExistingWalletRoute, ComposableContentComponent>) {
|
||||
Column(
|
||||
modifier = Modifier.Companion
|
||||
.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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.tangem.features.hotwallet.addexistingwallet.start
|
||||
|
||||
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.start.ui.AddExistingWalletStartContent
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
internal class AddExistingWalletStartComponent @AssistedInject constructor(
|
||||
@Assisted private val context: AppComponentContext,
|
||||
@Assisted private val params: Params,
|
||||
) : ComposableContentComponent, AppComponentContext by context {
|
||||
private val model: AddExistingWalletStartModel = getOrCreateModel(params)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
AddExistingWalletStartContent(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
interface ModelCallbacks {
|
||||
fun onBackClick()
|
||||
fun onImportPhraseClick()
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val callbacks: ModelCallbacks,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.tangem.features.hotwallet.addexistingwallet.start
|
||||
|
||||
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.start.entity.AddExistingWalletStartUM
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
internal class AddExistingWalletStartModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
) : Model() {
|
||||
|
||||
private val params: AddExistingWalletStartComponent.Params = paramsContainer.require()
|
||||
|
||||
internal val uiState: StateFlow<AddExistingWalletStartUM>
|
||||
field = MutableStateFlow(
|
||||
AddExistingWalletStartUM(
|
||||
onBackClick = params.callbacks::onBackClick,
|
||||
onImportPhraseClick = params.callbacks::onImportPhraseClick,
|
||||
onScanCardClick = { /* [REDACTED_TODO_COMMENT] */ },
|
||||
onBuyCardClick = { /* [REDACTED_TODO_COMMENT] */ },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.features.hotwallet.addexistingwallet.start.entity
|
||||
|
||||
internal data class AddExistingWalletStartUM(
|
||||
val onBackClick: () -> Unit,
|
||||
val onImportPhraseClick: () -> Unit,
|
||||
val onScanCardClick: () -> Unit,
|
||||
val onBuyCardClick: () -> Unit,
|
||||
)
|
||||
|
|
@ -0,0 +1,214 @@
|
|||
package com.tangem.features.hotwallet.addexistingwallet.start.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.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
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.components.buttons.common.TangemButtonSize
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults
|
||||
import com.tangem.core.ui.extensions.clickableSingle
|
||||
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
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
internal fun AddExistingWalletStartContent(state: AddExistingWalletStartUM, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.fillMaxSize()
|
||||
.systemBarsPadding(),
|
||||
) {
|
||||
TangemTopAppBar(
|
||||
modifier = modifier
|
||||
.statusBarsPadding(),
|
||||
startButton = TopAppBarButtonUM.Back(state.onBackClick),
|
||||
title = "Add existing wallet",
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(
|
||||
start = 16.dp,
|
||||
top = 24.dp,
|
||||
end = 16.dp,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
// TODO [REDACTED_TASK_KEY] update and extract this string
|
||||
text = "Import wallet",
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
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",
|
||||
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,
|
||||
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",
|
||||
badge = {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.field.focused,
|
||||
shape = TangemTheme.shapes.roundedCorners8,
|
||||
)
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp),
|
||||
) {
|
||||
Text(
|
||||
// TODO [REDACTED_TASK_KEY] update and extract this string
|
||||
text = "Coming Soon",
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
},
|
||||
onClick = null,
|
||||
enabled = false,
|
||||
)
|
||||
}
|
||||
BuyTangemWalletBlock(
|
||||
onScanClick = state.onBuyCardClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun OptionBlock(
|
||||
title: String,
|
||||
description: String,
|
||||
badge: (@Composable () -> Unit)?,
|
||||
onClick: (() -> Unit)?,
|
||||
enabled: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 8.dp)
|
||||
.background(
|
||||
color = TangemTheme.colors.background.secondary,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
)
|
||||
.padding(16.dp)
|
||||
.conditional(onClick != null) {
|
||||
onClick?.let { clickableSingle(onClick = it) } ?: Modifier
|
||||
},
|
||||
) {
|
||||
Row {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(end = 8.dp),
|
||||
text = title,
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = if (enabled) {
|
||||
TangemTheme.colors.text.primary1
|
||||
} else {
|
||||
TangemTheme.colors.text.secondary
|
||||
},
|
||||
)
|
||||
badge?.invoke()
|
||||
}
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(top = 4.dp),
|
||||
text = description,
|
||||
style = TangemTheme.typography.body2,
|
||||
color = if (enabled) {
|
||||
TangemTheme.colors.text.tertiary
|
||||
} else {
|
||||
TangemTheme.colors.text.disabled
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BuyTangemWalletBlock(onScanClick: () -> Unit, 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 [REDACTED_TASK_KEY] update and extract this string
|
||||
text = "Want to purchase a Tangem Wallet?",
|
||||
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",
|
||||
onClick = onScanClick,
|
||||
size = TangemButtonSize.RoundedAction,
|
||||
colors = TangemButtonsDefaults.secondaryButtonColors,
|
||||
enabled = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun PreviewCreateWalletContent() {
|
||||
TangemThemePreview {
|
||||
AddExistingWalletStartContent(
|
||||
state = AddExistingWalletStartUM(
|
||||
onBackClick = {},
|
||||
onImportPhraseClick = {},
|
||||
onScanCardClick = {},
|
||||
onBuyCardClick = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue