-add a password and login table
-disallow screenshots
This commit is contained in:
parent
8540a15e54
commit
aa78bfc14b
@ -4,7 +4,8 @@ object TestIds {
|
|||||||
object Auth {
|
object Auth {
|
||||||
const val ERROR = "auth_error"
|
const val ERROR = "auth_error"
|
||||||
const val SIGN_BUTTON = "auth_sign_button"
|
const val SIGN_BUTTON = "auth_sign_button"
|
||||||
const val CODE_INPUT = "auth_code_input"
|
const val LOGIN_INPUT = "auth_login_input"
|
||||||
|
const val PASSWORD_INPUT = "auth_password_input"
|
||||||
}
|
}
|
||||||
object Main {
|
object Main {
|
||||||
const val ERROR = "main_error"
|
const val ERROR = "main_error"
|
||||||
|
|||||||
@ -16,13 +16,13 @@ object AuthRepository {
|
|||||||
|
|
||||||
private var codeCache: String? = null
|
private var codeCache: String? = null
|
||||||
|
|
||||||
suspend fun checkAndSave(text: String): Result<Boolean> {
|
suspend fun checkAndSave(textLogin: String, textPassword: String): Result<Boolean> {
|
||||||
return NetworkDataSource.checkAuth(text).onSuccess { success ->
|
return NetworkDataSource.checkAuth(textLogin, textPassword).onSuccess { success ->
|
||||||
if (success) {
|
if (success) {
|
||||||
codeCache = text
|
codeCache = textLogin // TODO(переделать под отправку и логина и пароля в api)
|
||||||
App.context.userDataStore.edit { preferences ->
|
App.context.userDataStore.edit { preferences ->
|
||||||
val prefKey = stringPreferencesKey(CODE_KEY)
|
val prefKey = stringPreferencesKey(CODE_KEY)
|
||||||
preferences[prefKey] = text
|
preferences[prefKey] = textLogin // TODO(переделать под отправку и логина и пароля в api)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,9 +36,9 @@ object NetworkDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun checkAuth(code: String): Result<Boolean> = withContext(Dispatchers.IO) {
|
suspend fun checkAuth(login: String, password: String): Result<Boolean> = withContext(Dispatchers.IO) {
|
||||||
return@withContext runCatching {
|
return@withContext runCatching {
|
||||||
val response = client.get(getUrl(code, Constants.AUTH_URL))
|
val response = client.post(getUrl(login, Constants.AUTH_URL))
|
||||||
when (response.status) {
|
when (response.status) {
|
||||||
HttpStatusCode.OK -> true
|
HttpStatusCode.OK -> true
|
||||||
else -> false
|
else -> false
|
||||||
|
|||||||
@ -1,15 +0,0 @@
|
|||||||
package ru.myitschool.work.domain.auth
|
|
||||||
|
|
||||||
import ru.myitschool.work.data.repo.AuthRepository
|
|
||||||
|
|
||||||
class CheckAndSaveAuthCodeUseCase(
|
|
||||||
private val repository: AuthRepository
|
|
||||||
) {
|
|
||||||
suspend operator fun invoke(
|
|
||||||
text: String
|
|
||||||
): Result<Unit> {
|
|
||||||
return repository.checkAndSave(text).mapCatching { success ->
|
|
||||||
if (!success) error("Code is incorrect")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package ru.myitschool.work.domain.auth
|
||||||
|
|
||||||
|
import ru.myitschool.work.data.repo.AuthRepository
|
||||||
|
|
||||||
|
class CheckAndSaveAuthDataUseCase(
|
||||||
|
private val repository: AuthRepository
|
||||||
|
) {
|
||||||
|
suspend operator fun invoke(
|
||||||
|
textLogin: String,
|
||||||
|
textPassword: String
|
||||||
|
): Result<Unit> {
|
||||||
|
return repository.checkAndSave(textLogin, textPassword).mapCatching { success ->
|
||||||
|
if (!success) error("Login or password is incorrect")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,12 +0,0 @@
|
|||||||
package ru.myitschool.work.domain.auth
|
|
||||||
|
|
||||||
class CheckCodeFormatUseCase {
|
|
||||||
operator fun invoke(
|
|
||||||
text: String
|
|
||||||
): Boolean {
|
|
||||||
return text.length == 4 && text.all { char ->
|
|
||||||
char.isLetterOrDigit() &&
|
|
||||||
((char >= 'A' && char <= 'Z') || (char >= 'a' && char <= 'z') || char.isDigit())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
package ru.myitschool.work.domain.auth
|
||||||
|
|
||||||
|
class CheckLoginFormatUseCase {
|
||||||
|
operator fun invoke(
|
||||||
|
text: String
|
||||||
|
): Boolean {
|
||||||
|
return text.all { char ->
|
||||||
|
(char.isLetterOrDigit() &&
|
||||||
|
((char in ('A'..'Z')) || (char in ('a'..'z')) || char.isDigit()))
|
||||||
|
|| char == '.'
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package ru.myitschool.work.domain.auth
|
||||||
|
|
||||||
|
import java.util.Locale
|
||||||
|
import java.util.Locale.getDefault
|
||||||
|
|
||||||
|
class CheckPasswordFormatUseCase {
|
||||||
|
operator fun invoke(
|
||||||
|
textLogin: String,
|
||||||
|
textPassword: String
|
||||||
|
): Boolean {
|
||||||
|
val lowerCasePassword = textPassword.lowercase(getDefault())
|
||||||
|
val lowerCaseLogin = textLogin.lowercase(getDefault())
|
||||||
|
val intersect = lowerCasePassword.toList().intersect(lowerCaseLogin.toList());
|
||||||
|
return !(textPassword.length < 8 || !textPassword.all { char ->
|
||||||
|
char.isLetterOrDigit()
|
||||||
|
&& ((char in 'A'..'Z')
|
||||||
|
|| (char in 'a'..'z')
|
||||||
|
|| char.isDigit())
|
||||||
|
&& textPassword.count { it == char } < 3
|
||||||
|
&& intersect.size < 3
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
package ru.myitschool.work.ui.root
|
package ru.myitschool.work.ui.root
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.WindowManager
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
@ -15,6 +16,8 @@ class RootActivity : ComponentActivity() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
|
window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
WorkTheme {
|
WorkTheme {
|
||||||
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
package ru.myitschool.work.ui.screen.auth
|
package ru.myitschool.work.ui.screen.auth
|
||||||
|
|
||||||
sealed interface AuthIntent {
|
sealed interface AuthIntent {
|
||||||
data class Send(val text: String): AuthIntent
|
data class Send(val textLogin: String, val textPassword: String): AuthIntent
|
||||||
data class TextInput(val text: String): AuthIntent
|
data class TextInput(val textLogin: String, val textPassword: String): AuthIntent
|
||||||
}
|
}
|
||||||
@ -77,22 +77,39 @@ private fun Content(
|
|||||||
viewModel: AuthViewModel,
|
viewModel: AuthViewModel,
|
||||||
state: AuthState.Data
|
state: AuthState.Data
|
||||||
) {
|
) {
|
||||||
var inputText by rememberSaveable { mutableStateOf("") }
|
var inputLogin by rememberSaveable { mutableStateOf("") }
|
||||||
Spacer(modifier = Modifier.size(16.dp))
|
Spacer(modifier = Modifier.size(16.dp))
|
||||||
TextField(
|
TextField(
|
||||||
modifier = Modifier.testTag(TestIds.Auth.CODE_INPUT).fillMaxWidth(),
|
modifier = Modifier.testTag(TestIds.Auth.LOGIN_INPUT).fillMaxWidth(),
|
||||||
value = inputText,
|
value = inputLogin,
|
||||||
onValueChange = {
|
onValueChange = {
|
||||||
inputText = it
|
inputLogin = it
|
||||||
viewModel.onIntent(AuthIntent.TextInput(it))
|
viewModel.onIntent(AuthIntent.TextInput(
|
||||||
|
it,
|
||||||
|
textPassword = ""
|
||||||
|
))
|
||||||
},
|
},
|
||||||
label = { Text(stringResource(R.string.auth_label)) }
|
label = { Text(stringResource(R.string.auth_label_login)) }
|
||||||
|
)
|
||||||
|
var inputPassword by rememberSaveable { mutableStateOf("") }
|
||||||
|
Spacer(modifier = Modifier.size(16.dp))
|
||||||
|
TextField(
|
||||||
|
modifier = Modifier.testTag(TestIds.Auth.PASSWORD_INPUT).fillMaxWidth(),
|
||||||
|
value = inputPassword,
|
||||||
|
onValueChange = {
|
||||||
|
inputPassword = it
|
||||||
|
viewModel.onIntent(AuthIntent.TextInput(
|
||||||
|
inputLogin,
|
||||||
|
textPassword = it
|
||||||
|
))
|
||||||
|
},
|
||||||
|
label = { Text(stringResource(R.string.auth_label_password)) }
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.size(16.dp))
|
Spacer(modifier = Modifier.size(16.dp))
|
||||||
Button(
|
Button(
|
||||||
modifier = Modifier.testTag(TestIds.Auth.SIGN_BUTTON).fillMaxWidth(),
|
modifier = Modifier.testTag(TestIds.Auth.SIGN_BUTTON).fillMaxWidth(),
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.onIntent(AuthIntent.Send(inputText))
|
viewModel.onIntent(AuthIntent.Send(inputLogin, inputPassword))
|
||||||
},
|
},
|
||||||
enabled = state.isEnabledSend
|
enabled = state.isEnabledSend
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -10,13 +10,17 @@ import kotlinx.coroutines.flow.asStateFlow
|
|||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import ru.myitschool.work.data.repo.AuthRepository
|
import ru.myitschool.work.data.repo.AuthRepository
|
||||||
import ru.myitschool.work.domain.auth.CheckAndSaveAuthCodeUseCase
|
import ru.myitschool.work.domain.auth.CheckAndSaveAuthDataUseCase
|
||||||
import ru.myitschool.work.domain.auth.CheckCodeFormatUseCase
|
import ru.myitschool.work.domain.auth.CheckLoginFormatUseCase
|
||||||
|
import ru.myitschool.work.domain.auth.CheckPasswordFormatUseCase
|
||||||
import ru.myitschool.work.ui.nav.MainScreenDestination
|
import ru.myitschool.work.ui.nav.MainScreenDestination
|
||||||
|
|
||||||
class AuthViewModel : ViewModel() {
|
class AuthViewModel : ViewModel() {
|
||||||
private val checkCodeFormatUseCase by lazy { CheckCodeFormatUseCase() }
|
private val checkLoginFormatUseCase by lazy { CheckLoginFormatUseCase() }
|
||||||
private val checkAndSaveAuthCodeUseCase by lazy { CheckAndSaveAuthCodeUseCase(AuthRepository) }
|
private val checkAndSaveAuthDataUseCase by lazy { CheckAndSaveAuthDataUseCase(AuthRepository) }
|
||||||
|
private val checkPasswordFormatUseCase by lazy { CheckPasswordFormatUseCase() }
|
||||||
|
|
||||||
|
|
||||||
private val _uiState = MutableStateFlow<AuthState>(
|
private val _uiState = MutableStateFlow<AuthState>(
|
||||||
AuthState.Data(
|
AuthState.Data(
|
||||||
isEnabledSend = false,
|
isEnabledSend = false,
|
||||||
@ -32,7 +36,7 @@ class AuthViewModel : ViewModel() {
|
|||||||
when (intent) {
|
when (intent) {
|
||||||
is AuthIntent.Send -> {
|
is AuthIntent.Send -> {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
checkAndSaveAuthCodeUseCase.invoke(intent.text).fold(
|
checkAndSaveAuthDataUseCase.invoke(intent.textLogin, intent.textPassword).fold(
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
_actionFlow.emit(AuthAction.Open(MainScreenDestination))
|
_actionFlow.emit(AuthAction.Open(MainScreenDestination))
|
||||||
},
|
},
|
||||||
@ -46,10 +50,13 @@ class AuthViewModel : ViewModel() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is AuthIntent.TextInput -> {
|
is AuthIntent.TextInput -> {
|
||||||
updateStateIfData { oldState ->
|
updateStateIfData { oldState ->
|
||||||
oldState.copy(
|
oldState.copy(
|
||||||
isEnabledSend = checkCodeFormatUseCase.invoke(intent.text),
|
isEnabledSend = (checkLoginFormatUseCase.invoke(intent.textLogin) && checkPasswordFormatUseCase.invoke(
|
||||||
|
intent.textLogin, intent.textPassword
|
||||||
|
)),
|
||||||
error = null
|
error = null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,8 @@
|
|||||||
<string name="app_name">Work</string>
|
<string name="app_name">Work</string>
|
||||||
<string name="title_activity_root">Приложение</string>
|
<string name="title_activity_root">Приложение</string>
|
||||||
<string name="auth_title">Привет! Введи код для авторизации</string>
|
<string name="auth_title">Привет! Введи код для авторизации</string>
|
||||||
<string name="auth_label">Код</string>
|
<string name="auth_label_login">Логин</string>
|
||||||
|
<string name="auth_label_password">Пароль</string>
|
||||||
<string name="auth_sign_in">Войти</string>
|
<string name="auth_sign_in">Войти</string>
|
||||||
|
|
||||||
<string name="main_refresh">Обновить</string>
|
<string name="main_refresh">Обновить</string>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user