improvements for previewing validation & captcha screens
This commit is contained in:
@@ -7,7 +7,6 @@ import android.content.Intent
|
|||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.PowerManager
|
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
@@ -121,10 +120,6 @@ object AndroidUtils {
|
|||||||
return intent
|
return intent
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isBatterySaverOn(context: Context): Boolean {
|
|
||||||
return (context.getSystemService(Context.POWER_SERVICE) as? PowerManager)?.isPowerSaveMode == true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getImageToShare(context: Context, existingFile: File): Uri? {
|
fun getImageToShare(context: Context, existingFile: File): Uri? {
|
||||||
val imageFolder = File(context.cacheDir, "images")
|
val imageFolder = File(context.cacheDir, "images")
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.compositionLocalOf
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
import androidx.compose.ui.graphics.luminance
|
import androidx.compose.ui.graphics.luminance
|
||||||
import com.meloda.app.fast.ui.util.isUsingDarkTheme
|
import com.meloda.app.fast.ui.theme.LocalIsDarkTheme
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default alpha levels used by Material components.
|
* Default alpha levels used by Material components.
|
||||||
@@ -79,7 +79,7 @@ object ContentAlpha {
|
|||||||
lowContrastAlpha: Float
|
lowContrastAlpha: Float
|
||||||
): Float {
|
): Float {
|
||||||
val contentColor = LocalContentColor.current
|
val contentColor = LocalContentColor.current
|
||||||
return if (!isUsingDarkTheme()) {
|
return if (!LocalIsDarkTheme.current) {
|
||||||
if (contentColor.luminance() > 0.5) highContrastAlpha else lowContrastAlpha
|
if (contentColor.luminance() > 0.5) highContrastAlpha else lowContrastAlpha
|
||||||
} else {
|
} else {
|
||||||
if (contentColor.luminance() < 0.5) highContrastAlpha else lowContrastAlpha
|
if (contentColor.luminance() < 0.5) highContrastAlpha else lowContrastAlpha
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import androidx.compose.material3.dynamicDarkColorScheme
|
|||||||
import androidx.compose.material3.dynamicLightColorScheme
|
import androidx.compose.material3.dynamicLightColorScheme
|
||||||
import androidx.compose.material3.lightColorScheme
|
import androidx.compose.material3.lightColorScheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.SideEffect
|
import androidx.compose.runtime.SideEffect
|
||||||
import androidx.compose.runtime.compositionLocalOf
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
@@ -115,6 +116,8 @@ val LocalTheme = compositionLocalOf {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val LocalIsDarkTheme = compositionLocalOf { false }
|
||||||
|
|
||||||
val LocalHazeState = compositionLocalOf {
|
val LocalHazeState = compositionLocalOf {
|
||||||
HazeState()
|
HazeState()
|
||||||
}
|
}
|
||||||
@@ -178,9 +181,11 @@ fun AppTheme(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialTheme(
|
CompositionLocalProvider(LocalIsDarkTheme provides useDarkTheme) {
|
||||||
colorScheme = predefinedColorScheme ?: colorScheme,
|
MaterialTheme(
|
||||||
typography = typography,
|
colorScheme = predefinedColorScheme ?: colorScheme,
|
||||||
content = content
|
typography = typography,
|
||||||
)
|
content = content
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.meloda.app.fast.ui.util
|
package com.meloda.app.fast.ui.util
|
||||||
|
|
||||||
import android.content.res.Configuration
|
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -11,34 +10,9 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.key.onKeyEvent
|
import androidx.compose.ui.input.key.onKeyEvent
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.res.pluralStringResource
|
import androidx.compose.ui.res.pluralStringResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import com.meloda.app.fast.common.UiText
|
import com.meloda.app.fast.common.UiText
|
||||||
import com.meloda.app.fast.common.util.AndroidUtils
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun isUsingDarkTheme(): Boolean {
|
|
||||||
// val nightThemeMode = AppCompatDelegate.MODE_NIGHT_YES
|
|
||||||
|
|
||||||
// SettingsController.getInt(
|
|
||||||
// SettingsKeys.KEY_APPEARANCE_DARK_THEME,
|
|
||||||
// SettingsKeys.DEFAULT_VALUE_APPEARANCE_DARK_THEME
|
|
||||||
// )
|
|
||||||
// val appForceDarkMode = nightThemeMode == AppCompatDelegate.MODE_NIGHT_YES
|
|
||||||
// val appBatterySaver = nightThemeMode == AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY
|
|
||||||
|
|
||||||
val context = LocalContext.current
|
|
||||||
|
|
||||||
val systemUiNightMode = context.resources.configuration.uiMode
|
|
||||||
|
|
||||||
val isSystemBatterySaver = AndroidUtils.isBatterySaverOn(context)
|
|
||||||
val isSystemUsingDarkTheme =
|
|
||||||
systemUiNightMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
|
|
||||||
|
|
||||||
return true
|
|
||||||
// return appForceDarkMode || (appBatterySaver && isSystemBatterySaver) || (!appBatterySaver && isSystemUsingDarkTheme && nightThemeMode == AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun UiText?.getString(): String? {
|
fun UiText?.getString(): String? {
|
||||||
|
|||||||
+11
@@ -43,6 +43,7 @@ import androidx.compose.ui.res.painterResource
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.input.ImeAction
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
@@ -251,3 +252,13 @@ fun CaptchaScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun CaptchaScreenPreview() {
|
||||||
|
CaptchaScreen(
|
||||||
|
screenState = CaptchaScreenState.EMPTY.copy(
|
||||||
|
code = "zcuecz"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -278,8 +278,7 @@ class LoginViewModelImpl(
|
|||||||
redirectUri = error.redirectUri,
|
redirectUri = error.redirectUri,
|
||||||
phoneMask = error.phoneMask,
|
phoneMask = error.phoneMask,
|
||||||
validationType = error.validationType.value,
|
validationType = error.validationType.value,
|
||||||
canResendSms = error.validationResend == "sms",
|
canResendSms = error.validationResend == "sms"
|
||||||
wrongCodeError = null
|
|
||||||
)
|
)
|
||||||
validationArguments.update { arguments }
|
validationArguments.update { arguments }
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -11,6 +11,5 @@ data class LoginValidationArguments(
|
|||||||
val redirectUri: String,
|
val redirectUri: String,
|
||||||
val phoneMask: String,
|
val phoneMask: String,
|
||||||
val validationType: String,
|
val validationType: String,
|
||||||
val canResendSms: Boolean,
|
val canResendSms: Boolean
|
||||||
val wrongCodeError: String?,
|
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|||||||
@@ -43,8 +43,7 @@ fun NavGraphBuilder.authNavGraph(
|
|||||||
redirectUri = URLEncoder.encode(arguments.redirectUri, "utf-8"),
|
redirectUri = URLEncoder.encode(arguments.redirectUri, "utf-8"),
|
||||||
phoneMask = arguments.phoneMask,
|
phoneMask = arguments.phoneMask,
|
||||||
validationType = arguments.validationType,
|
validationType = arguments.validationType,
|
||||||
canResendSms = arguments.canResendSms,
|
canResendSms = arguments.canResendSms
|
||||||
wrongCodeError = arguments.wrongCodeError
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
+19
-34
@@ -7,7 +7,6 @@ import com.meloda.app.fast.auth.validation.model.ValidationScreenState
|
|||||||
import com.meloda.app.fast.auth.validation.model.ValidationType
|
import com.meloda.app.fast.auth.validation.model.ValidationType
|
||||||
import com.meloda.app.fast.auth.validation.navigation.Validation
|
import com.meloda.app.fast.auth.validation.navigation.Validation
|
||||||
import com.meloda.app.fast.auth.validation.validation.ValidationValidator
|
import com.meloda.app.fast.auth.validation.validation.ValidationValidator
|
||||||
import com.meloda.app.fast.common.UiText
|
|
||||||
import com.meloda.app.fast.common.extensions.createTimerFlow
|
import com.meloda.app.fast.common.extensions.createTimerFlow
|
||||||
import com.meloda.app.fast.common.extensions.listenValue
|
import com.meloda.app.fast.common.extensions.listenValue
|
||||||
import com.meloda.app.fast.common.extensions.setValue
|
import com.meloda.app.fast.common.extensions.setValue
|
||||||
@@ -26,6 +25,8 @@ interface ValidationViewModel {
|
|||||||
|
|
||||||
val screenState: StateFlow<ValidationScreenState>
|
val screenState: StateFlow<ValidationScreenState>
|
||||||
|
|
||||||
|
val validationType: StateFlow<ValidationType?>
|
||||||
|
|
||||||
val isNeedToOpenLogin: StateFlow<Boolean>
|
val isNeedToOpenLogin: StateFlow<Boolean>
|
||||||
|
|
||||||
fun onCodeInputChanged(newCode: String)
|
fun onCodeInputChanged(newCode: String)
|
||||||
@@ -47,10 +48,11 @@ class ValidationViewModelImpl(
|
|||||||
|
|
||||||
override val screenState = MutableStateFlow(ValidationScreenState.EMPTY)
|
override val screenState = MutableStateFlow(ValidationScreenState.EMPTY)
|
||||||
|
|
||||||
|
override val validationType = MutableStateFlow<ValidationType?>(null)
|
||||||
|
|
||||||
override val isNeedToOpenLogin = MutableStateFlow(false)
|
override val isNeedToOpenLogin = MutableStateFlow(false)
|
||||||
|
|
||||||
private var validationSid: String? = null
|
private var validationSid: String? = null
|
||||||
|
|
||||||
private var delayJob: Job? = null
|
private var delayJob: Job? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -61,11 +63,13 @@ class ValidationViewModelImpl(
|
|||||||
|
|
||||||
validationSid = arguments.validationSid
|
validationSid = arguments.validationSid
|
||||||
|
|
||||||
|
validationType.setValue {
|
||||||
|
ValidationType.parse(arguments.validationType)
|
||||||
|
}
|
||||||
|
|
||||||
screenState.setValue { old ->
|
screenState.setValue { old ->
|
||||||
old.copy(
|
old.copy(
|
||||||
isSmsButtonVisible = arguments.canResendSms,
|
isSmsButtonVisible = arguments.canResendSms,
|
||||||
codeError = arguments.wrongCodeError,
|
|
||||||
validationText = getValidationText(ValidationType.parse(arguments.validationType)),
|
|
||||||
phoneMask = arguments.phoneMask
|
phoneMask = arguments.phoneMask
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -75,7 +79,7 @@ class ValidationViewModelImpl(
|
|||||||
screenState.updateValue(
|
screenState.updateValue(
|
||||||
screenState.value.copy(
|
screenState.value.copy(
|
||||||
code = newCode.trim(),
|
code = newCode.trim(),
|
||||||
codeError = null
|
codeError = false
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -118,34 +122,29 @@ class ValidationViewModelImpl(
|
|||||||
private fun processValidation(): Boolean {
|
private fun processValidation(): Boolean {
|
||||||
val isValid = validator.validate(screenState.value).isValid()
|
val isValid = validator.validate(screenState.value).isValid()
|
||||||
|
|
||||||
screenState.updateValue(
|
screenState.setValue { old -> old.copy(codeError = !isValid) }
|
||||||
screenState.value.copy(
|
|
||||||
codeError = if (isValid) null
|
|
||||||
else "Field must not be empty"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return isValid
|
return isValid
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sendValidationCode() {
|
private fun sendValidationCode() {
|
||||||
authUseCase.validatePhone(validationSid.orEmpty())
|
val sid = validationSid ?: return
|
||||||
|
|
||||||
|
authUseCase.validatePhone(sid)
|
||||||
.listenValue { state ->
|
.listenValue { state ->
|
||||||
state.processState(
|
state.processState(
|
||||||
error = { error ->
|
error = { error ->
|
||||||
|
|
||||||
},
|
},
|
||||||
success = { response ->
|
success = { response ->
|
||||||
val newValidationType = response.validationType
|
response.validationType?.let { newValidationType ->
|
||||||
|
validationType.setValue { ValidationType.parse(newValidationType) }
|
||||||
|
}
|
||||||
|
|
||||||
val newCanResendSms = response.validationResend == "sms"
|
val newCanResendSms = response.validationResend == "sms"
|
||||||
|
|
||||||
screenState.setValue { old ->
|
screenState.setValue { old ->
|
||||||
old.copy(
|
old.copy(isSmsButtonVisible = newCanResendSms)
|
||||||
isSmsButtonVisible = newCanResendSms,
|
|
||||||
validationText = getValidationText(
|
|
||||||
ValidationType.parse(newValidationType.orEmpty())
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
startTickTimer(response.delay)
|
startTickTimer(response.delay)
|
||||||
@@ -158,7 +157,7 @@ class ValidationViewModelImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startTickTimer(delay: Int?) {
|
private fun startTickTimer(delay: Int?) {
|
||||||
if (delay == null || delayJob?.isActive == true) return
|
if (delay == null || delayJob?.isActive == true) return
|
||||||
|
|
||||||
delayJob = createTimerFlow(
|
delayJob = createTimerFlow(
|
||||||
@@ -182,18 +181,4 @@ class ValidationViewModelImpl(
|
|||||||
},
|
},
|
||||||
).launchIn(viewModelScope)
|
).launchIn(viewModelScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getValidationText(validationType: ValidationType): UiText {
|
|
||||||
return when (validationType) {
|
|
||||||
ValidationType.Sms -> {
|
|
||||||
UiText.Simple("SMS with the code is sent to ${screenState.value.phoneMask}")
|
|
||||||
}
|
|
||||||
|
|
||||||
ValidationType.App -> {
|
|
||||||
UiText.Simple("Enter the code from the code generator application")
|
|
||||||
}
|
|
||||||
|
|
||||||
is ValidationType.Other -> UiText.Simple(validationType.type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -11,6 +11,5 @@ data class ValidationArguments(
|
|||||||
val redirectUri: String,
|
val redirectUri: String,
|
||||||
val phoneMask: String,
|
val phoneMask: String,
|
||||||
val validationType: String,
|
val validationType: String,
|
||||||
val canResendSms: Boolean,
|
val canResendSms: Boolean
|
||||||
val wrongCodeError: String?,
|
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|||||||
+4
-11
@@ -1,27 +1,20 @@
|
|||||||
package com.meloda.app.fast.auth.validation.model
|
package com.meloda.app.fast.auth.validation.model
|
||||||
|
|
||||||
import com.meloda.app.fast.common.UiText
|
|
||||||
|
|
||||||
data class ValidationScreenState(
|
data class ValidationScreenState(
|
||||||
val code: String?,
|
val code: String?,
|
||||||
val codeError: String?,
|
val codeError: Boolean,
|
||||||
val isSmsButtonVisible: Boolean,
|
val isSmsButtonVisible: Boolean,
|
||||||
val delayTime: Int,
|
val delayTime: Int,
|
||||||
val phoneMask: String,
|
val phoneMask: String
|
||||||
|
|
||||||
// TODO: 13/07/2024, Danil Nikolaev: check wtf is this
|
|
||||||
val validationText: UiText,
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val EMPTY = ValidationScreenState(
|
val EMPTY = ValidationScreenState(
|
||||||
code = null,
|
code = null,
|
||||||
codeError = null,
|
codeError = false,
|
||||||
isSmsButtonVisible = false,
|
isSmsButtonVisible = false,
|
||||||
delayTime = 0,
|
delayTime = 0,
|
||||||
phoneMask = "",
|
phoneMask = ""
|
||||||
|
|
||||||
validationText = UiText.Simple("")
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-17
@@ -1,23 +1,10 @@
|
|||||||
package com.meloda.app.fast.auth.validation.model
|
package com.meloda.app.fast.auth.validation.model
|
||||||
|
|
||||||
sealed class ValidationType(val value: String) {
|
enum class ValidationType(val value: String) {
|
||||||
|
SMS("sms"), APP("2fa_app");
|
||||||
data object Sms : ValidationType(TYPE_SMS)
|
|
||||||
|
|
||||||
data object App : ValidationType(TYPE_TWO_FA_APP)
|
|
||||||
|
|
||||||
data class Other(val type: String) : ValidationType(type)
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TYPE_SMS = "sms"
|
fun parse(value: String): ValidationType = entries.firstOrNull { it.value == value }
|
||||||
private const val TYPE_TWO_FA_APP = "2fa_app"
|
?: throw IllegalArgumentException("Unknown validation type with value: $value")
|
||||||
|
|
||||||
fun parse(validationType: String): ValidationType {
|
|
||||||
return when (validationType) {
|
|
||||||
TYPE_SMS -> Sms
|
|
||||||
TYPE_TWO_FA_APP -> App
|
|
||||||
else -> Other(validationType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-7
@@ -42,15 +42,16 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.text.input.ImeAction
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.meloda.app.fast.auth.validation.ValidationViewModel
|
import com.meloda.app.fast.auth.validation.ValidationViewModel
|
||||||
import com.meloda.app.fast.auth.validation.ValidationViewModelImpl
|
import com.meloda.app.fast.auth.validation.ValidationViewModelImpl
|
||||||
import com.meloda.app.fast.auth.validation.model.ValidationScreenState
|
import com.meloda.app.fast.auth.validation.model.ValidationScreenState
|
||||||
|
import com.meloda.app.fast.auth.validation.model.ValidationType
|
||||||
import com.meloda.app.fast.ui.components.ActionInvokeDismiss
|
import com.meloda.app.fast.ui.components.ActionInvokeDismiss
|
||||||
import com.meloda.app.fast.ui.components.MaterialDialog
|
import com.meloda.app.fast.ui.components.MaterialDialog
|
||||||
import com.meloda.app.fast.ui.components.TextFieldErrorText
|
import com.meloda.app.fast.ui.components.TextFieldErrorText
|
||||||
import com.meloda.app.fast.ui.util.getString
|
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
import com.meloda.app.fast.ui.R as UiR
|
import com.meloda.app.fast.ui.R as UiR
|
||||||
|
|
||||||
@@ -62,6 +63,7 @@ fun ValidationRoute(
|
|||||||
) {
|
) {
|
||||||
val screenState by viewModel.screenState.collectAsStateWithLifecycle()
|
val screenState by viewModel.screenState.collectAsStateWithLifecycle()
|
||||||
val isNeedToOpenLogin by viewModel.isNeedToOpenLogin.collectAsStateWithLifecycle()
|
val isNeedToOpenLogin by viewModel.isNeedToOpenLogin.collectAsStateWithLifecycle()
|
||||||
|
val validationType by viewModel.validationType.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
LaunchedEffect(isNeedToOpenLogin) {
|
LaunchedEffect(isNeedToOpenLogin) {
|
||||||
if (isNeedToOpenLogin) {
|
if (isNeedToOpenLogin) {
|
||||||
@@ -78,6 +80,7 @@ fun ValidationRoute(
|
|||||||
|
|
||||||
ValidationScreen(
|
ValidationScreen(
|
||||||
screenState = screenState,
|
screenState = screenState,
|
||||||
|
validationType = validationType,
|
||||||
onBack = onBack,
|
onBack = onBack,
|
||||||
onCodeInputChanged = viewModel::onCodeInputChanged,
|
onCodeInputChanged = viewModel::onCodeInputChanged,
|
||||||
onTextFieldDoneAction = viewModel::onTextFieldDoneAction,
|
onTextFieldDoneAction = viewModel::onTextFieldDoneAction,
|
||||||
@@ -89,6 +92,7 @@ fun ValidationRoute(
|
|||||||
@Composable
|
@Composable
|
||||||
fun ValidationScreen(
|
fun ValidationScreen(
|
||||||
screenState: ValidationScreenState = ValidationScreenState.EMPTY,
|
screenState: ValidationScreenState = ValidationScreenState.EMPTY,
|
||||||
|
validationType: ValidationType? = null,
|
||||||
onBack: () -> Unit = {},
|
onBack: () -> Unit = {},
|
||||||
onCodeInputChanged: (String) -> Unit = {},
|
onCodeInputChanged: (String) -> Unit = {},
|
||||||
onTextFieldDoneAction: () -> Unit = {},
|
onTextFieldDoneAction: () -> Unit = {},
|
||||||
@@ -105,6 +109,17 @@ fun ValidationScreen(
|
|||||||
mutableStateOf(false)
|
mutableStateOf(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val validationText by remember(validationType) {
|
||||||
|
mutableStateOf(
|
||||||
|
when (validationType) {
|
||||||
|
ValidationType.SMS -> "SMS with the code is sent to ${screenState.phoneMask}"
|
||||||
|
ValidationType.APP -> "Enter the code from the code generator application"
|
||||||
|
|
||||||
|
null -> ""
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(confirmedExit) {
|
LaunchedEffect(confirmedExit) {
|
||||||
if (confirmedExit) {
|
if (confirmedExit) {
|
||||||
onBack()
|
onBack()
|
||||||
@@ -130,7 +145,6 @@ fun ValidationScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
var code by remember { mutableStateOf(TextFieldValue(screenState.code.orEmpty())) }
|
var code by remember { mutableStateOf(TextFieldValue(screenState.code.orEmpty())) }
|
||||||
val codeError = screenState.codeError
|
|
||||||
|
|
||||||
Scaffold { padding ->
|
Scaffold { padding ->
|
||||||
Column(
|
Column(
|
||||||
@@ -167,7 +181,7 @@ fun ValidationScreen(
|
|||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(38.dp))
|
Spacer(modifier = Modifier.height(38.dp))
|
||||||
Text(
|
Text(
|
||||||
text = screenState.validationText.getString().orEmpty(),
|
text = validationText.orEmpty(),
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
color = MaterialTheme.colorScheme.onBackground
|
color = MaterialTheme.colorScheme.onBackground
|
||||||
)
|
)
|
||||||
@@ -201,7 +215,7 @@ fun ValidationScreen(
|
|||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(id = UiR.drawable.round_qr_code_24),
|
painter = painterResource(id = UiR.drawable.round_qr_code_24),
|
||||||
contentDescription = "QR Code icon",
|
contentDescription = "QR Code icon",
|
||||||
tint = if (codeError != null) {
|
tint = if (screenState.codeError) {
|
||||||
MaterialTheme.colorScheme.error
|
MaterialTheme.colorScheme.error
|
||||||
} else {
|
} else {
|
||||||
MaterialTheme.colorScheme.primary
|
MaterialTheme.colorScheme.primary
|
||||||
@@ -219,11 +233,11 @@ fun ValidationScreen(
|
|||||||
onTextFieldDoneAction()
|
onTextFieldDoneAction()
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
isError = codeError != null
|
isError = screenState.codeError
|
||||||
)
|
)
|
||||||
|
|
||||||
AnimatedVisibility(visible = codeError != null) {
|
AnimatedVisibility(screenState.codeError) {
|
||||||
TextFieldErrorText(text = codeError.orEmpty())
|
TextFieldErrorText(text = "Field must not be empty")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,3 +293,15 @@ fun ValidationScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun ValidationScreenPreview() {
|
||||||
|
ValidationScreen(
|
||||||
|
screenState = ValidationScreenState.EMPTY.copy(
|
||||||
|
phoneMask = "+7 (***) ***-**-21",
|
||||||
|
code = "222222"
|
||||||
|
),
|
||||||
|
validationType = ValidationType.SMS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user