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

View File

@@ -0,0 +1,9 @@
package ru.myitschool.work.data.models
import kotlinx.serialization.Serializable
@Serializable
data class BookBody (
val date: String,
val placeId: Int
)

View File

@@ -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 }
}
}

View File

@@ -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 }
}

View File

@@ -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"
}

View File

@@ -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")
}
}
}

View File

@@ -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
}

View File

@@ -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(

View File

@@ -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)
}