13 Commits

Author SHA1 Message Date
student-18211 3941641d1f Merge pull request 'My job and Egor's job' (#6) from student-15047/NTO-2025-Android-TeamTask:main into main
Reviewed-on: student-18211/NTO-2025-Android-TeamTask#6
2025-12-08 19:17:52 +00:00
student-15047 b11e010cdc completed user authorization with a saved user, and also optimized imports 2025-12-08 22:07:43 +03:00
student-15047 8a5fee532b I completed all the tasks assigned to me and combined my part of the project with Egor's part. 2025-12-08 21:22:55 +03:00
student-18211 d4fc4a929f Merge pull request 'I made the logic for moving between screens and added stubs' (#5) from student-15047/NTO-2025-Android-TeamTask:main into main
Reviewed-on: student-18211/NTO-2025-Android-TeamTask#5
2025-12-05 18:35:30 +00:00
student-15047 2d9682ebba merge upstream 2025-12-05 18:33:07 +00:00
student-15047 655baf713f I made the logic for moving between screens and added stubs 2025-12-05 21:32:25 +03:00
student-18211 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
student-15047 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
student-15047 f5ff5dbca8 I merge fork 2025-12-05 18:28:06 +03:00
student-15047 09e97f9d67 I configured the server connection and improved the logic 2025-12-04 23:19:42 +03:00
student-18211 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
student-18211 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
student-15047 2807368adb Added appearance and basic booking functionality 2025-11-30 17:52:39 +03:00
15 changed files with 469 additions and 43 deletions
+1 -1
View File
@@ -36,7 +36,7 @@ android {
dependencies { dependencies {
defaultComposeLibrary() defaultComposeLibrary()
implementation("androidx.datastore:datastore-preferences:1.2.0") implementation("androidx.datastore:datastore-preferences:1.1.7")
implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.4.0") implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.4.0")
implementation("androidx.navigation:navigation-compose:2.9.6") implementation("androidx.navigation:navigation-compose:2.9.6")
val coil = "3.3.0" val coil = "3.3.0"
@@ -1,7 +1,7 @@
package ru.myitschool.work.core package ru.myitschool.work.core
object Constants { object Constants {
const val HOST = "http://192.168.0.121:8080/" const val HOST = "http://192.168.1.74:8080"
const val AUTH_URL = "/auth" const val AUTH_URL = "/auth"
const val INFO_URL = "/info" const val INFO_URL = "/info"
const val BOOKING_URL = "/booking" const val BOOKING_URL = "/booking"
@@ -0,0 +1,14 @@
package ru.myitschool.work.data.repo
import ru.myitschool.work.data.source.NetworkDataSource
import ru.myitschool.work.ui.screen.BookingItem
object BookRepository {
suspend fun loadBooks(code: String): Result<Map<String, List<BookingItem>>> {
return NetworkDataSource.getFreeBooking(code)
}
suspend fun sendData(code: String, data: String, placeId: Int) : Result<Boolean> {
return NetworkDataSource.createNewBooking(code, data, placeId)
}
}
@@ -1,8 +1,8 @@
package ru.myitschool.work.data.repo package ru.myitschool.work.data.repo
import androidx.core.content.edit
import ru.myitschool.work.data.source.NetworkDataSource import ru.myitschool.work.data.source.NetworkDataSource
import ru.myitschool.work.ui.screen.UserInfo import ru.myitschool.work.ui.screen.UserInfo
import androidx.core.content.edit
object MainRepository { object MainRepository {
suspend fun loadUserInfo(code: String): Result<UserInfo> { suspend fun loadUserInfo(code: String): Result<UserInfo> {
@@ -1,33 +1,36 @@
package ru.myitschool.work.data.source package ru.myitschool.work.data.source
import android.icu.text.IDNA
import android.service.autofill.UserData
import android.util.Log import android.util.Log
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.call.body import io.ktor.client.call.body
import io.ktor.client.engine.cio.CIO import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.request.get import io.ktor.client.request.get
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.bodyAsText import io.ktor.client.statement.bodyAsText
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
import io.ktor.http.contentType
import io.ktor.serialization.kotlinx.json.json import io.ktor.serialization.kotlinx.json.json
import io.ktor.utils.io.InternalAPI
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import ru.myitschool.work.core.Constants import ru.myitschool.work.core.Constants
import ru.myitschool.work.ui.screen.BookingItem
import ru.myitschool.work.ui.screen.CreateBookingRequest
import ru.myitschool.work.ui.screen.UserInfo import ru.myitschool.work.ui.screen.UserInfo
object NetworkDataSource { object NetworkDataSource {
private val client by lazy { private val client by lazy {
HttpClient(CIO) { HttpClient(CIO) {
engine { requestTimeout= 10000; }
install(ContentNegotiation) { install(ContentNegotiation) {
json( json(
Json { Json {
isLenient = true isLenient = true
ignoreUnknownKeys = true ignoreUnknownKeys = true
explicitNulls = false explicitNulls = true
encodeDefaults = true encodeDefaults = true
} }
) )
@@ -35,6 +38,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))
@@ -51,16 +55,45 @@ object NetworkDataSource {
return@withContext runCatching { return@withContext runCatching {
val response = client.get(getUrl(code, Constants.INFO_URL)) val response = client.get(getUrl(code, Constants.INFO_URL))
when(response.status){ when(response.status){
HttpStatusCode.OK -> response.body() HttpStatusCode.OK -> {
response.body<UserInfo>()
}
else -> error("Ошибка получения данных") else -> error("Ошибка получения данных")
} }
} }
} }
private const val TAG = "NetworkDataSource"
private fun getUrl(code: String, targetUrl: String): String { suspend fun getFreeBooking(code: String): Result<Map<String, List<BookingItem>>> =
val url = "${Constants.HOST}api/$code$targetUrl" withContext(Dispatchers.IO) {
Log.d(TAG, "URL: $url") runCatching {
return url val response = client.get(getUrl(code, Constants.BOOKING_URL))
}} when (response.status) {
HttpStatusCode.OK -> {
// Используйте response.body с явным указанием типа
response.body<Map<String, List<BookingItem>>>()
}
else -> error(response.bodyAsText())
}
}
}
@OptIn(InternalAPI::class)
suspend fun createNewBooking(code: String, data: String, placeId: Int): Result<Boolean> =
withContext(Dispatchers.IO) {
return@withContext runCatching {
val response = client.post(getUrl(code, Constants.BOOK_URL)) {
contentType(ContentType.Application.Json)
setBody(CreateBookingRequest(data, placeId)) // используйте setBody вместо body
}
when (response.status) {
HttpStatusCode.OK -> true
else -> error(response.bodyAsText())
}
}
}
private fun getUrl(code: String, targetUrl: String) = "${Constants.HOST}/api/$code$targetUrl"
}
@@ -1,23 +1,38 @@
package ru.myitschool.work.ui.screen package ru.myitschool.work.ui.screen
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
data class UserInfo( data class UserInfo(
val name: String, val name: String,
@SerialName("photoUrl") val photoUrl: String?, val photoUrl: String? = null,
@SerialName("booking") val booking: Map<String, BookingItem> val booking: Map<String, List<BookingItem>>
) { ) {
val bookings: List<Booking> by lazy { fun bookingToList() : List<Booking> {
booking.map { (date, item) -> val bookings = mutableListOf<Booking>()
Booking(date = date, place = item.place) booking.forEach {
}.sortedBy { it.date } it.value.forEach { value ->
bookings.add(Booking(it.key, value.place))
}
}
return bookings
} }
} }
@Serializable @Serializable
data class BookingItem(val place: String, val id: Int) data class Booking(
val date: String,
val place: String
)
@Serializable @Serializable
data class Booking(val date: String, val place: String) data class BookingItem(
val id: Int,
val place: String
)
@Serializable
data class CreateBookingRequest(
val date: String,
val placeId: Int
)
@@ -1,22 +1,20 @@
package ru.myitschool.work.ui.screen package ru.myitschool.work.ui.screen
import android.util.Log
import androidx.compose.animation.EnterTransition import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition import androidx.compose.animation.ExitTransition
import androidx.compose.foundation.layout.Box
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import ru.myitschool.work.data.repo.AuthRepository
import ru.myitschool.work.ui.nav.AuthScreenDestination import ru.myitschool.work.ui.nav.AuthScreenDestination
import ru.myitschool.work.ui.nav.BookScreenDestination import ru.myitschool.work.ui.nav.BookScreenDestination
import ru.myitschool.work.ui.nav.MainScreenDestination import ru.myitschool.work.ui.nav.MainScreenDestination
import ru.myitschool.work.ui.screen.auth.AuthScreen import ru.myitschool.work.ui.screen.auth.AuthScreen
import ru.myitschool.work.ui.screen.book.BookScreen
import ru.myitschool.work.ui.screen.main.MainScreen import ru.myitschool.work.ui.screen.main.MainScreen
@Composable @Composable
@@ -24,29 +22,27 @@ fun AppNavHost(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController() navController: NavHostController = rememberNavController()
) { ) {
val startDestination = if (AuthRepository.getSavedCode() != null) {
MainScreenDestination
} else {
AuthScreenDestination
}
NavHost( NavHost(
modifier = modifier, modifier = modifier,
enterTransition = { EnterTransition.None },
exitTransition = { ExitTransition.None },
navController = navController, navController = navController,
startDestination = startDestination startDestination = AuthScreenDestination,
) { ) {
composable<AuthScreenDestination> { composable<AuthScreenDestination> {
Log.d("compose", "Auth")
AuthScreen(navController = navController) AuthScreen(navController = navController)
} }
composable<MainScreenDestination> { composable<MainScreenDestination> {
MainScreen( Log.d("compose", "Main")
MainScreen(viewModel = viewModel(), navController)
}
composable<BookScreenDestination> {
Log.d("compose", "Book")
BookScreen(
viewModel = viewModel(), viewModel = viewModel(),
navController = navController navController = navController
) )
} }
composable<BookScreenDestination> {
Box(contentAlignment = Alignment.Center) {
Text("Hello")
}
}
} }
} }
@@ -5,7 +5,6 @@ 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
@@ -30,6 +29,7 @@ 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.TestIds import ru.myitschool.work.core.TestIds
import ru.myitschool.work.data.repo.AuthRepository
import ru.myitschool.work.ui.nav.MainScreenDestination import ru.myitschool.work.ui.nav.MainScreenDestination
@Composable @Composable
@@ -45,6 +45,8 @@ fun AuthScreen(
} }
} }
AuthRepository.getSavedCode()?.let { viewModel.onIntent(AuthIntent.Send(it)) }
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@@ -0,0 +1,8 @@
package ru.myitschool.work.ui.screen.book
sealed interface BookIntent {
data class Send(val code : String, val data: String, val placeId : Int) : BookIntent
data object BackToMainScreen : BookIntent
data object ToAuthScreen : BookIntent
data object LoadData : BookIntent
}
@@ -0,0 +1,220 @@
package ru.myitschool.work.ui.screen.book;
import android.util.Log
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.selection.selectableGroup
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
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.platform.testTag
import androidx.compose.ui.res.stringResource
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.data.repo.AuthRepository
import ru.myitschool.work.ui.nav.AuthScreenDestination
import ru.myitschool.work.ui.nav.MainScreenDestination
import java.time.LocalDate
import java.time.format.DateTimeFormatter
@Composable
fun BookScreen(
viewModel: BookViewModel = viewModel(),
navController: NavController,
modifier: Modifier = Modifier
) {
val state by viewModel.uiState.collectAsState()
LaunchedEffect(Unit) {
Log.d("BookScreen", "1")
viewModel.navigationFlow.collect { event ->
Log.d("navigation", "Event received: $event")
when (event) {
BookNavigationEvent.NavigateToMain -> {
Log.d("BookScreen", "3")
navController.navigate(MainScreenDestination)
}
BookNavigationEvent.NavigateToAuth -> {
Log.d("BookScreen", "4")
navController.navigate(AuthScreenDestination) {
Log.d("BookScreen", "5")
popUpTo(navController.graph.startDestinationId) {
Log.d("BookScreen", "6")
inclusive = true
}
}
}
}
}
}
Column(
modifier = modifier.fillMaxSize()
) {
// Text("" + selectedTime.value + "," + currentTime.value + "," + selectedBooking.value)
when (state) {
is BookState.Data -> {
var selectedTime : String? by remember { mutableStateOf(null) }
var selectedBooking : Int? by remember { mutableStateOf(null) }
var currentTime : String? by remember { mutableStateOf(null) }
val options = (state as BookState.Data).booking
if (currentTime == null)
for (el in options) {
if (!el.value.isEmpty()) {
currentTime = el.key
break
}
}
NavigationBar(
Modifier.fillMaxWidth()
) {
val inputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
val outputFormatter = DateTimeFormatter.ofPattern("dd.MM")
options.keys
.map { LocalDate.parse(it, inputFormatter) }
.sorted()
// .map { it.format(inputFormatter) }
.forEachIndexed { index, label ->
options[label.format(inputFormatter)]?.let {
if (!it.isEmpty()) {
NavigationBarItem(
selected = currentTime == label.format(inputFormatter),
onClick = {
currentTime = label.format(inputFormatter)
},
icon = {
Text(
text = label.format(outputFormatter),
modifier = Modifier
.testTag(TestIds.Book.ITEM_DATE)
)
},
modifier = Modifier
.testTag(TestIds.Book.getIdDateItemByPosition(index))
)
}
}
}
}
LazyColumn(
Modifier
.fillMaxWidth()
) {
options[currentTime]?.forEach { book ->
item {
Row(
Modifier
.fillMaxWidth()
.selectableGroup()
.testTag(TestIds.Book.getIdPlaceItemByPosition(book.id)),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = book.place,
modifier = Modifier
.testTag(TestIds.Book.ITEM_PLACE_TEXT)
)
RadioButton(
selected = book.id == selectedBooking && currentTime == selectedTime,
onClick = {
selectedBooking = book.id
selectedTime = currentTime
},
modifier = Modifier
.testTag(TestIds.Book.ITEM_PLACE_SELECTOR)
)
}
}
}
}
Button(
onClick = {
viewModel.onIntent(BookIntent.Send(AuthRepository.getSavedCode()!!, selectedTime!!, selectedBooking!!))
},
modifier = Modifier
.testTag(TestIds.Book.BOOK_BUTTON),
enabled = selectedBooking != null
) {
Text(stringResource(R.string.to_book))
}
}
is BookState.Error -> {
Text(
text = (state as BookState.Error).error,
color = MaterialTheme.colorScheme.error,
modifier = Modifier
.testTag(TestIds.Book.ERROR)
)
Button(
onClick = {
viewModel.onIntent(BookIntent.LoadData)
},
modifier = Modifier
.testTag(TestIds.Book.REFRESH_BUTTON)
) {
Text(stringResource(R.string.upadate)) // А что сюда писать?
}
}
BookState.Loading -> {
CircularProgressIndicator(
modifier = Modifier
.align(Alignment.CenterHorizontally)
.size(64.dp)
)
}
BookState.NotData -> {
Text(
text = stringResource(R.string.not_book),
modifier = Modifier
.testTag(TestIds.Book.EMPTY)
)
}
}
Button(
onClick = {
viewModel.onIntent(BookIntent.BackToMainScreen)
},
modifier = Modifier
.testTag(TestIds.Book.BACK_BUTTON)
) {
Text(stringResource(R.string.back))
}
}
}
@@ -0,0 +1,14 @@
package ru.myitschool.work.ui.screen.book
import ru.myitschool.work.ui.screen.BookingItem
sealed interface BookState {
object Loading: BookState
data class Data(
val booking: Map<String, List<BookingItem>> = mapOf()
): BookState
data class Error(
val error : String
): BookState
object NotData : BookState
}
@@ -0,0 +1,121 @@
package ru.myitschool.work.ui.screen.book
import android.util.Log
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.data.repo.BookRepository
class BookViewModel : ViewModel() {
private val _uiState = MutableStateFlow<BookState>(BookState.Loading)
val uiState: StateFlow<BookState> = _uiState.asStateFlow();
private val _navigationFlow = MutableSharedFlow<BookNavigationEvent>(replay = 0, extraBufferCapacity = 1)
val navigationFlow: SharedFlow<BookNavigationEvent> = _navigationFlow
init {
loadData()
}
private fun loadData() {
viewModelScope.launch(Dispatchers.IO) {
_uiState.update { BookState.Loading }
val code = AuthRepository.getSavedCode() ?: run {
_navigationFlow.emit(BookNavigationEvent.NavigateToAuth)
Log.d("", "Go to AuthScreen")
return@launch
}
Log.d("", "Проверка")
// _uiState.update {
// BookState.Data(
// listOf(
// Booking(
// "19.04",
// "Рабочее место у окна"
// ),
// Booking(
// "19.04",
// "Переговорная комната № 1"
// ),
// Booking(
// "19.04",
// "Коворкинг А"
// ),
// Booking(
// "20.04",
// "Кабинет № 33"
// ),
// ).toMap()
// )
// }
BookRepository.loadBooks(code).fold(
onSuccess = {
it.let { bookings ->
_uiState.update {
Log.d("test", bookings.isEmpty().toString())
when (!bookings.isEmpty()) {
true -> BookState.Data(
booking = bookings
)
false -> BookState.NotData
}
}
}
},
onFailure = { error ->
_uiState.update {
BookState.Error(
error = error.message ?: "Не удалось загрузить данные"
)
}
}
)
}
}
fun onIntent(intent: BookIntent) {
when (intent) {
is BookIntent.Send -> {
viewModelScope.launch(Dispatchers.IO) {
_uiState.update { BookState.Loading }
BookRepository.sendData(intent.code, intent.data, intent.placeId).fold(
onSuccess = {
Log.d("send date", "success")
_navigationFlow.emit(BookNavigationEvent.NavigateToMain)
},
onFailure = { error ->
Log.d("send date", "error: $error")
_uiState.update {
BookState.Error(error.message ?: "Неизвестная ошибка")
}
}
)
}
}
BookIntent.BackToMainScreen -> {
viewModelScope.launch {
_navigationFlow.emit(BookNavigationEvent.NavigateToMain)
}
}
BookIntent.LoadData -> loadData()
BookIntent.ToAuthScreen -> {
viewModelScope.launch {
_navigationFlow.emit(BookNavigationEvent.NavigateToAuth)
}
}
}
}
}
sealed interface BookNavigationEvent {
object NavigateToAuth : BookNavigationEvent
object NavigateToMain : BookNavigationEvent
}
@@ -9,7 +9,6 @@ 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.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import ru.myitschool.work.data.repo.AuthRepository import ru.myitschool.work.data.repo.AuthRepository
@@ -50,7 +49,7 @@ class MainViewModel : ViewModel() {
val inputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") val inputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
val outputFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy") val outputFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy")
val sortedBookings = userInfo.bookings val sortedBookings = userInfo.bookingToList()
.sortedBy { LocalDate.parse(it.date, inputFormatter) } .sortedBy { LocalDate.parse(it.date, inputFormatter) }
.map { booking -> .map { booking ->
Booking( Booking(
+4
View File
@@ -4,4 +4,8 @@
<string name="auth_title">Привет! Введи код для авторизации</string> <string name="auth_title">Привет! Введи код для авторизации</string>
<string name="auth_label">Код</string> <string name="auth_label">Код</string>
<string name="auth_sign_in">Войти</string> <string name="auth_sign_in">Войти</string>
<string name="to_book">забронировать</string>
<string name="back">Назад</string>
<string name="not_book">Всё забранировано</string>
<string name="upadate">Обновить</string>
</resources> </resources>