From f7a2ee8720ae71ff42ae85a983b5e6027cbdd880 Mon Sep 17 00:00:00 2001 From: student Date: Thu, 11 Dec 2025 18:31:23 +0300 Subject: [PATCH] Finish Commit --- .../work/ui/screen/NavigationGraph.kt | 72 +-------- .../work/ui/screen/book/BookIntent.kt | 3 + .../work/ui/screen/book/BookScreen.kt | 99 +++++++++--- .../work/ui/screen/book/BookState.kt | 8 + .../work/ui/screen/book/BookViewModel.kt | 43 +++++ .../work/ui/screen/book/MainState.kt | 4 + .../work/ui/screen/main/MainIntent.kt | 7 + .../work/ui/screen/main/MainScreen.kt | 153 ++++++++++++++++++ .../work/ui/screen/main/MainState.kt | 8 + .../work/ui/screen/main/MainViewModel.kt | 43 +++++ 10 files changed, 351 insertions(+), 89 deletions(-) create mode 100644 app/src/main/java/ru/myitschool/work/ui/screen/book/BookState.kt create mode 100644 app/src/main/java/ru/myitschool/work/ui/screen/book/BookViewModel.kt create mode 100644 app/src/main/java/ru/myitschool/work/ui/screen/book/MainState.kt create mode 100644 app/src/main/java/ru/myitschool/work/ui/screen/main/MainIntent.kt create mode 100644 app/src/main/java/ru/myitschool/work/ui/screen/main/MainScreen.kt create mode 100644 app/src/main/java/ru/myitschool/work/ui/screen/main/MainState.kt create mode 100644 app/src/main/java/ru/myitschool/work/ui/screen/main/MainViewModel.kt diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/NavigationGraph.kt b/app/src/main/java/ru/myitschool/work/ui/screen/NavigationGraph.kt index 397dd6f..6d209f4 100644 --- a/app/src/main/java/ru/myitschool/work/ui/screen/NavigationGraph.kt +++ b/app/src/main/java/ru/myitschool/work/ui/screen/NavigationGraph.kt @@ -46,6 +46,8 @@ import ru.myitschool.work.ui.nav.MainScreenDestination import ru.myitschool.work.ui.screen.auth.AuthIntent import ru.myitschool.work.ui.screen.auth.AuthScreen import ru.myitschool.work.ui.screen.auth.AuthViewModel +import ru.myitschool.work.ui.screen.book.BookScreen +import ru.myitschool.work.ui.screen.main.MainScreen @Composable @@ -55,11 +57,6 @@ fun AppNavHost( navController: NavHostController = rememberNavController(), ) { - var expanded by remember{ mutableStateOf(false)} - var inputUsername by remember { mutableStateOf("Введите имя") } - val date:String = "" - val username:String = "" - var place:String = "" NavHost( modifier = modifier, @@ -72,71 +69,10 @@ fun AppNavHost( AuthScreen(navController = navController) } composable { - Box( - contentAlignment = Alignment.Center - ) { - Text(text = "") - - } - Column() { - Spacer(modifier = Modifier.size(16.dp)) - Text( - modifier = Modifier.testTag("main_name").fillMaxWidth(), - text = username - ) - Spacer(modifier = Modifier.size(10.dp)) - - Spacer(modifier = Modifier.size(10.dp)) - Row (horizontalArrangement = Arrangement.SpaceBetween, - modifier = Modifier.fillMaxWidth().padding(8.dp)) { - - Button(onClick = { }) { - Text(text = "Выход") - Modifier - .padding(20.dp) - .fillMaxWidth() - .semantics { testTag = "main_logout_button" } - } - Spacer(modifier = Modifier.size(10.dp)) - Button(onClick = { }) { - Text(text = "Обновить") - Modifier - .padding(20.dp) - .fillMaxWidth() - .semantics { testTag = "main_refresh_button" } - } - Spacer(modifier = Modifier.size(10.dp)) - } - Spacer(modifier = Modifier.size(10.dp)) - - Button(onClick = { expanded = true }) { - Text(text = "бронировать") - Modifier - .padding(20.dp) - .fillMaxWidth() - .semantics { testTag = "main_add_button" } - } - Text( - modifier = Modifier.testTag("main_item_date").fillMaxWidth(), - text = date - ) - Spacer(modifier = Modifier.size(10.dp)) - Text( - modifier = Modifier.testTag("main_item_place").fillMaxWidth(), - text = place - ) - Text(text = "ERROR", - modifier = Modifier.testTag("main_error").fillMaxWidth().alpha(0f)) - - } - + MainScreen(navController = navController) } composable { - Box( - contentAlignment = Alignment.Center - ) { - Text(text = "Hello") - } + BookScreen(navController = navController) } } } diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookIntent.kt b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookIntent.kt index 8ca3569..f5e1590 100644 --- a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookIntent.kt +++ b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookIntent.kt @@ -1,4 +1,7 @@ package ru.myitschool.work.ui.screen.book + interface BookIntent { + data class Send(val text: String): BookIntent + data class TextInput(val text: String): BookIntent } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookScreen.kt b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookScreen.kt index d575ec9..b6de44e 100644 --- a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookScreen.kt +++ b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookScreen.kt @@ -2,12 +2,16 @@ package ru.myitschool.work.ui.screen.book import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.selection.selectable +import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -16,23 +20,22 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.testTag import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController -import ru.myitschool.work.R import ru.myitschool.work.ui.nav.MainScreenDestination -import ru.myitschool.work.ui.screen.auth.AuthState -import ru.myitschool.work.ui.screen.auth.AuthViewModel @Composable fun BookScreen( - viewModel: AuthViewModel = viewModel(), + viewModel: BookViewModel = viewModel(), navController: NavController ) { + val state by viewModel.uiState.collectAsState() LaunchedEffect(Unit) { @@ -45,17 +48,10 @@ fun BookScreen( modifier = Modifier .fillMaxSize() .padding(all = 24.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center ) { - Text( - text = stringResource(R.string.auth_title), - style = MaterialTheme.typography.headlineSmall, - textAlign = TextAlign.Center - ) when (val currentState = state) { - is AuthState.Data -> Content(viewModel, currentState) - is AuthState.Loading -> { + is BookState.Data -> Content(viewModel, currentState) + is BookState.Loading -> { CircularProgressIndicator( modifier = Modifier.size(64.dp) ) @@ -66,10 +62,71 @@ fun BookScreen( @Composable private fun Content( - viewModel: AuthViewModel, - state: AuthState.Data + viewModel: BookViewModel, + state: BookState.Data ) { - var inputText by remember { mutableStateOf("") } - Spacer(modifier = Modifier.size(16.dp)) + val error = false + var selected = false + var input by remember { mutableStateOf("") } + Column() { Row (horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth().padding(8.dp)) { + OutlinedButton(onClick = { }) { + Text("19.04", Modifier.testTag("main_item_place")) + Modifier.semantics { testTag = "main_add_button"}.selectable(selected = selected, onClick = {selected = true }, role = Role.Button) + } + OutlinedButton(onClick = { }) { + Text("20.04", Modifier.testTag("main_item_place")) + Modifier.semantics { testTag = "main_add_button"}.selectable(selected = selected, onClick = {selected = true }, role = Role.Button) + } + OutlinedButton(onClick = { }) { + Text("21.04", Modifier.testTag("main_item_place")) + Modifier.semantics { testTag = "main_add_button"}.selectable(selected = selected, onClick = {selected = true }, role = Role.Button) + } + OutlinedButton(onClick = { }) { + Text("22.04", Modifier.testTag("main_item_place")) + Modifier.semantics { testTag = "main_add_button" }.selectable(selected = selected, onClick = {selected = true }, role = Role.Button) + } + } + Spacer(modifier = Modifier.size(20.dp)) + Text("Рабочее место у окна") + Spacer(modifier = Modifier.size(20.dp)) + Text("Переговорная комната №1") + Spacer(modifier = Modifier.size(20.dp)) + Text("Коворкинг A", Modifier.selectable(selected = selected, onClick = {selected = true}, role = Role.Button)) + Spacer(modifier = Modifier.size(20.dp)) + Row (horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth().padding(8.dp)){ + Button(onClick = { viewModel.onIntent(BookIntent.Send(input)) }) { + Text(text = "Назад") + Modifier + .padding(20.dp) + .fillMaxWidth() + .semantics { testTag = "book_back_button" } + } + Button(onClick = { }) { + Text(text = "Бронировать") + Modifier + .padding(20.dp) + .fillMaxWidth() + .semantics { testTag = "book_book_button" } + } + + } + if (error){ + Spacer(modifier = Modifier.size(20.dp)) + Text("Ошибка", Modifier.testTag("book_error")) + Button(onClick = { }) { + Text(text = "Обновить") + Modifier + .padding(20.dp) + .fillMaxWidth() + .semantics { testTag = "book_refresh_button" } + } + Text("Всё забронировано", Modifier.testTag("book_empty")) + + } + + + } } diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookState.kt b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookState.kt new file mode 100644 index 0000000..eb69ccd --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookState.kt @@ -0,0 +1,8 @@ +package ru.myitschool.work.ui.screen.book + +import ru.myitschool.work.ui.screen.auth.AuthState + +interface BookState { + object Loading: BookState + object Data: BookState +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/book/BookViewModel.kt b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookViewModel.kt new file mode 100644 index 0000000..ff9a04f --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/screen/book/BookViewModel.kt @@ -0,0 +1,43 @@ +package ru.myitschool.work.ui.screen.book + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.Dispatchers +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.domain.auth.CheckAndSaveAuthCodeUseCase + +class BookViewModel: ViewModel() { + private val checkAndSaveAuthCodeUseCase by lazy { CheckAndSaveAuthCodeUseCase(AuthRepository) } + private val _uiState = MutableStateFlow(BookState.Data) + val uiState: StateFlow = _uiState.asStateFlow() + + private val _actionFlow: MutableSharedFlow = MutableSharedFlow() + val actionFlow: SharedFlow = _actionFlow + + fun onIntent(intent: BookIntent) { + when (intent) { + is BookIntent.Send -> { + viewModelScope.launch(Dispatchers.Default) { + _uiState.update { BookState.Loading } + checkAndSaveAuthCodeUseCase.invoke("9999").fold( + onSuccess = { + _actionFlow.emit(Unit) + }, + onFailure = { error -> + error.printStackTrace() + _actionFlow.emit(Unit) + } + ) + } + } + is BookIntent.TextInput -> Unit + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/book/MainState.kt b/app/src/main/java/ru/myitschool/work/ui/screen/book/MainState.kt new file mode 100644 index 0000000..e12c10b --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/screen/book/MainState.kt @@ -0,0 +1,4 @@ +package ru.myitschool.work.ui.screen.book + +interface MainState { +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/main/MainIntent.kt b/app/src/main/java/ru/myitschool/work/ui/screen/main/MainIntent.kt new file mode 100644 index 0000000..40d5c01 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/screen/main/MainIntent.kt @@ -0,0 +1,7 @@ +package ru.myitschool.work.ui.screen.main + + +interface MainIntent { + data class Send(val text: String): MainIntent + data class TextInput(val text: String): MainIntent +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/main/MainScreen.kt b/app/src/main/java/ru/myitschool/work/ui/screen/main/MainScreen.kt new file mode 100644 index 0000000..a0c29f8 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/screen/main/MainScreen.kt @@ -0,0 +1,153 @@ +package ru.myitschool.work.ui.screen.main + + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +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.OutlinedButton +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Color.Companion.Gray +import androidx.compose.ui.graphics.painter.ColorPainter +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.testTag +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavController +import ru.myitschool.work.R +import ru.myitschool.work.core.TestIds +import ru.myitschool.work.ui.nav.BookScreenDestination +import ru.myitschool.work.ui.nav.MainScreenDestination +import ru.myitschool.work.ui.screen.auth.AuthIntent +import ru.myitschool.work.ui.screen.auth.AuthState +import ru.myitschool.work.ui.screen.auth.AuthViewModel +import ru.myitschool.work.ui.screen.book.BookIntent + +@Composable +fun MainScreen( + viewModel: MainViewModel = viewModel(), + navController: NavController +) { + + val state by viewModel.uiState.collectAsState() + + LaunchedEffect(Unit) { + viewModel.actionFlow.collect { + navController.navigate(BookScreenDestination) + } + } + + Column( + modifier = Modifier + .fillMaxSize() + .padding(all = 24.dp) + ) { + when (val currentState = state) { + is MainState.Data -> Content(viewModel, currentState) + is MainState.Loading -> { + CircularProgressIndicator( + modifier = Modifier.size(64.dp) + ) + } + } + } +} + +@Composable +private fun Content( + viewModel: MainViewModel, + state: MainState.Data +) { + var expanded by remember{ mutableStateOf(false)} + var inputUsername by remember { mutableStateOf("Введите имя") } + var input by remember { mutableStateOf("") } + val date:String = "dd.MM.yyyy" + val username:String = "test" + var place:String = "test" + val error = false + Spacer(modifier = Modifier.size(16.dp)) + Column() { + Spacer(modifier = Modifier.size(16.dp)) + Text( + modifier = Modifier.testTag("main_name").fillMaxWidth(), + text = username + ) + Spacer(modifier = Modifier.size(10.dp)) + Image(painter = ColorPainter(Color.DarkGray), contentDescription = "main_photo", modifier = Modifier.size(50.dp)) + + Spacer(modifier = Modifier.size(10.dp)) + Row (horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth().padding(8.dp)) { + + Button(onClick = { }) { + Text(text = "Выход") + Modifier + .padding(20.dp) + .fillMaxWidth() + .semantics { testTag = "main_logout_button" } + } + Spacer(modifier = Modifier.size(10.dp)) + Button(onClick = { }) { + Text(text = "Обновить") + Modifier + .padding(20.dp) + .fillMaxWidth() + .semantics { testTag = "main_refresh_button" } + } + Spacer(modifier = Modifier.size(10.dp)) + Button(onClick = { + viewModel.onIntent(MainIntent.Send(input)) + }) { + Text(text = "Бронировать") + Modifier + .padding(20.dp) + .fillMaxWidth() + .semantics { testTag = "main_add_button" } + } + Spacer(modifier = Modifier.size(10.dp)) + } + Spacer(modifier = Modifier.size(10.dp)) + + + Text( + modifier = Modifier.testTag("main_item_date").fillMaxWidth(), + text = date + ) + Spacer(modifier = Modifier.size(10.dp)) + Text( + modifier = Modifier.testTag("main_item_place").fillMaxWidth(), + text = place + ) + Text(text = "ERROR", + modifier = Modifier.testTag("main_error").fillMaxWidth().alpha(0f)) + + + } + var inputText by remember { mutableStateOf("") } + Spacer(modifier = Modifier.size(16.dp)) + + +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/main/MainState.kt b/app/src/main/java/ru/myitschool/work/ui/screen/main/MainState.kt new file mode 100644 index 0000000..463915b --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/screen/main/MainState.kt @@ -0,0 +1,8 @@ +package ru.myitschool.work.ui.screen.main + +import ru.myitschool.work.ui.screen.auth.AuthState + +interface MainState { + object Loading: MainState + object Data: MainState +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/screen/main/MainViewModel.kt b/app/src/main/java/ru/myitschool/work/ui/screen/main/MainViewModel.kt new file mode 100644 index 0000000..a69ed85 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/screen/main/MainViewModel.kt @@ -0,0 +1,43 @@ +package ru.myitschool.work.ui.screen.main + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.Dispatchers +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.domain.auth.CheckAndSaveAuthCodeUseCase + +class MainViewModel: ViewModel() { + private val checkAndSaveAuthCodeUseCase by lazy { CheckAndSaveAuthCodeUseCase(AuthRepository) } + private val _uiState = MutableStateFlow(MainState.Data) + val uiState: StateFlow = _uiState.asStateFlow() + private val _actionFlow: MutableSharedFlow = MutableSharedFlow() + val actionFlow: SharedFlow = _actionFlow + + fun onIntent(intent: MainIntent) { + when (intent) { + is MainIntent.Send -> { + viewModelScope.launch(Dispatchers.Default) { + _uiState.update { MainState.Loading } + checkAndSaveAuthCodeUseCase.invoke("9999").fold( + onSuccess = { + _actionFlow.emit(Unit) + }, + onFailure = { error -> + error.printStackTrace() + _actionFlow.emit(Unit) + } + ) + } + } + + is MainIntent.TextInput -> Unit + } + } +} \ No newline at end of file