Compare commits

7 Commits

Author SHA1 Message Date
2d9682ebba merge upstream 2025-12-05 18:33:07 +00:00
655baf713f I made the logic for moving between screens and added stubs 2025-12-05 21:32:25 +03:00
32cdc2b3a6 Merge pull request 'add booking to main branch' (#4) from student-15047/NTO-2025-Android-TeamTask:main into main
Reviewed-on: student-18211/NTO-2025-Android-TeamTask#4
2025-12-05 17:24:18 +00:00
f632ddbe2b Merge remote-tracking branch 'origin/main'
# Conflicts:
#	app/src/main/java/ru/myitschool/work/ui/screen/NavigationGraph.kt
#	app/src/main/java/ru/myitschool/work/ui/screen/book/BookIntent.kt
#	app/src/main/java/ru/myitschool/work/ui/screen/book/BookScreen.kt
#	app/src/main/java/ru/myitschool/work/ui/screen/book/BookState.kt
#	app/src/main/java/ru/myitschool/work/ui/screen/book/BookViewModel.kt
#	app/src/main/res/values/strings.xml
2025-12-05 18:31:20 +03:00
f5ff5dbca8 I merge fork 2025-12-05 18:28:06 +03:00
47e9018b67 revert abc0f81356
revert Merge pull request 'Added appearance and basic booking functionality' (#1) from student-15047/NTO-2025-Android-TeamTask:to-book into main

Reviewed-on: student-18211/NTO-2025-Android-TeamTask#1
2025-11-30 16:05:59 +00:00
abc0f81356 Merge pull request 'Added appearance and basic booking functionality' (#1) from student-15047/NTO-2025-Android-TeamTask:to-book into main
Reviewed-on: student-18211/NTO-2025-Android-TeamTask#1
2025-11-30 16:04:45 +00:00
6 changed files with 53 additions and 24 deletions

View File

@@ -1,11 +1,21 @@
package ru.myitschool.work.data.repo package ru.myitschool.work.data.repo
import android.content.Context
import ru.myitschool.work.App
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
private const val PREF_NAME = "auth_prefs"
private const val KEY_SAVED_CODE = "saved_code"
private val context: Context get() = App.context
private fun loadSavedCode(): String? {
// return context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
// .getString(KEY_SAVED_CODE, null)
return ""
}
fun getSavedCode(): String? = loadSavedCode()
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) {

View File

@@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button import androidx.compose.material3.Button
@@ -21,7 +22,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
@@ -74,6 +74,11 @@ private fun Content(
state: AuthState.Data state: AuthState.Data
) { ) {
var inputText by remember { mutableStateOf("") } var inputText by remember { mutableStateOf("") }
val isValidCode = inputText.length >= 4 && inputText.isNotEmpty() && inputText.none { it.isWhitespace() } && inputText.all { ch ->
ch in '0'..'9' || ch in 'A'..'Z' || ch in 'a'..'z'
}
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(),
@@ -82,15 +87,23 @@ private fun Content(
inputText = it inputText = it
viewModel.onIntent(AuthIntent.TextInput(it)) viewModel.onIntent(AuthIntent.TextInput(it))
}, },
label = { Text(stringResource(R.string.auth_label)) } label = { Text(stringResource(R.string.auth_label)) },
) )
if (state.error != null) {
Text(
text = state.error,
color = MaterialTheme.colorScheme.error,
modifier = Modifier
.testTag(TestIds.Auth.ERROR)
)
}
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(inputText))
}, },
enabled = true enabled = isValidCode
) { ) {
Text(stringResource(R.string.auth_sign_in)) Text(stringResource(R.string.auth_sign_in))
} }

View File

@@ -2,5 +2,7 @@ package ru.myitschool.work.ui.screen.auth
sealed interface AuthState { sealed interface AuthState {
object Loading: AuthState object Loading: AuthState
object Data: AuthState data class Data(
val error: String? = null
) : AuthState
} }

View File

