Auth first steps
This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
package ru.myitschool.work.core
|
||||||
|
|
||||||
|
import androidx.datastore.preferences.core.intPreferencesKey
|
||||||
|
|
||||||
|
// Не добавляйте ничего, что уже есть в Constants!
|
||||||
|
object OurConstants {
|
||||||
|
const val SHABLON = "^[a-zA-Z0-9]*\$"
|
||||||
|
const val DS_AUTH_KEY = "authkey"
|
||||||
|
}
|
||||||
@@ -1,15 +1,17 @@
|
|||||||
package ru.myitschool.work.data.repo
|
package ru.myitschool.work.data.repo
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import ru.myitschool.work.data.source.DataStoreDataSource.createAuthCode
|
||||||
import ru.myitschool.work.data.source.NetworkDataSource
|
import ru.myitschool.work.data.source.NetworkDataSource
|
||||||
|
|
||||||
object AuthRepository {
|
object AuthRepository {
|
||||||
|
|
||||||
private var codeCache: String? = null
|
private var codeCache: String? = null
|
||||||
|
// TODO: разобраться с контекстом
|
||||||
suspend fun checkAndSave(text: String): Result<Boolean> {
|
suspend fun checkAndSave(text: String): Result<Boolean> {
|
||||||
return NetworkDataSource.checkAuth(text).onSuccess { success ->
|
return NetworkDataSource.checkAuth(text).onSuccess { success ->
|
||||||
if (success) {
|
if (success) {
|
||||||
codeCache = text
|
codeCache = text
|
||||||
|
createAuthCode(context = appContext, code = text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package ru.myitschool.work.data.source
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.datastore.core.DataStore
|
||||||
|
import androidx.datastore.preferences.core.Preferences
|
||||||
|
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||||
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import ru.myitschool.work.core.OurConstants.DS_AUTH_KEY
|
||||||
|
|
||||||
|
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "auth")
|
||||||
|
val AUTH_KEY = stringPreferencesKey(DS_AUTH_KEY)
|
||||||
|
|
||||||
|
object DataStoreDataSource {
|
||||||
|
fun authFlow(context : Context): Flow<String> = context.dataStore.data.map { preferences ->
|
||||||
|
(preferences[AUTH_KEY] ?: 0).toString()
|
||||||
|
}
|
||||||
|
// TODO: разобраться с контекстом
|
||||||
|
suspend fun createAuthCode (context : Context, code : String) {
|
||||||
|
context.dataStore.updateData {
|
||||||
|
it.toMutablePreferences().also { preferences ->
|
||||||
|
preferences[AUTH_KEY] = code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,7 +30,7 @@ object NetworkDataSource {
|
|||||||
|
|
||||||
suspend fun checkAuth(code: String): Result<Boolean> = withContext(Dispatchers.IO) {
|
suspend fun checkAuth(code: String): Result<Boolean> = withContext(Dispatchers.IO) {
|
||||||
return@withContext runCatching {
|
return@withContext runCatching {
|
||||||
val response = client.get(getUrl(code, Constants.AUTH_URL))
|
val response = client.get(getUrl(code, Constants.AUTH_URL)) // TODO: Отпрвка запроса на сервер
|
||||||
when (response.status) {
|
when (response.status) {
|
||||||
HttpStatusCode.OK -> true
|
HttpStatusCode.OK -> true
|
||||||
else -> error(response.bodyAsText())
|
else -> error(response.bodyAsText())
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package ru.myitschool.work.ui.screen.auth
|
||||||
|
|
||||||
|
sealed interface AuthAction {
|
||||||
|
data class ShowError(val message: String) : AuthAction
|
||||||
|
|
||||||
|
}
|
||||||
@@ -29,6 +29,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import ru.myitschool.work.R
|
import ru.myitschool.work.R
|
||||||
|
import ru.myitschool.work.core.OurConstants.SHABLON
|
||||||
import ru.myitschool.work.core.TestIds
|
import ru.myitschool.work.core.TestIds
|
||||||
import ru.myitschool.work.ui.nav.MainScreenDestination
|
import ru.myitschool.work.ui.nav.MainScreenDestination
|
||||||
|
|
||||||
@@ -74,6 +75,7 @@ private fun Content(
|
|||||||
state: AuthState.Data
|
state: AuthState.Data
|
||||||
) {
|
) {
|
||||||
var inputText by remember { mutableStateOf("") }
|
var inputText by remember { mutableStateOf("") }
|
||||||
|
var errorText : String? by remember { mutableStateOf(null) }
|
||||||
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.CODE_INPUT).fillMaxWidth(),
|
||||||
@@ -88,12 +90,20 @@ private fun Content(
|
|||||||
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))
|
if (!inputText.isEmpty() || inputText.length == 4 || inputText.matches(Regex(SHABLON))){
|
||||||
|
viewModel.onIntent(AuthIntent.Send(inputText))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
enabled = true
|
enabled = true
|
||||||
|
|
||||||
) { Text(stringResource(R.string.auth_sign_in))
|
) { Text(stringResource(R.string.auth_sign_in)) }
|
||||||
|
ShowError(errorText) // TODO: раскидать в коде когда показывается ошибка и когда нет
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ShowError(text : String?){
|
||||||
|
if (text != null){
|
||||||
|
Text(text, modifier = Modifier.testTag(TestIds.Auth.ERROR))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,21 +18,23 @@ class AuthViewModel : ViewModel() {
|
|||||||
private val _uiState = MutableStateFlow<AuthState>(AuthState.Data)
|
private val _uiState = MutableStateFlow<AuthState>(AuthState.Data)
|
||||||
val uiState: StateFlow<AuthState> = _uiState.asStateFlow()
|
val uiState: StateFlow<AuthState> = _uiState.asStateFlow()
|
||||||
|
|
||||||
private val _actionFlow: MutableSharedFlow<Unit> = MutableSharedFlow()
|
private val _actionFlow: MutableSharedFlow<AuthAction> = MutableSharedFlow()
|
||||||
val actionFlow: SharedFlow<Unit> = _actionFlow
|
val actionFlow: SharedFlow<AuthAction> = _actionFlow
|
||||||
|
|
||||||
fun onIntent(intent: AuthIntent) {
|
fun onIntent(intent: AuthIntent) {
|
||||||
when (intent) {
|
when (intent) {
|
||||||
is AuthIntent.Send -> {
|
is AuthIntent.Send -> {
|
||||||
viewModelScope.launch(Dispatchers.Default) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
_uiState.update { AuthState.Loading }
|
_uiState.update { AuthState.Loading }
|
||||||
checkAndSaveAuthCodeUseCase.invoke("9999").fold(
|
checkAndSaveAuthCodeUseCase.invoke(intent.text).fold(
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
_actionFlow.emit(Unit)
|
// TODO: Поведение при успехе
|
||||||
},
|
},
|
||||||
onFailure = { error ->
|
onFailure = { error ->
|
||||||
error.printStackTrace()
|
error.printStackTrace()
|
||||||
_actionFlow.emit(Unit)
|
if (error.message != null) {
|
||||||
|
_actionFlow.emit(AuthAction.ShowError(error.message.toString()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user