Updated on 2026-08-14
This commit is contained in:
parent
56164d0b8b
commit
b469c49178
19 changed files with 738 additions and 26 deletions
|
|
@ -40,7 +40,6 @@ sealed class AppRoute(val path: String) : Route {
|
|||
val isTosAccepted: Boolean,
|
||||
) : AppRoute(path = "/disclaimer${if (isTosAccepted) "/tos_accepted" else ""}")
|
||||
|
||||
@Deprecated("Do not use! Should be replaced by an implementation in onboarding component")
|
||||
@Serializable
|
||||
data object OnboardingNote : AppRoute(path = "/onboarding/note")
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.features.onboarding.v2.common.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import coil.compose.AsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
|
||||
@Composable
|
||||
internal fun WalletCard(url: String?, modifier: Modifier = Modifier) {
|
||||
AsyncImage(
|
||||
modifier = modifier,
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
.data(url)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
placeholder = painterResource(R.drawable.card_placeholder_black),
|
||||
error = painterResource(R.drawable.card_placeholder_black),
|
||||
fallback = painterResource(R.drawable.card_placeholder_black),
|
||||
contentScale = ContentScale.Fit,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
|
@ -15,19 +15,14 @@ import androidx.compose.runtime.*
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import androidx.compose.ui.zIndex
|
||||
import coil.compose.AsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
import com.tangem.features.onboarding.v2.common.ui.WalletCard
|
||||
import kotlinx.coroutines.delay
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
|
@ -244,22 +239,6 @@ private fun AnimatedWalletCards(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WalletCard(url: String?, modifier: Modifier = Modifier) {
|
||||
AsyncImage(
|
||||
modifier = modifier,
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
.data(url)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
placeholder = painterResource(R.drawable.card_placeholder_black),
|
||||
error = painterResource(R.drawable.card_placeholder_black),
|
||||
fallback = painterResource(R.drawable.card_placeholder_black),
|
||||
contentScale = ContentScale.Fit,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
||||
private fun WalletArtworksState.toTransitionSetState(maxWidthDp: Float, maxHeightDp: Float, density: Float) =
|
||||
when (this) {
|
||||
WalletArtworksState.Hidden -> listOf()
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
package com.tangem.features.onboarding.v2.note.api
|
||||
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.decompose.navigation.inner.InnerNavigationHolder
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.features.onboarding.v2.TitleProvider
|
||||
|
||||
interface OnboardingNoteComponent : ComposableContentComponent {
|
||||
interface OnboardingNoteComponent : ComposableContentComponent, InnerNavigationHolder {
|
||||
|
||||
data class Params(
|
||||
val titleProvider: TitleProvider,
|
||||
|
|
|
|||
|
|
@ -1,21 +1,128 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
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.extensions.compose.subscribeAsState
|
||||
import com.arkivanov.decompose.router.stack.ChildStack
|
||||
import com.arkivanov.decompose.router.stack.childStack
|
||||
import com.arkivanov.decompose.router.stack.push
|
||||
import com.arkivanov.decompose.value.Value
|
||||
import com.arkivanov.decompose.value.subscribe
|
||||
import com.arkivanov.essenty.instancekeeper.getOrCreateSimple
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.decompose.navigation.inner.InnerNavigation
|
||||
import com.tangem.core.decompose.navigation.inner.InnerNavigationState
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
import com.tangem.features.onboarding.v2.note.api.OnboardingNoteComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.create.OnboardingNoteCreateWalletComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.topup.OnboardingNoteTopUpComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.model.OnboardingNoteModel
|
||||
import com.tangem.features.onboarding.v2.note.impl.route.ONBOARDING_NOTE_STEPS_COUNT
|
||||
import com.tangem.features.onboarding.v2.note.impl.route.OnboardingNoteRoute
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
internal class DefaultOnboardingNoteComponent @AssistedInject constructor(
|
||||
@Assisted context: AppComponentContext,
|
||||
@Assisted val params: OnboardingNoteComponent.Params,
|
||||
) : OnboardingNoteComponent, AppComponentContext by context {
|
||||
|
||||
private val model: OnboardingNoteModel = getOrCreateModel(params)
|
||||
|
||||
private val currentChildBackEventHandle = instanceKeeper.getOrCreateSimple(key = "currentChildBackEventHandle") {
|
||||
MutableSharedFlow<Unit>()
|
||||
}
|
||||
|
||||
private val childParams = ChildParams(
|
||||
onBack = ::onChildBack,
|
||||
parentBackEvent = currentChildBackEventHandle,
|
||||
)
|
||||
|
||||
private val childStack: Value<ChildStack<OnboardingNoteRoute, ComposableContentComponent>> =
|
||||
childStack(
|
||||
key = "innerStack",
|
||||
source = model.stackNavigation,
|
||||
serializer = null,
|
||||
initialConfiguration = model.initialRoute,
|
||||
handleBackButton = true,
|
||||
childFactory = { configuration, factoryContext ->
|
||||
createChild(
|
||||
configuration,
|
||||
childByContext(factoryContext),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
override val innerNavigation: InnerNavigation = object : InnerNavigation {
|
||||
override val state: StateFlow<InnerNavigationState> = model.innerNavigationState
|
||||
|
||||
override fun pop(onComplete: (Boolean) -> Unit) {
|
||||
// TODO [REDACTED_TASK_KEY]
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
componentScope.launch {
|
||||
model.onDone.collect { params.onDone() }
|
||||
}
|
||||
|
||||
// sets title and stepper value
|
||||
childStack.subscribe(lifecycle) { stack ->
|
||||
val currentRoute = stack.active.configuration
|
||||
params.titleProvider.changeTitle(TextReference.Res(R.string.onboarding_title))
|
||||
model.updateStepForNewRoute(currentRoute)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createChild(
|
||||
route: OnboardingNoteRoute,
|
||||
factoryContext: AppComponentContext,
|
||||
): ComposableContentComponent {
|
||||
return when (route) {
|
||||
OnboardingNoteRoute.CreateWallet -> OnboardingNoteCreateWalletComponent(
|
||||
appComponentContext = factoryContext,
|
||||
params = OnboardingNoteCreateWalletComponent.Params(
|
||||
childParams = childParams,
|
||||
onDone = { model.stackNavigation.push(OnboardingNoteRoute.TopUp) },
|
||||
),
|
||||
)
|
||||
OnboardingNoteRoute.TopUp -> OnboardingNoteTopUpComponent(
|
||||
appComponentContext = factoryContext,
|
||||
params = OnboardingNoteTopUpComponent.Params(
|
||||
childParams = childParams,
|
||||
onDone = { model.onDone.tryEmit(Unit) },
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onChildBack() = model.onChildBack(
|
||||
currentRoute = childStack.value.active.configuration,
|
||||
)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
// TODO [REDACTED_TASK_KEY]
|
||||
val stackState by childStack.subscribeAsState()
|
||||
|
||||
Children(
|
||||
stack = stackState,
|
||||
animation = stackAnimation(slide()),
|
||||
) {
|
||||
it.instance.Content(modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
|
|
@ -25,4 +132,17 @@ internal class DefaultOnboardingNoteComponent @AssistedInject constructor(
|
|||
params: OnboardingNoteComponent.Params,
|
||||
): DefaultOnboardingNoteComponent
|
||||
}
|
||||
}
|
||||
|
||||
data class ChildParams(
|
||||
val onBack: () -> Unit,
|
||||
val parentBackEvent: SharedFlow<Unit>,
|
||||
)
|
||||
}
|
||||
|
||||
internal data class OnboardingNoteInnerNavigationState(
|
||||
override val stackSize: Int,
|
||||
) : InnerNavigationState {
|
||||
override val stackMaxSize: Int = ONBOARDING_NOTE_STEPS_COUNT
|
||||
}
|
||||
|
||||
internal const val ALL_STEPS_TOP_CONTAINER_WEIGHT = 0.48f
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.child.create
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
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.onboarding.v2.note.impl.DefaultOnboardingNoteComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.create.model.OnboardingNoteCreateWalletModel
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.create.ui.OnboardingNoteCreateWallet
|
||||
|
||||
internal class OnboardingNoteCreateWalletComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
private val params: Params,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: OnboardingNoteCreateWalletModel = getOrCreateModel(params)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
BackHandler(onBack = remember(this) { { params.childParams.onBack() } })
|
||||
|
||||
OnboardingNoteCreateWallet(
|
||||
modifier = modifier,
|
||||
state = state,
|
||||
)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val childParams: DefaultOnboardingNoteComponent.ChildParams,
|
||||
val onDone: () -> Unit,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.child.create.model
|
||||
|
||||
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.onboarding.v2.note.impl.child.create.ui.state.OnboardingNoteCreateWalletUM
|
||||
import com.tangem.sdk.api.TangemSdkManager
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
@ModelScoped
|
||||
internal class OnboardingNoteCreateWalletModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val tangemSdkManager: TangemSdkManager,
|
||||
) : Model() {
|
||||
|
||||
private val _uiState = MutableStateFlow(
|
||||
OnboardingNoteCreateWalletUM(),
|
||||
)
|
||||
|
||||
val uiState: StateFlow<OnboardingNoteCreateWalletUM> = _uiState
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.child.create.ui
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
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.PrimaryButtonIconEnd
|
||||
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.onboarding.v2.impl.R
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.ui.WalletArtworks
|
||||
import com.tangem.features.onboarding.v2.multiwallet.impl.ui.WalletArtworksState
|
||||
import com.tangem.features.onboarding.v2.note.impl.ALL_STEPS_TOP_CONTAINER_WEIGHT
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.create.ui.state.OnboardingNoteCreateWalletUM
|
||||
|
||||
@Composable
|
||||
internal fun OnboardingNoteCreateWallet(state: OnboardingNoteCreateWalletUM, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.navigationBarsPadding(),
|
||||
verticalArrangement = Arrangement.Bottom,
|
||||
) {
|
||||
WalletArtworks(
|
||||
url1 = state.artworkUrl,
|
||||
url2 = null,
|
||||
url3 = null,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 34.dp)
|
||||
.padding(top = 56.dp)
|
||||
.weight(ALL_STEPS_TOP_CONTAINER_WEIGHT)
|
||||
.fillMaxWidth(),
|
||||
state = WalletArtworksState.Folded,
|
||||
)
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(32.dp)
|
||||
.weight(1.0f),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.onboarding_create_wallet_header),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.onboarding_create_wallet_body),
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
)
|
||||
}
|
||||
|
||||
PrimaryButtonIconEnd(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
text = stringResourceSafe(R.string.onboarding_create_wallet_button_create_wallet),
|
||||
onClick = state.onCreateClick,
|
||||
showProgress = state.createWalletInProgress,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun OnboardingNoteCreatePreview() {
|
||||
TangemThemePreview {
|
||||
OnboardingNoteCreateWallet(
|
||||
state = OnboardingNoteCreateWalletUM(
|
||||
artworkUrl = null,
|
||||
createWalletInProgress = false,
|
||||
onCreateClick = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.child.create.ui.state
|
||||
|
||||
data class OnboardingNoteCreateWalletUM(
|
||||
val artworkUrl: String? = null,
|
||||
val createWalletInProgress: Boolean = false,
|
||||
val onCreateClick: () -> Unit = {},
|
||||
)
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.child.topup
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
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.onboarding.v2.note.impl.DefaultOnboardingNoteComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.topup.model.OnboardingNoteTopUpModel
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.topup.ui.OnboardingNoteTopUp
|
||||
|
||||
internal class OnboardingNoteTopUpComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
private val params: Params,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: OnboardingNoteTopUpModel = getOrCreateModel(params)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
BackHandler(onBack = remember(this) { { params.childParams.onBack() } })
|
||||
|
||||
OnboardingNoteTopUp(
|
||||
modifier = modifier,
|
||||
state = state,
|
||||
)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val childParams: DefaultOnboardingNoteComponent.ChildParams,
|
||||
val onDone: () -> Unit,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.child.topup.model
|
||||
|
||||
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.onboarding.v2.note.impl.child.topup.ui.state.OnboardingNoteTopUpUM
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
@ModelScoped
|
||||
internal class OnboardingNoteTopUpModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
) : Model() {
|
||||
|
||||
private val _uiState = MutableStateFlow(
|
||||
OnboardingNoteTopUpUM(),
|
||||
)
|
||||
|
||||
val uiState: StateFlow<OnboardingNoteTopUpUM> = _uiState
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.child.topup.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
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
|
||||
import com.tangem.core.ui.components.SpacerH8
|
||||
import com.tangem.core.ui.components.SpacerHMax
|
||||
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.onboarding.v2.common.ui.WalletCard
|
||||
import com.tangem.features.onboarding.v2.impl.R
|
||||
|
||||
@Composable
|
||||
fun OnboardinNoteTopUpHeader(
|
||||
balance: String?,
|
||||
cardArtworkUrl: String?,
|
||||
isRefreshing: Boolean,
|
||||
onRefreshBalanceClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 24.dp, horizontal = 32.dp)
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
TangemTheme.colors.button.secondary,
|
||||
shape = TangemTheme.shapes.roundedCornersMedium,
|
||||
),
|
||||
)
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.padding(horizontal = 32.dp),
|
||||
) {
|
||||
WalletCard(
|
||||
modifier = Modifier.width(120.dp),
|
||||
url = cardArtworkUrl,
|
||||
)
|
||||
SpacerHMax()
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.common_balance_title),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
SpacerH8()
|
||||
Text(
|
||||
text = balance.orEmpty(),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
SpacerHMax()
|
||||
RefreshButton(
|
||||
isRefreshing = isRefreshing,
|
||||
onRefreshBalanceClick = onRefreshBalanceClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RefreshButton(isRefreshing: Boolean, onRefreshBalanceClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(TangemTheme.dimens.size48)
|
||||
.shadow(elevation = 2.dp, shape = CircleShape)
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.clickable(
|
||||
enabled = !isRefreshing,
|
||||
onClick = onRefreshBalanceClick,
|
||||
indication = ripple(),
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
),
|
||||
) {
|
||||
if (isRefreshing) {
|
||||
Box {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.padding(TangemTheme.dimens.spacing12),
|
||||
color = TangemTheme.colors.icon.primary1,
|
||||
strokeWidth = TangemTheme.dimens.size2,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_refresh_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.text.disabled,
|
||||
modifier = Modifier.padding(TangemTheme.dimens.spacing12),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun OnboardinNoteTopUpHeaderPreview() {
|
||||
TangemThemePreview {
|
||||
OnboardinNoteTopUpHeader(
|
||||
balance = "0.00000001 BTC",
|
||||
cardArtworkUrl = "",
|
||||
onRefreshBalanceClick = {},
|
||||
isRefreshing = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.child.topup.ui
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Text
|
||||
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.PrimaryButtonIconEnd
|
||||
import com.tangem.core.ui.components.SecondaryButton
|
||||
import com.tangem.core.ui.components.SpacerH8
|
||||
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.onboarding.v2.impl.R
|
||||
import com.tangem.features.onboarding.v2.note.impl.ALL_STEPS_TOP_CONTAINER_WEIGHT
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.topup.ui.state.OnboardingNoteTopUpUM
|
||||
|
||||
@Composable
|
||||
fun OnboardingNoteTopUp(state: OnboardingNoteTopUpUM, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.navigationBarsPadding(),
|
||||
verticalArrangement = Arrangement.Bottom,
|
||||
) {
|
||||
OnboardinNoteTopUpHeader(
|
||||
balance = state.balance,
|
||||
cardArtworkUrl = state.cardArtworkUrl,
|
||||
onRefreshBalanceClick = state.onRefreshBalanceClick,
|
||||
isRefreshing = state.isRefreshing,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 34.dp)
|
||||
.padding(top = 20.dp)
|
||||
.weight(ALL_STEPS_TOP_CONTAINER_WEIGHT)
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(32.dp)
|
||||
.weight(1.0f),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.onboarding_topup_title),
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.onboarding_top_up_body),
|
||||
style = TangemTheme.typography.body1,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
)
|
||||
}
|
||||
|
||||
BottomButtons(state)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BottomButtons(state: OnboardingNoteTopUpUM) {
|
||||
if (state.availableForBuy) {
|
||||
PrimaryButtonIconEnd(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
text = stringResourceSafe(R.string.onboarding_top_up_button_but_crypto),
|
||||
onClick = state.onBuyCryptoClick,
|
||||
)
|
||||
SpacerH8()
|
||||
SecondaryButton(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
text = stringResourceSafe(R.string.onboarding_top_up_button_show_wallet_address),
|
||||
onClick = state.onShowWalletAddressClick,
|
||||
)
|
||||
} else {
|
||||
PrimaryButtonIconEnd(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
|
||||
.fillMaxWidth(),
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
text = stringResourceSafe(R.string.onboarding_button_receive_crypto),
|
||||
onClick = state.onShowWalletAddressClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun OnboardingNoteTopUpPreview() {
|
||||
TangemThemePreview {
|
||||
OnboardingNoteTopUp(
|
||||
state = OnboardingNoteTopUpUM(
|
||||
availableForBuy = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.child.topup.ui.state
|
||||
|
||||
data class OnboardingNoteTopUpUM(
|
||||
val cardArtworkUrl: String? = null,
|
||||
val availableForBuy: Boolean = true,
|
||||
val balance: String? = null,
|
||||
val isRefreshing: Boolean = false,
|
||||
val amountToCreateAccount: String? = null,
|
||||
val onBuyCryptoClick: () -> Unit = {},
|
||||
val onShowWalletAddressClick: () -> Unit = {},
|
||||
val onRefreshBalanceClick: () -> Unit = {},
|
||||
)
|
||||
|
|
@ -1,11 +1,18 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.di
|
||||
|
||||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.onboarding.v2.note.api.OnboardingNoteComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.DefaultOnboardingNoteComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.create.model.OnboardingNoteCreateWalletModel
|
||||
import com.tangem.features.onboarding.v2.note.impl.child.topup.model.OnboardingNoteTopUpModel
|
||||
import com.tangem.features.onboarding.v2.note.impl.model.OnboardingNoteModel
|
||||
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
|
||||
|
|
@ -15,4 +22,24 @@ internal interface ComponentModule {
|
|||
@Binds
|
||||
@Singleton
|
||||
fun bindComponent(factory: DefaultOnboardingNoteComponent.Factory): OnboardingNoteComponent.Factory
|
||||
}
|
||||
|
||||
@Module
|
||||
@InstallIn(ModelComponent::class)
|
||||
internal interface ModelModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(OnboardingNoteModel::class)
|
||||
fun provideNoteModel(model: OnboardingNoteModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(OnboardingNoteCreateWalletModel::class)
|
||||
fun provideNoteCreateWalletModel(model: OnboardingNoteCreateWalletModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(OnboardingNoteTopUpModel::class)
|
||||
fun provideNoteTopUpModel(model: OnboardingNoteTopUpModel): Model
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.model
|
||||
|
||||
import com.arkivanov.decompose.router.stack.StackNavigation
|
||||
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.onboarding.v2.note.api.OnboardingNoteComponent
|
||||
import com.tangem.features.onboarding.v2.note.impl.OnboardingNoteInnerNavigationState
|
||||
import com.tangem.features.onboarding.v2.note.impl.route.OnboardingNoteRoute
|
||||
import com.tangem.features.onboarding.v2.note.impl.route.stepNum
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
internal class OnboardingNoteModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
) : Model() {
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
private val params = paramsContainer.require<OnboardingNoteComponent.Params>()
|
||||
|
||||
private val _currentScanResponse = MutableStateFlow(params.scanResponse)
|
||||
|
||||
val initialRoute = OnboardingNoteRoute.CreateWallet // TODO [REDACTED_TASK_KEY]
|
||||
|
||||
private val _innerNavigationState =
|
||||
MutableStateFlow(OnboardingNoteInnerNavigationState(stackSize = initialRoute.stepNum()))
|
||||
|
||||
val currentScanResponse = _currentScanResponse.asStateFlow()
|
||||
|
||||
val innerNavigationState = _innerNavigationState.asStateFlow()
|
||||
|
||||
val onDone = MutableSharedFlow<Unit>()
|
||||
|
||||
val stackNavigation = StackNavigation<OnboardingNoteRoute>()
|
||||
|
||||
private val _uiState = MutableStateFlow(
|
||||
OnboardingNoteState(),
|
||||
)
|
||||
|
||||
val uiState: StateFlow<OnboardingNoteState> = _uiState
|
||||
|
||||
fun updateStepForNewRoute(route: OnboardingNoteRoute) {
|
||||
_innerNavigationState.value = innerNavigationState.value.copy(
|
||||
stackSize = route.stepNum(),
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
fun onChildBack(currentRoute: OnboardingNoteRoute) {
|
||||
// TODO [REDACTED_TASK_KEY]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.model
|
||||
|
||||
data class OnboardingNoteState(
|
||||
val cardArtworkUrl: String? = null,
|
||||
)
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.route
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
internal sealed class OnboardingNoteRoute {
|
||||
|
||||
@Serializable
|
||||
data object CreateWallet : OnboardingNoteRoute()
|
||||
|
||||
@Serializable
|
||||
data object TopUp : OnboardingNoteRoute()
|
||||
}
|
||||
|
||||
internal const val ONBOARDING_NOTE_STEPS_COUNT = 3
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.features.onboarding.v2.note.impl.route
|
||||
|
||||
internal fun OnboardingNoteRoute.stepNum() = when (this) {
|
||||
OnboardingNoteRoute.CreateWallet -> 1
|
||||
OnboardingNoteRoute.TopUp -> 2
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue