checkpoint 0 #5

Open
student-e-klyukin wants to merge 15 commits from WindWin-org/NTO-2026-Android-TeamTask-Template:main into main
9 changed files with 115 additions and 24 deletions
Showing only changes of commit 311cab5b43 - Show all commits

View File

@ -1,10 +1,9 @@
package ru.myitschool.work.core
object Constants {
const val HOST = "http://10.0.0.14:49182" // "http://10.0.0.14:49182" or "http://10.0.2.2:8080"
const val HOST = "http://10.0.0.172:49182" // "http://10.0.0.14:49182" or "http://10.0.2.2:8080"
const val AUTH_URL = "/auth"
const val INFO_URL = "/info"
const val BOOKING_URL = "/booking"
const val BOOK_URL = "/book"
const val GET_TOKEN = "/getToken"
}

View File

@ -1,6 +1,7 @@
package ru.myitschool.work.data.repo
import android.content.Context
import android.util.Log
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
@ -25,6 +26,10 @@ object AuthRepository {
}
}
}
/**
* Из памяти
*/
suspend fun getToken(): String? {
if (tokenCache == null) {
tokenCache = App.context.userDataStore.data
@ -33,10 +38,15 @@ object AuthRepository {
preferences[stringPreferencesKey(TOKEN_KEY)]
}
}
Log.e("getTokenCache", tokenCache.toString())
return tokenCache
}
/**
* При обращении к серверу
*/
suspend fun getToken(login : String, password : String) : Result<String> {
Log.e("getTokenNDSInAR", NetworkDataSource.getToken(login, password).toString())
return NetworkDataSource.getToken(login, password)
}

View File

