Mainpage logic implemented

This commit is contained in:
2025-12-01 21:38:25 +03:00
parent 053a916b55
commit 3e404ed765
8 changed files with 225 additions and 155 deletions

View File

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

View File

@@ -0,0 +1,10 @@
package ru.myitschool.work.data.models
import kotlinx.serialization.Serializable
@Serializable
data class UserData(
val name: String,
val photoUrl: String,
val booking: Map<String, Booking>
)

View File

@@ -0,0 +1,4 @@
package ru.myitschool.work.data.repo
object BookRepository {
}

View File

@@ -0,0 +1,14 @@
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.repo.AuthRepository.getCode
import ru.myitschool.work.data.source.LocalDataSource
import ru.myitschool.work.data.source.NetworkDataSource
object MainRepository {
suspend fun fetch(): Result<UserData> {
return NetworkDataSource.Info(getCode()).onSuccess { data -> data.name }
}
}

View File

@@ -1,6 +1,8 @@
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
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.request.get
@@ -11,6 +13,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
object NetworkDataSource {
private val client by lazy {
@@ -39,4 +43,14 @@ object NetworkDataSource {
}
private fun getUrl(code: String, targetUrl: String) = "${Constants.HOST}/api/$code$targetUrl"
suspend fun Info(code: String): Result<UserData> = withContext(Dispatchers.IO) {
return@withContext runCatching {
val response = client.get(getUrl(code, Constants.INFO_URL))
when (response.status) {
HttpStatusCode.OK -> Json.decodeFromString(response.body())
else -> error(response.bodyAsText())
}
}
}
}

View File

@@ -0,0 +1,12 @@
package ru.myitschool.work.domain.main
import ru.myitschool.work.data.models.UserData
import ru.myitschool.work.data.repo.MainRepository
class Fetch(
private val repository: MainRepository
) {
suspend operator fun invoke(): Result<UserData> {
return repository.fetch().mapCatching { success -> success }
}
}

View File

@@ -50,6 +50,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import coil3.compose.rememberAsyncImagePainter
import ru.myitschool.work.core.TestIds
import ru.myitschool.work.data.models.Booking
import ru.myitschool.work.ui.nav.BookScreenDestination
import ru.myitschool.work.ui.nav.MainScreenDestination
@@ -59,41 +60,24 @@ fun MainScreen(
navController: NavController
) {
val state by viewModel.uiState.collectAsState()
val bookings = listOf(
Booking(time = "08:00", place = "Meeting Room A"),
Booking(time = "09:30", place = "Conference Hall 1"),
Booking(time = "10:00", place = "CEO Office"),
Booking(time = "11:15", place = "Training Room B"),
Booking(time = "12:00", place = "Cafeteria (Lunch Meeting)"),
Booking(time = "13:30", place = "Boardroom"),
Booking(time = "14:00", place = "Design Studio"),
Booking(time = "15:00", place = "Meeting Room C"),
Booking(time = "16:15", place = "HR Office"),
Booking(time = "17:00", place = "Auditorium"),
Booking(time = "09:00", place = "Meeting Room B"),
Booking(time = "10:30", place = "Client Lounge"),
Booking(time = "11:00", place = "Server Room"),
Booking(time = "13:00", place = "Rooftop Terrace"),
Booking(time = "14:30", place = "Lab 3"),
Booking(time = "15:45", place = "Reception Area"),
Booking(time = "18:00", place = "Parking Lot (Team Event)"),
Booking(time = "19:30", place = "Offsite - Sky Bar"),
Booking(time = "08:30", place = "Video Conference Room"),
Booking(time = "16:30", place = "Gym (Wellness Session)")
)
val err by viewModel.errorFlow.collectAsState()
val info by viewModel.infoFlow.collectAsState()
when (val currentState = state) {
is MainState.Error -> {
Column (
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
text = "TEST_ERROR",
text = err,
modifier = Modifier.testTag(TestIds.Main.ERROR),
color = MaterialTheme.colorScheme.error
)
IconButton(
onClick = { viewModel.onIntent(MainIntent.Fetch) },
modifier = Modifier
.size(24.dp)
.size(32.dp)
.aspectRatio(1f)
.testTag(TestIds.Main.REFRESH_BUTTON),
enabled = true,
@@ -106,6 +90,7 @@ fun MainScreen(
)
}
}
}
is MainState.Loading -> {
Column(
@@ -117,6 +102,7 @@ fun MainScreen(
}
}
is MainState.Data -> {
info?.let {
Column (
modifier = Modifier.fillMaxSize()
) {
@@ -227,8 +213,8 @@ fun MainScreen(
modifier = Modifier
.fillMaxWidth()
) {
itemsIndexed(bookings) { index, booking ->
Booking(booking = booking, index = index)
itemsIndexed(info!!.booking.entries.toList()) { index, booking ->
Booking(booking = booking.value, date = booking.key, index = index)
}
}
}
@@ -248,6 +234,7 @@ fun MainScreen(
}
}
}
}
LaunchedEffect(Unit) {
viewModel.onIntent(MainIntent.Fetch)
@@ -259,7 +246,7 @@ fun MainScreen(
}
@Composable
private fun Booking(booking: Booking, index: Int){
private fun Booking(booking: Booking, date: String, index: Int){
Row(
modifier = Modifier
.fillMaxWidth()
@@ -270,7 +257,7 @@ private fun Booking(booking: Booking, index: Int){
) {
Column(modifier = Modifier.weight(1f)) {
Text(
text = booking.time,
text = date,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.testTag(TestIds.Main.ITEM_DATE)
@@ -296,8 +283,3 @@ private fun Booking(booking: Booking, index: Int){
modifier = Modifier.padding(start = 16.dp)
)
}
data class Booking(
val time: String,
val place: String
)

View File

@@ -1,5 +1,6 @@
package ru.myitschool.work.ui.screen.main
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -7,25 +8,49 @@ 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.UserData
import ru.myitschool.work.data.repo.AuthRepository
import ru.myitschool.work.data.repo.MainRepository
import ru.myitschool.work.domain.main.Fetch
import ru.myitschool.work.domain.main.Logout
class MainViewModel(): ViewModel() {
private val fetch by lazy { Fetch(MainRepository) }
private val logout by lazy { Logout(AuthRepository) }
private val _uiState = MutableStateFlow<MainState>(MainState.Data)
private val _uiState = MutableStateFlow<MainState>(MainState.Loading)
val uiState: StateFlow<MainState> = _uiState.asStateFlow()
private val _actionFlow: MutableSharedFlow<Unit> = MutableSharedFlow()
val actionFlow: SharedFlow<Unit> = _actionFlow
private val _infoFlow: MutableStateFlow<UserData?> = MutableStateFlow(null)
val infoFlow: StateFlow<UserData?> = _infoFlow.asStateFlow()
private val _errorFlow = MutableStateFlow<String>("")
val errorFlow: StateFlow<String> = _errorFlow.asStateFlow()
fun onIntent(intent: MainIntent) {
when (intent) {
is MainIntent.Fetch -> Unit
is MainIntent.Fetch -> viewModelScope.launch {
_uiState.update { MainState.Loading }
fetch.invoke().fold(
onSuccess = { success ->
_infoFlow.update { success }
_uiState.update { MainState.Data }
},
onFailure = { failure ->
Log.d(failure.message, "")
_uiState.update { MainState.Error }
_errorFlow.update { failure.message.toString() }
}
)
}
is MainIntent.Logout -> {
viewModelScope.launch {
logout.invoke()
}
}
is MainIntent.NewBooking -> {
viewModelScope.launch {
_actionFlow.emit(Unit)