Updated on 2026-08-14
This commit is contained in:
parent
8255831701
commit
9613c37d29
10 changed files with 338 additions and 8 deletions
|
|
@ -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,
|
||||
)
|
||||
BIN
core/ui/src/main/res/drawable/img_card_visa.webp
Normal file
BIN
core/ui/src/main/res/drawable/img_card_visa.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue