First request #5

Closed
student-21892 wants to merge 14 commits from student-21892/NTO-2025-Android-minipigs:main into main
8 changed files with 89 additions and 18 deletions
Showing only changes of commit c53bd718c0 - Show all commits
@@ -0,0 +1,9 @@
package ru.myitschool.work.data.models
import kotlinx.serialization.Serializable
@Serializable
data class BookBody (
val date: String,
val placeId: Int
)
@@ -6,6 +6,10 @@ import ru.myitschool.work.data.source.NetworkDataSource
object BookRepository {
suspend fun fetch(): Result<BookingInfo> {
return NetworkDataSource.book(getCode()).onSuccess { data -> data }
return NetworkDataSource.bookInfo(getCode()).onSuccess { data -> data }
}
suspend fun book(date: String, placeId: Int): Result<Boolean> {
return NetworkDataSource.book(getCode(), date, placeId).onSuccess { success -> success }
}
}
@@ -27,5 +27,5 @@ object LocalDataSource {
appContext.dataStore.edit { it[Keys.CODE] = code }
}
val isCodePresentFlow: Flow<Boolean> = appContext.dataStore.data.map { it[Keys.CODE] != "" }
val isCodePresentFlow: Flow<Boolean> = appContext.dataStore.data.map { it[Keys.CODE] != "" || it[Keys.CODE] != null }
}
@@ -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<UserInfo> = 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<BookingInfo> = withContext(Dispatchers.IO) {
suspend fun bookInfo(code: String): Result<BookingInfo> = 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<Boolean> = 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"
}
@@ -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<Unit> {
return repository.book(date, placeId).mapCatching { success ->
if (!success) error("Code is incorrect")
}
}
}
@@ -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
}
@@ -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<Unit>) {
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(
@@ -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>(BookState.Loading)
val uiState: StateFlow<BookState> = _uiState.asStateFlow()
private val _actionFlow: MutableSharedFlow<Unit> = 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)
}