feat: Implement airport data fetching and display

This commit introduces the functionality to fetch airport data from the Ryanair API and display it in the UI.

Key changes:
- Added `AirportInfoModel` interface and its implementation `AirportInfoModelImpl` to represent airport data in the data layer.
- Created `AirportInfoModelMapper` to map `AirportResponse` to `AirportInfoModel`.
- Set the base URL for Retrofit in `NetworkModule` to `https://services-api.ryanair.com`.
- Added new Gradle modules: `model:data:shared`, `model:datasource:airports`, `model:datasource:shared`, and `domain:search`.
- Implemented `CacheImpl` in `model:data:shared` for generic caching.
- Defined `ObserveAirportsUseCase` interface in `domain:search` to observe airport data.
- Added Detekt configuration for the `domain:search` and `model:datasource:shared` modules.
- Created `AirportsDatasource` interface and its implementation `AirportsDatasourceImpl` to manage airport data.
- Implemented `AirportInfoDomainMapper` to map `AirportInfoModel` to the domain `AirportInfo`.
- Updated `HomeScreen` to display a list of airports using `LazyColumn`.
- Updated `HomeScreenViewModel` to fetch and expose airport data via `ObserveAirportsUseCase`.
- Added `ObserveAirportsUseCaseModule` to provide the `ObserveAirportsUseCase` implementation.
- Implemented `ObserveAirportsUseCaseImpl` in the repository layer to fetch data from the API and update the datasource.
- Added `kotlinx-datetime` dependency.
- Created `AirportsDatasourceModule` to provide `AirportsDatasource` and its implementation.
- Added `CacheObservers.kt` with a utility function `loadData` to handle cache observation and data loading logic.
- Updated dependencies in `model:data:simple`, `model:repository`, and `ui:home` build files.
- Updated `settings.gradle.kts` to include new modules.
- Defined `Cache` interface in `model:datasource:shared`.
This commit is contained in:
2025-06-13 21:45:55 +02:00
parent 0fb82d57dd
commit fcc84dc728
26 changed files with 437 additions and 5 deletions

View File

@ -10,6 +10,7 @@ android {
dependencies {
implementation(projects.ui.designsystem)
implementation(projects.domain.search)
implementation(libs.androidx.hilt.navigation.compose)
implementation(libs.timber)

View File

@ -1,7 +1,14 @@
package dev.adriankuta.flights.ui.home
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
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
@ -9,12 +16,31 @@ import dev.adriankuta.flights.ui.designsystem.theme.PreviewDevices
internal fun HomeScreen(
viewModel: HomeScreenViewModel = hiltViewModel(),
) {
val homeUiState by viewModel.uiState.collectAsStateWithLifecycle()
HomeScreen(
airports = homeUiState.airports
)
}
@Composable
private fun HomeScreen(
airports: List<AirportInfo>,
modifier: Modifier = Modifier,
) {
LazyColumn {
items(airports) { airport ->
Text(airport.name)
}
}
}
@PreviewDevices
@Composable
private fun HomeScreenPreview() {
FlightsTheme {
HomeScreen()
HomeScreen(
airports = listOf()
)
}
}

View File

@ -1,8 +1,47 @@
package dev.adriankuta.flights.ui.home
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
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() : ViewModel()
class HomeScreenViewModel @Inject constructor(
private val observeAirportsUseCase: ObserveAirportsUseCase
) : ViewModel() {
internal val uiState = homeUiState(
useCase = observeAirportsUseCase
)
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = HomeUiState(),
)
}
private fun homeUiState(
useCase: ObserveAirportsUseCase
): Flow<HomeUiState> {
return flow {
delay(15.seconds)
emit("")
}.flatMapLatest { useCase() }.map {
HomeUiState(it.orEmpty())
}
}
internal data class HomeUiState(
val airports: List<AirportInfo> = emptyList()
)