diff --git a/app/src/main/java/ru/myitschool/work/data/models/BookingInfo.kt b/app/src/main/java/ru/myitschool/work/data/models/BookingInfo.kt new file mode 100644 index 0000000..d60ffbf --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/data/models/BookingInfo.kt @@ -0,0 +1,3 @@ +package ru.myitschool.work.data.models + +typealias BookingInfo = Map> \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/data/models/UserData.kt b/app/src/main/java/ru/myitschool/work/data/models/UserInfo.kt similarity index 89% rename from app/src/main/java/ru/myitschool/work/data/models/UserData.kt rename to app/src/main/java/ru/myitschool/work/data/models/UserInfo.kt index 039837c..7728fc3 100644 --- a/app/src/main/java/ru/myitschool/work/data/models/UserData.kt +++ b/app/src/main/java/ru/myitschool/work/data/models/UserInfo.kt @@ -3,7 +3,7 @@ package ru.myitschool.work.data.models import kotlinx.serialization.Serializable @Serializable -data class UserData( +data class UserInfo( val name: String, val photoUrl: String, val booking: Map 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 ead988b..f305d95 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 @@ -1,4 +1,11 @@ package ru.myitschool.work.data.repo +import ru.myitschool.work.data.models.BookingInfo +import ru.myitschool.work.data.repo.AuthRepository.getCode +import ru.myitschool.work.data.source.NetworkDataSource + object BookRepository { + suspend fun fetch(): Result { + return NetworkDataSource.book(getCode()).onSuccess { data -> data } + } } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/data/repo/MainRepository.kt b/app/src/main/java/ru/myitschool/work/data/repo/MainRepository.kt index cdfe93d..3d49bfa 100644 --- a/app/src/main/java/ru/myitschool/work/data/repo/MainRepository.kt +++ b/app/src/main/java/ru/myitschool/work/data/repo/MainRepository.kt @@ -1,14 +1,11 @@ package ru.myitschool.work.data.repo -import android.util.Log -import ru.myitschool.work.data.models.Booking -import ru.myitschool.work.data.models.UserData +import ru.myitschool.work.data.models.UserInfo import ru.myitschool.work.data.repo.AuthRepository.getCode -import ru.myitschool.work.data.source.LocalDataSource import ru.myitschool.work.data.source.NetworkDataSource object MainRepository { - suspend fun fetch(): Result { - return NetworkDataSource.Info(getCode()).onSuccess { data -> data.name } + suspend fun fetch(): Result { + return NetworkDataSource.info(getCode()).onSuccess { data -> data } } } \ 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 091ed82..426701e 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 @@ -1,6 +1,5 @@ package ru.myitschool.work.data.source -import android.util.Log import io.ktor.client.HttpClient import io.ktor.client.call.body import io.ktor.client.engine.cio.CIO @@ -13,8 +12,8 @@ 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.Booking -import ru.myitschool.work.data.models.UserData +import ru.myitschool.work.data.models.BookingInfo +import ru.myitschool.work.data.models.UserInfo object NetworkDataSource { private val client by lazy { @@ -44,11 +43,21 @@ object NetworkDataSource { private fun getUrl(code: String, targetUrl: String) = "${Constants.HOST}/api/$code$targetUrl" - suspend fun Info(code: String): Result = withContext(Dispatchers.IO) { + suspend fun info(code: String): Result = withContext(Dispatchers.IO) { return@withContext runCatching { val response = client.get(getUrl(code, Constants.INFO_URL)) when (response.status) { - HttpStatusCode.OK -> Json.decodeFromString(response.body()) + HttpStatusCode.OK -> Json.decodeFromString(response.body()) + else -> error(response.bodyAsText()) + } + } + } + + suspend fun book(code: String): Result = withContext(Dispatchers.IO) { + return@withContext runCatching { + val response = client.get(getUrl(code, Constants.BOOKING_URL)) + when (response.status) { + HttpStatusCode.OK -> Json.decodeFromString(response.body()) else -> error(response.bodyAsText()) } } 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 new file mode 100644 index 0000000..2d395bb --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/domain/book/Book.kt @@ -0,0 +1,4 @@ +package ru.myitschool.work.domain.book + +class Book { +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/book/Fetch.kt b/app/src/main/java/ru/myitschool/work/domain/book/Fetch.kt new file mode 100644 index 0000000..6a78817 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/domain/book/Fetch.kt @@ -0,0 +1,12 @@ +package ru.myitschool.work.domain.book + +import ru.myitschool.work.data.models.BookingInfo +import ru.myitschool.work.data.repo.BookRepository + +class Fetch ( + private val repository: BookRepository +) { + suspend operator fun invoke(): Result { + return repository.fetch().mapCatching { success -> success.filter { it.value.isNotEmpty() } as BookingInfo } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/main/Fetch.kt b/app/src/main/java/ru/myitschool/work/domain/main/Fetch.kt index 540e789..b2496d9 100644 --- a/app/src/main/java/ru/myitschool/work/domain/main/Fetch.kt +++ b/app/src/main/java/ru/myitschool/work/domain/main/Fetch.kt @@ -1,12 +1,12 @@ package ru.myitschool.work.domain.main -import ru.myitschool.work.data.models.UserData +import ru.myitschool.work.data.models.UserInfo import ru.myitschool.work.data.repo.MainRepository class Fetch( private val repository: MainRepository ) { - suspend operator fun invoke(): Result { + suspend operator fun invoke(): Result { return repository.fetch().mapCatching { success -> success } } } \ 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 f306382..ba46f51 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 @@ -41,6 +41,7 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import ru.myitschool.work.core.TestIds +import ru.myitschool.work.data.models.Booking import ru.myitschool.work.ui.nav.MainScreenDestination @Composable @@ -49,7 +50,8 @@ fun BookScreen( viewModel: BookViewModel = viewModel() ) { val state by viewModel.uiState.collectAsState() - val dates = remember { bookingsByDate.keys.sorted() } + val info by viewModel.infoFlow.collectAsState() + val err by viewModel.errorFlow.collectAsState() var selectedTabIndex by remember { mutableStateOf(0) } Box( @@ -73,7 +75,7 @@ fun BookScreen( modifier = Modifier.fillMaxSize() ) { Text( - text = "TEST_ERROR", + text = err, modifier = Modifier.testTag(TestIds.Book.ERROR), color = MaterialTheme.colorScheme.error ) @@ -100,13 +102,13 @@ fun BookScreen( containerColor = MaterialTheme.colorScheme.surface, contentColor = MaterialTheme.colorScheme.primary, ) { - dates.forEachIndexed { index, date -> + info!!.entries.toList().forEachIndexed { index, entry -> Tab( selected = selectedTabIndex == index, onClick = { selectedTabIndex = index }, text = { Text( - text = date, + text = entry.key, modifier = Modifier.testTag(TestIds.Book.ITEM_DATE) ) }, @@ -115,13 +117,10 @@ fun BookScreen( } } - val selectedDate = dates[selectedTabIndex] - val bookings = bookingsByDate[selectedDate] ?: emptyList() - LazyColumn( modifier = Modifier.fillMaxSize(), ) { - itemsIndexed(bookings) { index, booking -> + itemsIndexed(info!!.entries.toList()[selectedTabIndex].value) { index, booking -> Booking(booking, index) } } @@ -169,6 +168,7 @@ fun BookScreen( } LaunchedEffect(Unit) { + viewModel.onIntent(BookIntent.Fetch) viewModel.actionFlow.collect { navController.navigate(MainScreenDestination) } @@ -208,14 +208,4 @@ private fun Booking(booking: Booking, index: Int){ thickness = 1.dp, modifier = Modifier.padding(start = 16.dp) ) -} - -data class Booking(val id: Int, val place: String) -typealias BookingsByDate = Map> - -val bookingsByDate: BookingsByDate = mapOf( - "2025-01-05" to listOf(Booking(1, "102"), Booking(2, "209.13")), - "2025-01-06" to listOf(Booking(3, "Зона 51. 50")), - "2025-01-07" to listOf(Booking(1, "102"), Booking(2, "209.13")), - "2025-01-08" to listOf(Booking(2, "209.13")) -) \ No newline at end of file +} \ 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 index f1e46f6..15e843e 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 @@ -1,5 +1,6 @@ package ru.myitschool.work.ui.screen.book +import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.flow.MutableSharedFlow @@ -7,17 +8,44 @@ 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.models.BookingInfo +import ru.myitschool.work.data.repo.BookRepository +import ru.myitschool.work.domain.book.Fetch +import ru.myitschool.work.ui.screen.main.MainState class BookViewModel(): ViewModel() { - private val _uiState = MutableStateFlow(BookState.DataPresent) + private val fetch by lazy { Fetch(BookRepository) } + private val _uiState = MutableStateFlow(BookState.Loading) val uiState: StateFlow = _uiState.asStateFlow() private val _actionFlow: MutableSharedFlow = MutableSharedFlow() val actionFlow: SharedFlow = _actionFlow + private val _infoFlow: MutableStateFlow = MutableStateFlow(null) + val infoFlow: StateFlow = _infoFlow.asStateFlow() + private val _errorFlow = MutableStateFlow("") + val errorFlow: StateFlow = _errorFlow.asStateFlow() fun onIntent(intent: BookIntent) { when(intent) { - is BookIntent.Fetch -> Unit + is BookIntent.Fetch -> viewModelScope.launch { + _uiState.update { BookState.Loading } + fetch.invoke().fold( + onSuccess = { success -> + if (success.isEmpty()) { + _uiState.update { BookState.DataAbsent } + } else { + _infoFlow.update { success } + _uiState.update { BookState.DataPresent } + } + }, + onFailure = { failure -> + Log.d(failure.message, "failure") + _uiState.update { BookState.Error } + _errorFlow.update { failure.message.toString() } + } + ) + } is BookIntent.Book -> Unit is BookIntent.GoBack -> viewModelScope.launch { _actionFlow.emit(Unit) 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 index ec65c7d..5a63a6d 100644 --- 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 @@ -10,7 +10,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import ru.myitschool.work.data.models.UserData +import ru.myitschool.work.data.models.UserInfo import ru.myitschool.work.data.repo.AuthRepository import ru.myitschool.work.data.repo.MainRepository import ru.myitschool.work.domain.main.Fetch @@ -23,8 +23,8 @@ class MainViewModel(): ViewModel() { val uiState: StateFlow = _uiState.asStateFlow() private val _actionFlow: MutableSharedFlow = MutableSharedFlow() val actionFlow: SharedFlow = _actionFlow - private val _infoFlow: MutableStateFlow = MutableStateFlow(null) - val infoFlow: StateFlow = _infoFlow.asStateFlow() + private val _infoFlow: MutableStateFlow = MutableStateFlow(null) + val infoFlow: StateFlow = _infoFlow.asStateFlow() private val _errorFlow = MutableStateFlow("") val errorFlow: StateFlow = _errorFlow.asStateFlow() @@ -38,7 +38,7 @@ class MainViewModel(): ViewModel() { _uiState.update { MainState.Data } }, onFailure = { failure -> - Log.d(failure.message, "") + Log.d(failure.message, "failure") _uiState.update { MainState.Error } _errorFlow.update { failure.message.toString() } }