Auth first steps

This commit is contained in:
2025-11-24 18:33:16 +03:00
parent be88863cb2
commit bcbca3a10f
7 changed files with 68 additions and 12 deletions

View File

@@ -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"
}

View File

@@ -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)
} }
} }
} }

View File

@@ -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
}
}
}
}

View File

@@ -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())

View File

@@ -0,0 +1,6 @@
package ru.myitschool.work.ui.screen.auth
sealed interface AuthAction {
data class ShowError(val message: String) : AuthAction
}

View File

@@ -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 = {
if (!inputText.isEmpty() || inputText.length == 4 || inputText.matches(Regex(SHABLON))){
viewModel.onIntent(AuthIntent.Send(inputText)) 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))
}
} }

View File

@@ -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()))
}
} }
) )
} }