mirror of
https://github.com/AdrianKuta/android-challange-adrian-kuta.git
synced 2025-07-02 05:47:59 +02:00
feat: Introduce Result type and update UI states
This commit introduces a generic `Result` sealed interface to represent success, error, and loading states for asynchronous operations. It also updates the home screen UI and ViewModel to utilize this `Result` type for displaying different states. Key changes: - Added `Result.kt` in `core:util` defining the `Result` sealed interface and an `asResult()` Flow extension. - Updated `HomeScreenViewModel` to use `asResult()` and map the `ObserveAirportsUseCase` output to `HomeUiState` (Loading, Success, Error). - Modified `HomeUiState` to be a sealed interface with `Loading`, `Success`, and `Error` subtypes. - Updated `HomeScreen` to handle different `HomeUiState` values and display appropriate UI (Loading text, Error text, or list of airports). - Added `core.util` dependency to `ui:home`. - Updated Moshi and Retrofit Moshi converter dependencies in `model/data/api/build.gradle.kts` and `gradle/libs.versions.toml`. - Added `moshi` and `moshiKotlinCodegen` versions to `gradle/libs.versions.toml`. - Removed `converter-moshi` and added `retrofit-converter-moshi` in `gradle/libs.versions.toml`. - Added `ksp(libs.moshi.kotlin.codegen)` to `model/data/api/build.gradle.kts`. - Added `INTERNET` permission to `app/src/main/AndroidManifest.xml`. - Updated `ObserveAirportsUseCaseImpl` to use `cacheKey` when setting airports info and to fetch airports for "pl" (lowercase). - Added `.editorconfig` file with Kotlin trailing comma settings.
This commit is contained in:
@ -9,6 +9,7 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.core.util)
|
||||
implementation(projects.ui.designsystem)
|
||||
implementation(projects.domain.search)
|
||||
|
||||
|
@ -8,7 +8,6 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import dev.adriankuta.flights.domain.types.AirportInfo
|
||||
import dev.adriankuta.flights.ui.designsystem.theme.FlightsTheme
|
||||
import dev.adriankuta.flights.ui.designsystem.theme.PreviewDevices
|
||||
|
||||
@ -19,18 +18,24 @@ internal fun HomeScreen(
|
||||
val homeUiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
HomeScreen(
|
||||
airports = homeUiState.airports
|
||||
uiState = homeUiState,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun HomeScreen(
|
||||
airports: List<AirportInfo>,
|
||||
uiState: HomeUiState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
LazyColumn {
|
||||
items(airports) { airport ->
|
||||
Text(airport.name)
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
) {
|
||||
when (uiState) {
|
||||
is HomeUiState.Error -> item { Text("Error") }
|
||||
HomeUiState.Loading -> item { Text("Loading") }
|
||||
is HomeUiState.Success -> items(uiState.airports) { airport ->
|
||||
Text(airport.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -40,7 +45,7 @@ private fun HomeScreen(
|
||||
private fun HomeScreenPreview() {
|
||||
FlightsTheme {
|
||||
HomeScreen(
|
||||
airports = listOf()
|
||||
uiState = HomeUiState.Loading,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -3,17 +3,15 @@ package dev.adriankuta.flights.ui.home
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dev.adriankuta.flights.core.util.Result
|
||||
import dev.adriankuta.flights.core.util.asResult
|
||||
import dev.adriankuta.flights.domain.search.ObserveAirportsUseCase
|
||||
import dev.adriankuta.flights.domain.types.AirportInfo
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import javax.inject.Inject
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@HiltViewModel
|
||||
class HomeScreenViewModel @Inject constructor(
|
||||
@ -26,7 +24,7 @@ class HomeScreenViewModel @Inject constructor(
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(5_000),
|
||||
initialValue = HomeUiState(),
|
||||
initialValue = HomeUiState.Loading,
|
||||
)
|
||||
}
|
||||
|
||||
@ -34,14 +32,19 @@ private fun homeUiState(
|
||||
useCase: ObserveAirportsUseCase
|
||||
): Flow<HomeUiState> {
|
||||
|
||||
return flow {
|
||||
delay(15.seconds)
|
||||
emit("")
|
||||
}.flatMapLatest { useCase() }.map {
|
||||
HomeUiState(it.orEmpty())
|
||||
}
|
||||
return useCase()
|
||||
.asResult()
|
||||
.map { result ->
|
||||
when (result) {
|
||||
is Result.Error -> HomeUiState.Error(result.exception)
|
||||
Result.Loading -> HomeUiState.Loading
|
||||
is Result.Success -> HomeUiState.Success(result.data.orEmpty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal data class HomeUiState(
|
||||
val airports: List<AirportInfo> = emptyList()
|
||||
)
|
||||
internal sealed interface HomeUiState {
|
||||
data object Loading : HomeUiState
|
||||
data class Success(val airports: List<AirportInfo>) : HomeUiState
|
||||
data class Error(val exception: Throwable) : HomeUiState
|
||||
}
|
||||
|
Reference in New Issue
Block a user