From c53bd718c0f8f7969af5a5043e676f38f6bc051a Mon Sep 17 00:00:00 2001 From: nicktun Date: Wed, 3 Dec 2025 21:21:52 +0300 Subject: [PATCH] Finished book push functions --- .../myitschool/work/data/models/BookBody.kt | 9 ++++++ .../work/data/repo/BookRepository.kt | 6 +++- .../work/data/source/LocalDataSource.kt | 2 +- .../work/data/source/NetworkDataSource.kt | 25 +++++++++++++-- .../ru/myitschool/work/domain/book/Book.kt | 14 +++++++- .../work/ui/screen/book/BookIntent.kt | 2 +- .../work/ui/screen/book/BookScreen.kt | 32 +++++++++++++------ .../work/ui/screen/book/BookViewModel.kt | 17 ++++++++-- 8 files changed, 89 insertions(+), 18 deletions(-) create mode 100644 app/src/main/java/ru/myitschool/work/data/models/BookBody.kt diff --git a/app/src/main/java/ru/myitschool/work/data/models/BookBody.kt b/app/src/main/java/ru/myitschool/work/data/models/BookBody.kt new file mode 100644 index 0000000..893b3a3 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/data/models/BookBody.kt @@ -0,0 +1,9 @@ +package ru.myitschool.work.data.models + +import kotlinx.serialization.Serializable + +@Serializable +data class BookBody ( + val date: String, + val placeId: Int +) \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/data/repo/BookRepository.kt b/app/src/main/java/ru/myitschool/work/data/repo/BookRepository.kt index f305d95..0105d29 100644 --- a/app/src/main/java/ru/myitschool/work/data/repo/BookRepository.kt +++ b/app/src/main/java/ru/myitschool/work/data/repo/BookRepository.kt @@ -6,6 +6,10 @@ import ru.myitschool.work.data.source.NetworkDataSource object BookRepository { suspend fun fetch(): Result { - return NetworkDataSource.book(getCode()).onSuccess { data -> data } + return NetworkDataSource.bookInfo(getCode()).onSuccess { data -> data } + } + + suspend fun book(date: String, placeId: Int): Result { + return NetworkDataSource.book(getCode(), date, placeId).onSuccess { success -> success } } } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/data/source/LocalDataSource.kt b/app/src/main/java/ru/myitschool/work/data/source/LocalDataSource.kt index b6e84e8..c41b351 100644 --- a/app/src/main/java/ru/myitschool/work/data/source/LocalDataSource.kt +++ b/app/src/main/java/ru/myitschool/work/data/source/LocalDataSource.kt @@ -27,5 +27,5 @@ object LocalDataSource { appContext.dataStore.edit { it[Keys.CODE] = code } } - val isCodePresentFlow: Flow = appContext.dataStore.data.map { it[Keys.CODE] != "" } + val isCodePresentFlow: Flow = appContext.dataStore.data.map { it[Keys.CODE] != "" || it[Keys.CODE] != null } } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/data/source/NetworkDataSource.kt b/app/src/main/java/ru/myitschool/work/data/source/NetworkDataSource.kt index 426701e..6b8e72b 100644 --- a/app/src/main/java/ru/myitschool/work/data/source/NetworkDataSource.kt +++ b/app/src/main/java/ru/myitschool/work/data/source/NetworkDataSource.kt @@ -5,13 +5,18 @@ 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.request.post +import io.ktor.client.request.setBody import io.ktor.client.statement.bodyAsText +import io.ktor.http.ContentType import io.ktor.http.HttpStatusCode +import io.ktor.http.contentType 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.models.BookBody import ru.myitschool.work.data.models.BookingInfo import ru.myitschool.work.data.models.UserInfo @@ -41,8 +46,6 @@ object NetworkDataSource { } } - private fun getUrl(code: String, targetUrl: String) = "${Constants.HOST}/api/$code$targetUrl" - suspend fun info(code: String): Result = withContext(Dispatchers.IO) { return@withContext runCatching { val response = client.get(getUrl(code, Constants.INFO_URL)) @@ -53,7 +56,7 @@ object NetworkDataSource { } } - suspend fun book(code: String): Result = withContext(Dispatchers.IO) { + suspend fun bookInfo(code: String): Result = withContext(Dispatchers.IO) { return@withContext runCatching { val response = client.get(getUrl(code, Constants.BOOKING_URL)) when (response.status) { @@ -62,4 +65,20 @@ object NetworkDataSource { } } } + + suspend fun book(code: String, date: String, placeId: Int): Result = withContext(Dispatchers.IO) { + return@withContext runCatching { +// val requestBodyString = Json.encodeToString(BookBody(date, placeId)) + val response = client.post((getUrl(code, Constants.BOOK_URL))) { + contentType(ContentType.Application.Json) + setBody(BookBody(date, placeId)) + } + when(response.status) { + HttpStatusCode.OK -> true + else -> error(response.bodyAsText()) + } + } + } + + private fun getUrl(code: String, targetUrl: String) = "${Constants.HOST}/api/$code$targetUrl" } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/book/Book.kt b/app/src/main/java/ru/myitschool/work/domain/book/Book.kt index 2d395bb..3f9c9e3 100644 --- a/app/src/main/java/ru/myitschool/work/domain/book/Book.kt +++ b/app/src/main/java/ru/myitschool/work/domain/book/Book.kt @@ -1,4 +1,16 @@ package ru.myitschool.work.domain.book -class Book { +import ru.myitschool.work.data.repo.BookRepository + +class Book ( + private val repository: BookRepository +) { + suspend operator fun invoke( + date: String, + placeId: Int + ): Result { + return repository.book(date, placeId).mapCatching { success -> + if (!success) error("Code is incorrect") + } + } } \ No newline at end of file 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 f947f3e..1c5be79 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 @@ -2,6 +2,6 @@ package ru.myitschool.work.ui.screen.book sealed interface BookIntent { data object Fetch: BookIntent - data object Book: BookIntent + data class Book(val date: String, val placeId: Int): BookIntent data object GoBack: 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 ba46f51..56b622e 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 @@ -53,6 +53,7 @@ fun BookScreen( val info by viewModel.infoFlow.collectAsState() val err by viewModel.errorFlow.collectAsState() var selectedTabIndex by remember { mutableStateOf(0) } + var selectedPlaceIndex by remember { mutableStateOf(0) } Box( modifier = Modifier @@ -95,17 +96,22 @@ fun BookScreen( } is BookState.DataPresent -> { + val entriesList = info!!.entries.toList() Column(modifier = Modifier.fillMaxSize()) { + PrimaryScrollableTabRow( selectedTabIndex = selectedTabIndex, edgePadding = 16.dp, containerColor = MaterialTheme.colorScheme.surface, contentColor = MaterialTheme.colorScheme.primary, ) { - info!!.entries.toList().forEachIndexed { index, entry -> + entriesList.forEachIndexed { index, entry -> Tab( selected = selectedTabIndex == index, - onClick = { selectedTabIndex = index }, + onClick = { + selectedTabIndex = index + selectedPlaceIndex = 0 + }, text = { Text( text = entry.key, @@ -120,14 +126,22 @@ fun BookScreen( LazyColumn( modifier = Modifier.fillMaxSize(), ) { - itemsIndexed(info!!.entries.toList()[selectedTabIndex].value) { index, booking -> - Booking(booking, index) + itemsIndexed(entriesList[selectedTabIndex].value) { index, booking -> + Booking( + booking = booking, + index = index, + selected = selectedPlaceIndex, + onRadioChange = { selectedPlaceIndex = index } + ) } } } ExtendedFloatingActionButton( - onClick = { viewModel.onIntent(BookIntent.Book) }, + onClick = { viewModel.onIntent(BookIntent.Book( + date = entriesList[selectedTabIndex].key, + placeId = entriesList[selectedTabIndex].value[selectedPlaceIndex].id + )) }, containerColor = MaterialTheme.colorScheme.secondaryContainer, contentColor = MaterialTheme.colorScheme.onSecondaryContainer, text = { @@ -170,13 +184,13 @@ fun BookScreen( LaunchedEffect(Unit) { viewModel.onIntent(BookIntent.Fetch) viewModel.actionFlow.collect { - navController.navigate(MainScreenDestination) + navController.popBackStack() } } } @Composable -private fun Booking(booking: Booking, index: Int){ +private fun Booking(booking: Booking, index: Int, selected: Int, onRadioChange: Function0) { Row( modifier = Modifier .fillMaxWidth() @@ -186,8 +200,8 @@ private fun Booking(booking: Booking, index: Int){ verticalAlignment = Alignment.CenterVertically, ) { RadioButton( - selected = false, - onClick = {}, + selected = selected == index, + onClick = onRadioChange, modifier = Modifier .testTag(TestIds.Book.ITEM_PLACE_SELECTOR) // .selectable( 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 index 15e843e..959d4a3 100644 --- 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 @@ -12,11 +12,12 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import ru.myitschool.work.data.models.BookingInfo import ru.myitschool.work.data.repo.BookRepository +import ru.myitschool.work.domain.book.Book import ru.myitschool.work.domain.book.Fetch -import ru.myitschool.work.ui.screen.main.MainState class BookViewModel(): ViewModel() { private val fetch by lazy { Fetch(BookRepository) } + private val book by lazy { Book(BookRepository) } private val _uiState = MutableStateFlow(BookState.Loading) val uiState: StateFlow = _uiState.asStateFlow() private val _actionFlow: MutableSharedFlow = MutableSharedFlow() @@ -46,7 +47,19 @@ class BookViewModel(): ViewModel() { } ) } - is BookIntent.Book -> Unit + is BookIntent.Book -> viewModelScope.launch { + _uiState.update { BookState.Loading } + book.invoke(intent.date, intent.placeId).fold( + onSuccess = { success -> + _actionFlow.emit(Unit) + }, + onFailure = { failure -> + Log.d(failure.message, "failure") + _uiState.update { BookState.Error } + _errorFlow.update { failure.message.toString() } + } + ) + } is BookIntent.GoBack -> viewModelScope.launch { _actionFlow.emit(Unit) }