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 package ru.myitschool.work.data.source
import android.util.Log
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.engine.cio.CIO import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.request.get import io.ktor.client.request.get
@@ -11,6 +13,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import ru.myitschool.work.core.Constants import ru.myitschool.work.core.Constants
import ru.myitschool.work.data.models.Booking
import ru.myitschool.work.data.models.UserData
object NetworkDataSource { object NetworkDataSource {
private val client by lazy { private val client by lazy {
@@ -39,4 +43,14 @@ object NetworkDataSource {
} }
private fun getUrl(code: String, targetUrl: String) = "${Constants.HOST}/api/$code$targetUrl" 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 androidx.navigation.NavController
import coil3.compose.rememberAsyncImagePainter import coil3.compose.rememberAsyncImagePainter
import ru.myitschool.work.core.TestIds 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.BookScreenDestination
import ru.myitschool.work.ui.nav.MainScreenDestination import ru.myitschool.work.ui.nav.MainScreenDestination
@@ -59,41 +60,24 @@ fun MainScreen(
navController: NavController navController: NavController
) { ) {
val state by viewModel.uiState.collectAsState() val state by viewModel.uiState.collectAsState()
val err by viewModel.errorFlow.collectAsState()
val bookings = listOf( val info by viewModel.infoFlow.collectAsState()
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)")
)
when (val currentState = state) { when (val currentState = state) {
is MainState.Error -> { is MainState.Error -> {
Column (
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text( Text(
text = "TEST_ERROR", text = err,
modifier = Modifier.testTag(TestIds.Main.ERROR), modifier = Modifier.testTag(TestIds.Main.ERROR),
color = MaterialTheme.colorScheme.error color = MaterialTheme.colorScheme.error
) )
IconButton( IconButton(
onClick = { viewModel.onIntent(MainIntent.Fetch) }, onClick = { viewModel.onIntent(MainIntent.Fetch) },
modifier = Modifier modifier = Modifier
.size(24.dp) .size(32.dp)
.aspectRatio(1f) .aspectRatio(1f)
.testTag(TestIds.Main.REFRESH_BUTTON), .testTag(TestIds.Main.REFRESH_BUTTON),
enabled = true, enabled = true,
@@ -106,6 +90,7 @@ fun MainScreen(
) )
} }
} }
}
is MainState.Loading -> { is MainState.Loading -> {
Column( Column(
@@ -117,6 +102,7 @@ fun MainScreen(
} }
} }
is MainState.Data -> { is MainState.Data -> {
info?.let {
Column ( Column (
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) { ) {
@@ -227,8 +213,8 @@ fun MainScreen(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
) { ) {
itemsIndexed(bookings) { index, booking -> itemsIndexed(info!!.booking.entries.toList()) { index, booking ->
Booking(booking = booking, index = index) Booking(booking = booking.value, date = booking.key, index = index)
} }
} }
} }
@@ -248,6 +234,7 @@ fun MainScreen(
} }
} }
} }
}
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
viewModel.onIntent(MainIntent.Fetch) viewModel.onIntent(MainIntent.Fetch)
@@ -259,7 +246,7 @@ fun MainScreen(
} }
@Composable @Composable
private fun Booking(booking: Booking, index: Int){ private fun Booking(booking: Booking, date: String, index: Int){
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -270,7 +257,7 @@ private fun Booking(booking: Booking, index: Int){
) { ) {
Column(modifier = Modifier.weight(1f)) { Column(modifier = Modifier.weight(1f)) {
Text( Text(
text = booking.time, text = date,
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.testTag(TestIds.Main.ITEM_DATE) modifier = Modifier.testTag(TestIds.Main.ITEM_DATE)
@@ -296,8 +283,3 @@ private fun Booking(booking: Booking, index: Int){
modifier = Modifier.padding(start = 16.dp) 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 package ru.myitschool.work.ui.screen.main
import android.util.Log
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
@@ -7,25 +8,49 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import ru.myitschool.work.data.models.UserData
import ru.myitschool.work.data.repo.AuthRepository 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 import ru.myitschool.work.domain.main.Logout
class MainViewModel(): ViewModel() { class MainViewModel(): ViewModel() {
private val fetch by lazy { Fetch(MainRepository) }
private val logout by lazy { Logout(AuthRepository) } 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() val uiState: StateFlow<MainState> = _uiState.asStateFlow()
private val _actionFlow: MutableSharedFlow<Unit> = MutableSharedFlow() private val _actionFlow: MutableSharedFlow<Unit> = MutableSharedFlow()
val actionFlow: SharedFlow<Unit> = _actionFlow 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) { fun onIntent(intent: MainIntent) {
when (intent) { 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 -> { is MainIntent.Logout -> {
viewModelScope.launch { viewModelScope.launch {
logout.invoke() logout.invoke()
} }
} }
is MainIntent.NewBooking -> { is MainIntent.NewBooking -> {
viewModelScope.launch { viewModelScope.launch {
_actionFlow.emit(Unit) _actionFlow.emit(Unit)