Updated on 2026-08-14

This commit is contained in:
Tangem 2024-12-10 17:30:15 +03:00
parent 8255831701
commit 9613c37d29
10 changed files with 338 additions and 8 deletions

View file

@ -19,9 +19,9 @@ sealed class NavigationButtonsState {
data class NavigationButton(
val textReference: TextReference,
@DrawableRes val iconRes: Int? = null,
val isSecondary: Boolean,
val isIconVisible: Boolean,
val showProgress: Boolean,
val isEnabled: Boolean,
val isSecondary: Boolean = false,
val isIconVisible: Boolean = false,
val showProgress: Boolean = false,
val isEnabled: Boolean = true,
val onClick: () -> Unit,
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View file

@ -16,7 +16,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
@Suppress("UnusedPrivateMember")
class DefaultOnboardingVisaComponent @AssistedInject constructor(
internal class DefaultOnboardingVisaComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext,
@Assisted private val params: OnboardingVisaComponent.Params,
) : OnboardingVisaComponent, AppComponentContext by appComponentContext {

View file

@ -9,7 +9,7 @@ import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.features.onboarding.v2.visa.impl.child.accesscode.model.OnboardingVisaAccessCodeModel
import kotlinx.coroutines.flow.SharedFlow
class OnboardingVisaAccessCodeComponent(
internal class OnboardingVisaAccessCodeComponent(
appComponentContext: AppComponentContext,
private val params: Params,
) : ComposableContentComponent, AppComponentContext by appComponentContext {

View file

@ -10,7 +10,7 @@ import javax.inject.Inject
@Stable
@ComponentScoped
class OnboardingVisaAccessCodeModel @Inject constructor(
internal class OnboardingVisaAccessCodeModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val paramsContainer: ParamsContainer,
) : Model() {

View file

@ -0,0 +1,167 @@
package com.tangem.features.onboarding.v2.visa.impl.child.accesscode.ui
import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.common.ui.navigationButtons.NavigationButton
import com.tangem.common.ui.navigationButtons.NavigationPrimaryButton
import com.tangem.core.ui.components.OutlineTextFieldWithIcon
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.SpacerH16
import com.tangem.core.ui.components.SpacerH32
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemAnimations
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.visa.impl.child.accesscode.ui.state.OnboardingVisaAccessCodeUM
import kotlinx.coroutines.delay
@Composable
internal fun OnboardingVisaAccessCode(state: OnboardingVisaAccessCodeUM, modifier: Modifier = Modifier) {
Column(
modifier
.fillMaxSize()
.navigationBarsPadding(),
) {
AnimatedContent(
modifier = Modifier
.padding(top = 48.dp)
.weight(1f),
targetState = state.step,
transitionSpec = TangemAnimations.AnimatedContent
.slide { initial, target -> initial.ordinal < target.ordinal },
) { step ->
Content(
state = state,
reEnterAccessCodeState = step == OnboardingVisaAccessCodeUM.Step.ReEnter,
)
}
SpacerH(20.dp)
NavigationPrimaryButton(
modifier = Modifier
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
.imePadding(),
primaryButton = NavigationButton(
textReference = when (state.step) {
OnboardingVisaAccessCodeUM.Step.Enter -> resourceReference(R.string.common_continue)
OnboardingVisaAccessCodeUM.Step.ReEnter ->
resourceReference(R.string.onboarding_create_wallet_button_create_wallet)
},
onClick = state.onContinue,
iconRes = R.drawable.ic_tangem_24,
isIconVisible = state.step == OnboardingVisaAccessCodeUM.Step.ReEnter,
),
)
}
}
@Suppress("LongMethod")
@Composable
private fun Content(
state: OnboardingVisaAccessCodeUM,
reEnterAccessCodeState: Boolean,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
modifier = Modifier
.padding(horizontal = 32.dp),
text = if (reEnterAccessCodeState) {
"Re-enter your access code" // TODO
} else {
"Create access code" // TODO
},
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
)
SpacerH16()
// TODO
Text(
modifier = Modifier
.padding(horizontal = 32.dp),
text = "The access code will be used manage your payment account and protect it from unauthorized access",
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.secondary,
textAlign = TextAlign.Center,
)
SpacerH32()
val focusRequester = remember { FocusRequester() }
OutlineTextFieldWithIcon(
modifier = modifier
.padding(horizontal = 16.dp)
.focusRequester(focusRequester)
.fillMaxWidth(),
iconResId = if (state.accessCodeHidden) {
R.drawable.ic_eye_outline_24
} else {
R.drawable.ic_eye_off_outline_24
},
iconColor = TangemTheme.colors.icon.primary1,
onIconClick = state.onAccessCodeHideClick,
value = if (reEnterAccessCodeState) {
state.accessCodeSecond
} else {
state.accessCodeFirst
},
onValueChange = if (reEnterAccessCodeState) {
state.onAccessCodeSecondChange
} else {
state.onAccessCodeFirstChange
},
label = stringResource(id = R.string.onboarding_wallet_info_title_third),
isError = state.codesNotMatchError || state.atLeast4CharError,
visualTransformation = if (state.accessCodeHidden) {
PasswordVisualTransformation()
} else {
VisualTransformation.None
},
caption = when {
state.codesNotMatchError && reEnterAccessCodeState ->
stringResource(R.string.onboarding_access_codes_doesnt_match)
state.atLeast4CharError && !reEnterAccessCodeState ->
stringResource(R.string.onboarding_access_code_too_short)
else -> null
},
)
LaunchedEffect(Unit) {
delay(timeMillis = 200)
focusRequester.requestFocus()
}
}
}
@Preview(showBackground = true)
@Composable
private fun Preview() {
TangemThemePreview {
OnboardingVisaAccessCode(
state = OnboardingVisaAccessCodeUM(),
)
}
}

View file

@ -0,0 +1,20 @@
package com.tangem.features.onboarding.v2.visa.impl.child.accesscode.ui.state
import androidx.compose.ui.text.input.TextFieldValue
data class OnboardingVisaAccessCodeUM(
val step: Step = Step.Enter,
val accessCodeFirst: TextFieldValue = TextFieldValue(""),
val accessCodeSecond: TextFieldValue = TextFieldValue(""),
val codesNotMatchError: Boolean = false,
val atLeast4CharError: Boolean = false,
val accessCodeHidden: Boolean = true,
val onAccessCodeFirstChange: (TextFieldValue) -> Unit = {},
val onAccessCodeSecondChange: (TextFieldValue) -> Unit = {},
val onContinue: () -> Unit = {},
val onAccessCodeHideClick: () -> Unit = {},
) {
enum class Step {
Enter, ReEnter
}
}

View file

@ -6,8 +6,10 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.features.onboarding.v2.visa.impl.child.welcome.ui.OnboardingVisaWelcome
import com.tangem.features.onboarding.v2.visa.impl.child.welcome.ui.state.OnboardingVisaWelcomeUM
class OnboardingVisaWelcomeComponent(
internal class OnboardingVisaWelcomeComponent(
appComponentContext: AppComponentContext,
private val params: Params,
) : ComposableContentComponent, AppComponentContext by appComponentContext {
@ -15,9 +17,15 @@ class OnboardingVisaWelcomeComponent(
@Composable
override fun Content(modifier: Modifier) {
BackHandler(onBack = remember(this) { { params.onBack() } })
OnboardingVisaWelcome(
modifier = modifier,
state = OnboardingVisaWelcomeUM(),
)
}
data class Params(
val onBack: () -> Unit,
val startActivation: () -> Unit,
)
}

View file

@ -0,0 +1,124 @@
package com.tangem.features.onboarding.v2.visa.impl.child.welcome.ui
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.components.PrimaryButtonIconEnd
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.SpacerH12
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.visa.impl.child.welcome.ui.state.OnboardingVisaWelcomeUM
@Composable
internal fun OnboardingVisaWelcome(state: OnboardingVisaWelcomeUM, modifier: Modifier = Modifier) {
Column(
modifier = modifier
.fillMaxSize()
.navigationBarsPadding(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Column(
Modifier
.padding(
top = 20.dp,
bottom = 32.dp,
start = 32.dp,
end = 32.dp,
)
.fillMaxSize()
.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Artwork()
SpacerH(50.dp)
Text(
text = when (state.mode) {
OnboardingVisaWelcomeUM.Mode.Hello -> "Hello, ${state.userName}!"
OnboardingVisaWelcomeUM.Mode.WelcomeBack -> "Welcome back, ${state.userName}!"
},
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
)
SpacerH12()
Text(
text = when (state.mode) {
OnboardingVisaWelcomeUM.Mode.Hello -> "bla bla bla //TODO"
OnboardingVisaWelcomeUM.Mode.WelcomeBack -> "bla bla bla? //TODO"
},
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.secondary,
)
}
when (state.mode) {
OnboardingVisaWelcomeUM.Mode.Hello -> {
PrimaryButton(
modifier = Modifier
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
.fillMaxWidth(),
text = "Start Activation",
onClick = state.onContinueClick,
)
}
OnboardingVisaWelcomeUM.Mode.WelcomeBack -> {
PrimaryButtonIconEnd(
modifier = Modifier
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp)
.fillMaxWidth(),
text = "Start Activation",
iconResId = R.drawable.ic_tangem_24,
onClick = state.onContinueClick,
)
}
}
}
}
@Composable
fun Artwork(modifier: Modifier = Modifier) {
Box(modifier) {
val circleColor = TangemTheme.colors.background.secondary
Canvas(
modifier = Modifier
.align(Alignment.Center)
.size(254.dp)
.fillMaxSize(),
) {
drawCircle(
color = circleColor,
radius = size.width / 2,
center = center,
)
}
AsyncImage(
model = R.drawable.img_card_visa,
contentDescription = null,
modifier = Modifier
.align(Alignment.Center)
.size(128.dp), // Adjust the size as needed
)
}
}
@Preview(showBackground = true)
@Composable
private fun Preview() {
TangemThemePreview {
OnboardingVisaWelcome(state = OnboardingVisaWelcomeUM())
}
}

View file

@ -0,0 +1,11 @@
package com.tangem.features.onboarding.v2.visa.impl.child.welcome.ui.state
internal data class OnboardingVisaWelcomeUM(
val mode: Mode = Mode.Hello,
val userName: String = "",
val onContinueClick: () -> Unit = {},
) {
enum class Mode {
Hello, WelcomeBack
}
}