This commit is contained in:
imglmd
2025-12-01 16:53:39 +03:00
parent 0c5c97d2cd
commit fd8309521d
10 changed files with 139 additions and 55 deletions

View File

@@ -0,0 +1,14 @@
package ru.myitschool.work.data.dto
import kotlinx.serialization.Serializable
@Serializable
data class AvailableBookingResponse(
val dates: Map<String, List<AvailablePlace>> = emptyMap()
)
@Serializable
data class AvailablePlace(
val id: Int,
val place: String
)

View File

@@ -0,0 +1,9 @@
package ru.myitschool.work.data.dto
import kotlinx.serialization.Serializable
@Serializable
data class BookRequest(
val date: String,
val placeID: Int
)

View File

@@ -0,0 +1,16 @@
package ru.myitschool.work.data.dto
import kotlinx.serialization.Serializable
@Serializable
data class UserInfoResponse(
val name: String,
val photoUrl: String,
val booking: Map<String, BookingItem> = emptyMap()
)
@Serializable
data class BookingItem(
val id: Int,
val place: String
)

View File

@@ -1,16 +1,13 @@
package ru.myitschool.work.data.repo
import ru.myitschool.work.data.source.NetworkDataSource
import ru.myitschool.work.domain.MyResult
object AuthRepository {
private var codeCache: String? = null
suspend fun checkAndSave(text: String): Result<Boolean> {
return NetworkDataSource.checkAuth(text).onSuccess { success ->
if (success) {
codeCache = text
}
}
suspend fun checkAndSave(code: String): MyResult<Boolean> {
return NetworkDataSource.checkAuth(code)
}
}

View File

@@ -1,16 +1,18 @@
package ru.myitschool.work.data.source
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsText
import io.ktor.http.HttpStatusCode
import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
import ru.myitschool.work.core.Constants
import ru.myitschool.work.data.dto.UserInfoResponse
import ru.myitschool.work.domain.MyResult
object NetworkDataSource {
private val client by lazy {
@@ -28,15 +30,35 @@ object NetworkDataSource {
}
}
suspend fun checkAuth(code: String): Result<Boolean> = withContext(Dispatchers.IO) {
return@withContext runCatching {
suspend fun checkAuth(code: String): MyResult<Boolean> = withContext(Dispatchers.IO) {
try {
val response = client.get(getUrl(code, Constants.AUTH_URL))
when (response.status) {
HttpStatusCode.OK -> true
else -> error(response.bodyAsText())
HttpStatusCode.OK -> MyResult.Success(true)
else -> {
MyResult.Error("Неверный код или ошибка сервера")
}
}
} catch (e: Exception) {
MyResult.Error("Сетевая ошибка")
}
}
suspend fun getUserInfo(code: String): MyResult<UserInfoResponse> = withContext(Dispatchers.IO){
try {
val response = client.get(getUrl(code, Constants.INFO_URL))
when (response.status) {
HttpStatusCode.OK -> {
val userInfo: UserInfoResponse = response.body()
MyResult.Success(userInfo)
}
else -> {
MyResult.Error("Ошибка получения данных пользователя")
}
}
} catch (e: Exception){
MyResult.Error("Сетевая ошибка")
}
}
private fun getUrl(code: String, targetUrl: String) = "${Constants.HOST}/api/$code$targetUrl"
}

View File

@@ -0,0 +1,6 @@
package ru.myitschool.work.domain
sealed class MyResult<out T> {
data class Success<T>(val data: T): MyResult<T>()
data class Error(val error: String): MyResult<Nothing>()
}

View File

@@ -1,23 +1,25 @@
package ru.myitschool.work.domain.auth
import androidx.datastore.dataStore
import ru.myitschool.work.data.repo.AuthRepository
import ru.myitschool.work.domain.MyResult
import ru.myitschool.work.util.DataStoreManager
class CheckAndSaveAuthCodeUseCase(
private val repository: AuthRepository,
) {
suspend operator fun invoke(
code: String
): Result<Unit> = try {
val response = repository.checkAndSave(code)
if (response.isSuccess) {
suspend operator fun invoke(code: String): MyResult<Unit> {
return when (val repoResult = repository.checkAndSave(code)) {
is MyResult.Success -> {
if (repoResult.data) {
DataStoreManager.saveAuthCode(code)
Result.success(Unit)
MyResult.Success(Unit)
} else {
Result.failure(Exception("Неверный код"))
MyResult.Error("Неверный код")
}
}
is MyResult.Error -> {
MyResult.Error(repoResult.error)
}
}
} catch (e: Exception){
Result.failure(e)
}
}

View File

@@ -0,0 +1,4 @@
package ru.myitschool.work.domain.user
class GetUserUseCase {
}

View File

@@ -0,0 +1,6 @@
package ru.myitschool.work.domain.user
data class User(
val name: String,
val imageUrl: String
)

View File

@@ -8,6 +8,7 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import ru.myitschool.work.data.repo.AuthRepository
import ru.myitschool.work.domain.MyResult
import ru.myitschool.work.domain.auth.CheckAndSaveAuthCodeUseCase
class AuthViewModel : ViewModel() {
@@ -17,42 +18,49 @@ class AuthViewModel : ViewModel() {
fun onIntent(intent: AuthIntent) {
when (intent) {
is AuthIntent.Send -> {
viewModelScope.launch {
_uiState.update {
it.copy(isLoading = true)
AuthIntent.Send -> sendCode()
is AuthIntent.TextInput -> updateCode(intent.text)
}
}
checkAndSaveAuthCodeUseCase(_uiState.value.code).fold(
onSuccess = {
_uiState.update {
it.copy(
isLoading = false,
isAuthenticated = true
)
}
},
onFailure = { error ->
private fun updateCode(newCode: String) {
val trimmed = newCode.trim()
val isValid = trimmed.length == 4 && trimmed.matches(Regex("^[a-zA-Z0-9]{4}$"))
_uiState.update {
it.copy(
isLoading = false,
error = error.message ?: "Ошибка",
isAuthenticated = false
)
}
}
)
}
}
is AuthIntent.TextInput -> _uiState.update {
it.copy(
code = intent.text,
isButtonEnabled = intent.text.length == 4 &&
uiState.value.code.matches(Regex("^[a-zA-Z0-9]+$")),
code = trimmed,
isButtonEnabled = isValid,
error = null
)
}
}
private fun sendCode() {
val currentCode = _uiState.value.code
if (currentCode.length != 4 || !currentCode.matches(Regex("^[a-zA-Z0-9]{4}$"))) return
viewModelScope.launch {
_uiState.update { it.copy(isLoading = true, error = null) }
when (val result = checkAndSaveAuthCodeUseCase(currentCode)){
is MyResult.Success<*> -> {
_uiState.update {
it.copy(isLoading = false, isAuthenticated = true)
}
}
is MyResult.Error -> {
_uiState.update {
it.copy(
isLoading = false,
error = result.error,
isAuthenticated = false
)
}
}
}
}
}
}