@ -42,22 +42,29 @@ object NetworkDataSource {
}
suspend fun getToken(login: String, password: String): Result<String> = withContext(Dispatchers.IO) {
return@withContext runCatching {
val response = client.post("${Constants.HOST}/api${Constants.GET_TOKEN}") {
val response = client.post("${Constants.HOST}/api${Constants.AUTH_URL}") {
contentType(ContentType.Application.Json)
setBody(
"""
Log.d("bodyInToken",
""" {
"login" : "$login",
"password" : "$password"
""".trimIndent()
} """.trimIndent()
)
setBody(
"""{
"login" : "${login}",
"password" : "${password}"
} """.trimIndent()
)
}
Log.e("getTokenInNDS", response.body())
if (response.status != HttpStatusCode.OK) {
error(response.status)
Log.e("getToken", response.status.toString())
throw Exception("Неизвестная ошибка ${response.status}")
}
else if (response.status == HttpStatusCode.Unauthorized) {
error("Неверный логин или пароль")
throw Exception("Неверный логин или пароль")
}
Log.e("getToken", response.status.toString())
response.body<String>()
}
}

View File

@ -1,11 +1,13 @@
package ru.myitschool.work.domain.auth
import android.util.Log
import ru.myitschool.work.data.repo.AuthRepository
class GetTokenNetworkUseCase(
private val repository: AuthRepository
) {
suspend operator fun invoke(login : String, password: String): Result<String> {
Log.e("GetTokenNetworkUseCase", repository.getToken(login, password).toString())
return repository.getToken(login, password)
}
}

View File

@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SecureTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
@ -25,10 +26,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import io.ktor.http.HttpStatusCode
import ru.myitschool.work.R
import ru.myitschool.work.core.TestIds
import ru.myitschool.work.ui.custom.component.CustomButon
@ -71,10 +74,10 @@ fun AuthScreen(
}
}
}
@Composable
fun SecureScreen(enabled: Boolean = true) {
}
//@Composable
//fun SecureScreen(enabled: Boolean = true) {
//
//}
@Composable
private fun Content(
viewModel: AuthViewModel,
@ -112,14 +115,30 @@ private fun Content(
) {
Text(stringResource(R.string.auth_sign_in))
}
if (state.error != "Connection refused" && state.error != null) {
// if (state.error != null) {
// Text(
// modifier = Modifier.testTag(TestIds.Auth.ERROR),
// text = state.error,
// style = MaterialTheme.typography.bodyMedium,
// color = Color.Red,
// )
// }
if (state.error != null && !state.error.contains("401") && state.error.contains("Network")) {
Text(
modifier = Modifier.testTag(TestIds.Auth.ERROR),
text = "Неизвестная ошибка: ${state.error}",
text = state.error,
style = MaterialTheme.typography.bodyMedium,
color = Color.Red,
)
} else if (state.error == "Connection refused") {
}
if (state.error.toString().contains("401")) {
Text(
modifier = Modifier.testTag(TestIds.Auth.ERROR),
text = "Неверный логин или пароль",
style = MaterialTheme.typography.bodyMedium,
color = Color.Red,
)
} else if (state.error.toString().contains("Network")) {
Text(
modifier = Modifier.testTag(TestIds.Auth.ERROR),
text = "Отсутствует интернет-соединение",

View File

@ -30,10 +30,10 @@ class AuthViewModel : ViewModel() {
private val _actionFlow: MutableSharedFlow<AuthAction> = MutableSharedFlow()
val actionFlow: SharedFlow<AuthAction> = _actionFlow
fun onIntent(intent: AuthIntent) {
when (intent) {
is AuthIntent.Send -> {
Log.e("onIntent", intent.login)
viewModelScope.launch {
getTokenNetworkUseCase.invoke(intent.login, intent.password).fold(
onSuccess = {

View File

@ -1,4 +1,4 @@
package ru.myitschool.work.ui.screen.main
package ru.myitschool.work.ui.screen.meetings
object MeetingResult {
const val REFRESH_STATUS = "refresh_status"

View File

@ -1,4 +1,10 @@
package ru.myitschool.work.ui.screen.meetings
class MeetingsAction {
import ru.myitschool.work.ui.nav.AppDestination
sealed interface MeetingsAction {
class Open(
val destination: AppDestination,
val clearBackStack: Boolean = false
): MeetingsAction
}

View File

@ -1,18 +1,66 @@
package ru.myitschool.work.ui.screen.meetings
import android.provider.Settings.System.DATE_FORMAT
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
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.data.repo.BookRepository
import ru.myitschool.work.domain.auth.LogoutUseCase
import ru.myitschool.work.domain.main.GetMainDataUseCase
import ru.myitschool.work.ui.nav.AuthScreenDestination
import ru.myitschool.work.ui.screen.main.MainAction
import ru.myitschool.work.ui.screen.main.MainState
import java.time.LocalDate
import java.time.format.DateTimeFormatter
class MeetingsViewModel : ViewModel() {
class MeetingsViewModel: ViewModel() {
private val _uiState = MutableStateFlow<MeetingsState>(MeetingsState.Loading)
val uiState: StateFlow<MeetingsState> = _uiState.asStateFlow()
private val _actionFlow: MutableSharedFlow<MainAction> = MutableSharedFlow()
val actionFlow: SharedFlow<MainAction> = _actionFlow
private val logoutUseCase by lazy {
LogoutUseCase(AuthRepository)
}
private val getMeetingsDataUseCase by lazy {
GetMainDataUseCase(BookRepository(AuthRepository))
}
init {
private val _actionFlow: MutableSharedFlow<MeetingsAction> = MutableSharedFlow()
val actionFlow: SharedFlow<MeetingsAction> = _actionFlow
}
// fun onIntent(intent: MeetingIntent) {
// when(intent) {
// is MeetingIntent.Logout -> {
// viewModelScope.launch {
// logoutUseCase.invoke()
// _actionFlow.emit(/*TODo*/)
// }
// }
// is MeetingIntent.Refresh -> {
// /*TODO("обновляться автоматически с заданной периодичностью")*/
// }
// }
// }
fun refresh() {
viewModelScope.launch {
_uiState.update { MeetingsState.Loading }
_uiState.update {
getMeetingsDataUseCase.invoke().fold(
onFailure = { error ->
MeetingsState.Error(
error = error.message?.takeIf { it.isNotBlank() } ?: "Unknown error"
)
},
onSuccess = TODO()
)
}
}
}
}