@@ -15,7 +15,7 @@ import ru.myitschool.work.domain.auth.CheckAndSaveAuthCodeUseCase
class AuthViewModel : ViewModel() { class AuthViewModel : ViewModel() {
private val checkAndSaveAuthCodeUseCase by lazy { CheckAndSaveAuthCodeUseCase(AuthRepository) } private val checkAndSaveAuthCodeUseCase by lazy { CheckAndSaveAuthCodeUseCase(AuthRepository) }
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<Unit> = MutableSharedFlow()
@@ -26,18 +26,21 @@ class AuthViewModel : ViewModel() {
is AuthIntent.Send -> { is AuthIntent.Send -> {
viewModelScope.launch(Dispatchers.Default) { viewModelScope.launch(Dispatchers.Default) {
_uiState.update { AuthState.Loading } _uiState.update { AuthState.Loading }
checkAndSaveAuthCodeUseCase.invoke("9999").fold( checkAndSaveAuthCodeUseCase.invoke(intent.text).fold(
onSuccess = { onSuccess = {
_actionFlow.emit(Unit) _actionFlow.emit(Unit)// переход на MainScreen
}, },
onFailure = { error -> onFailure = { error ->
error.printStackTrace() _uiState.update {
_actionFlow.emit(Unit) AuthState.Data(error.message ?: "Неверный код для авторизации")
}
} }
) )
} }
} }
is AuthIntent.TextInput -> Unit is AuthIntent.TextInput -> {
_uiState.update { AuthState.Data() }
}
} }
} }
} }

View File

@@ -49,10 +49,9 @@ fun BookScreen(
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
Log.d("BookScreen", "1") Log.d("BookScreen", "1")
viewModel.navigationFlow.collect { viewModel.navigationFlow.collect { event ->
// TODO настроить наконец это переход между экранами Log.d("navigation", "Event received: $event")
// Log.d("BookScreen", "2") when (event) {
when (it) {
BookNavigationEvent.NavigateToMain -> { BookNavigationEvent.NavigateToMain -> {
Log.d("BookScreen", "3") Log.d("BookScreen", "3")
navController.navigate(MainScreenDestination) navController.navigate(MainScreenDestination)

View File

@@ -8,6 +8,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -18,21 +19,18 @@ import ru.myitschool.work.ui.screen.Booking
class BookViewModel : ViewModel() { class BookViewModel : ViewModel() {
private val _uiState = MutableStateFlow<BookState>(BookState.Loading) private val _uiState = MutableStateFlow<BookState>(BookState.Loading)
val uiState: StateFlow<BookState> = _uiState.asStateFlow(); val uiState: StateFlow<BookState> = _uiState.asStateFlow();
private val _actionFlow: MutableSharedFlow<Unit> = MutableSharedFlow()
val actionFlow: SharedFlow<Unit> = _actionFlow
private val _navigationFlow = MutableSharedFlow<BookNavigationEvent>() private val _navigationFlow = MutableSharedFlow<BookNavigationEvent>()
val navigationFlow: SharedFlow<BookNavigationEvent> = _navigationFlow val navigationFlow: SharedFlow<BookNavigationEvent> = _navigationFlow.asSharedFlow()
init { init {
loadData() loadData()
} }
private fun loadData() { private fun loadData() {
_uiState.update { BookState.Loading }
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
_uiState.update { BookState.Loading }
val code = AuthRepository.getSavedCode() ?: run { val code = AuthRepository.getSavedCode() ?: run {
onIntent(BookIntent.ToAuthScreen) _navigationFlow.emit(BookNavigationEvent.NavigateToAuth)
Log.d("", "Go to AuthScreen") Log.d("", "Go to AuthScreen")
return@launch return@launch
} }
@@ -90,7 +88,7 @@ class BookViewModel : ViewModel() {
_uiState.update { BookState.Loading } _uiState.update { BookState.Loading }
BookRepository.sendData(intent.code, intent.booking).fold( BookRepository.sendData(intent.code, intent.booking).fold(
onSuccess = { onSuccess = {
_actionFlow.emit(Unit) _navigationFlow.tryEmit(BookNavigationEvent.NavigateToMain)
}, },
onFailure = { error -> onFailure = { error ->
BookState.Error(error.message ?: "Неизвестная ошибка") BookState.Error(error.message ?: "Неизвестная ошибка")
@@ -99,11 +97,15 @@ class BookViewModel : ViewModel() {
} }
} }
BookIntent.BackToMainScreen -> { BookIntent.BackToMainScreen -> {
_navigationFlow.tryEmit(BookNavigationEvent.NavigateToMain) viewModelScope.launch {
_navigationFlow.emit(BookNavigationEvent.NavigateToMain)
}
} }
BookIntent.LoadData -> loadData() BookIntent.LoadData -> loadData()
BookIntent.ToAuthScreen -> { BookIntent.ToAuthScreen -> {
_navigationFlow.tryEmit(BookNavigationEvent.NavigateToAuth) viewModelScope.launch {
_navigationFlow.emit(BookNavigationEvent.NavigateToAuth)
}
} }
} }
} }