diff --git a/features/create-wallet-selection/impl/src/main/kotlin/com/tangem/features/createwalletselection/ui/CreateWalletSelectionContent.kt b/features/create-wallet-selection/impl/src/main/kotlin/com/tangem/features/createwalletselection/ui/CreateWalletSelectionContent.kt index 4c66af5a94..e5ffea8e68 100644 --- a/features/create-wallet-selection/impl/src/main/kotlin/com/tangem/features/createwalletselection/ui/CreateWalletSelectionContent.kt +++ b/features/create-wallet-selection/impl/src/main/kotlin/com/tangem/features/createwalletselection/ui/CreateWalletSelectionContent.kt @@ -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, diff --git a/features/hot-wallet/api/src/main/kotlin/com/tangem/features/hotwallet/AddExistingWalletComponent.kt b/features/hot-wallet/api/src/main/kotlin/com/tangem/features/hotwallet/AddExistingWalletComponent.kt new file mode 100644 index 0000000000..f9bf2edf1e --- /dev/null +++ b/features/hot-wallet/api/src/main/kotlin/com/tangem/features/hotwallet/AddExistingWalletComponent.kt @@ -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 +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/AddExistingWalletModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/AddExistingWalletModel.kt new file mode 100644 index 0000000000..abbb917a03 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/AddExistingWalletModel.kt @@ -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] + } + } +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/DefaultAddExistingWalletComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/DefaultAddExistingWalletComponent.kt new file mode 100644 index 0000000000..4ef6395750 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/DefaultAddExistingWalletComponent.kt @@ -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() + 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 + } +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/entity/AddExistingWalletUM.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/entity/AddExistingWalletUM.kt new file mode 100644 index 0000000000..8030308588 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/entity/AddExistingWalletUM.kt @@ -0,0 +1,5 @@ +package com.tangem.features.hotwallet.addexistingwallet.root.entity + +internal data class AddExistingWalletUM( + val onBackClick: () -> Unit, +) \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/routing/AddExistingWalletChildFactory.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/routing/AddExistingWalletChildFactory.kt new file mode 100644 index 0000000000..b9079208c6 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/routing/AddExistingWalletChildFactory.kt @@ -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, + ), + ) + } + } +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/routing/AddExistingWalletRoute.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/routing/AddExistingWalletRoute.kt new file mode 100644 index 0000000000..56ca941809 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/routing/AddExistingWalletRoute.kt @@ -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() +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/ui/AddExistingWalletContent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/ui/AddExistingWalletContent.kt new file mode 100644 index 0000000000..9d3367cbc7 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/root/ui/AddExistingWalletContent.kt @@ -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) { + 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)) + } + } +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/start/AddExistingWalletStartComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/start/AddExistingWalletStartComponent.kt new file mode 100644 index 0000000000..42e01eaf3b --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/start/AddExistingWalletStartComponent.kt @@ -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, + ) +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/start/AddExistingWalletStartModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/start/AddExistingWalletStartModel.kt new file mode 100644 index 0000000000..58ea450355 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/start/AddExistingWalletStartModel.kt @@ -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 + field = MutableStateFlow( + AddExistingWalletStartUM( + onBackClick = params.callbacks::onBackClick, + onImportPhraseClick = params.callbacks::onImportPhraseClick, + onScanCardClick = { /* [REDACTED_TODO_COMMENT] */ }, + onBuyCardClick = { /* [REDACTED_TODO_COMMENT] */ }, + ), + ) +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/start/entity/AddExistingWalletStartUM.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/start/entity/AddExistingWalletStartUM.kt new file mode 100644 index 0000000000..f898f9c1b7 --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/start/entity/AddExistingWalletStartUM.kt @@ -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, +) \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/start/ui/AddExistingWalletStartContent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/start/ui/AddExistingWalletStartContent.kt new file mode 100644 index 0000000000..a0a2ea6abe --- /dev/null +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/start/ui/AddExistingWalletStartContent.kt @@ -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 = {}, + ), + ) + } +} \ No newline at end of file