diff --git a/domain/search/build.gradle.kts b/domain/search/build.gradle.kts new file mode 100644 index 0000000..ec13401 --- /dev/null +++ b/domain/search/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + alias(libs.plugins.flights.android.library) + alias(libs.plugins.flights.android.library.hilt) +} + +android { + namespace = "dev.adriankuta.flights.domain.search" +} + +dependencies { + api(projects.domain.types) + implementation(libs.timber) +} \ No newline at end of file diff --git a/domain/search/config/detekt/detekt.yml b/domain/search/config/detekt/detekt.yml new file mode 100644 index 0000000..809b757 --- /dev/null +++ b/domain/search/config/detekt/detekt.yml @@ -0,0 +1,10 @@ +# Deviations from defaults +formatting: + TrailingCommaOnCallSite: + active: true + autoCorrect: true + useTrailingCommaOnCallSite: true + TrailingCommaOnDeclarationSite: + active: true + autoCorrect: true + useTrailingCommaOnDeclarationSite: true \ No newline at end of file diff --git a/domain/search/src/main/kotlin/dev/adriankuta/flights/domain/search/ObserveAirportsUseCase.kt b/domain/search/src/main/kotlin/dev/adriankuta/flights/domain/search/ObserveAirportsUseCase.kt new file mode 100644 index 0000000..0149cfd --- /dev/null +++ b/domain/search/src/main/kotlin/dev/adriankuta/flights/domain/search/ObserveAirportsUseCase.kt @@ -0,0 +1,8 @@ +package dev.adriankuta.flights.domain.search + +import dev.adriankuta.flights.domain.types.AirportInfo +import kotlinx.coroutines.flow.Flow + +fun interface ObserveAirportsUseCase { + operator fun invoke(): Flow?> +} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b97ab6e..fb7272d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,6 +18,7 @@ appUpdateKtx = "2.1.0" appcompat = "1.7.0" converterMoshi = "2.11.0" coreTest = "1.6.1" # https://developer.android.com/jetpack/androidx/releases/test +datetime = "0.6.1" # https://github.com/Kotlin/kotlinx-datetime/releases detekt = "1.23.8" # https://detekt.dev/changelog detektCompose = "0.4.22" # https://github.com/mrmans0n/compose-rules/releases espressoCore = "3.6.1" @@ -84,6 +85,7 @@ kotlin-test = { group = "org.jetbrains.kotlin", name = "kotlin-test", version.re kotlinx-collections-immutable = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version.ref = "immutableCollections" } kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinxCoroutinesAndroid" } kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutinesAndroid" } +kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" } kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } material = { group = "com.google.android.material", name = "material", version.ref = "material" } mockk-android = { module = "io.mockk:mockk-android", version.ref = "mockk" } diff --git a/model/data/api/src/main/kotlin/dev/adriankuta/model/data/api/di/NetworkModule.kt b/model/data/api/src/main/kotlin/dev/adriankuta/model/data/api/di/NetworkModule.kt index adeddd8..5ef405a 100644 --- a/model/data/api/src/main/kotlin/dev/adriankuta/model/data/api/di/NetworkModule.kt +++ b/model/data/api/src/main/kotlin/dev/adriankuta/model/data/api/di/NetworkModule.kt @@ -39,6 +39,7 @@ class NetworkModule { @Provides fun provideRetrofit(client: OkHttpClient, moshi: Moshi): Retrofit = Retrofit.Builder() // TODO("add URL provided in the task instructions") + .baseUrl("https://services-api.ryanair.com") .addConverterFactory(MoshiConverterFactory.create(moshi)) .client(client) .build() diff --git a/model/data/shared/build.gradle.kts b/model/data/shared/build.gradle.kts new file mode 100644 index 0000000..b4067ec --- /dev/null +++ b/model/data/shared/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + alias(libs.plugins.flights.android.library) + alias(libs.plugins.flights.android.library.hilt) +} + +android { + namespace = "dev.adriankuta.flights.model.data.shared" +} + +dependencies { + implementation(projects.model.datasource.shared) + + implementation(libs.timber) +} diff --git a/model/data/shared/src/main/kotlin/dev/adriankuta/flights/model/data/shared/CacheImpl.kt b/model/data/shared/src/main/kotlin/dev/adriankuta/flights/model/data/shared/CacheImpl.kt new file mode 100644 index 0000000..6254b92 --- /dev/null +++ b/model/data/shared/src/main/kotlin/dev/adriankuta/flights/model/data/shared/CacheImpl.kt @@ -0,0 +1,14 @@ +package dev.adriankuta.flights.model.data.shared + +import dev.adriankuta.flights.model.datasource.shared.Cache + +public data class CacheImpl(override val cacheKey: String?, override val data: T?) : + Cache { + public companion object { + public inline fun emptyCache(): CacheImpl = + CacheImpl(null, null) + + public inline fun Cache?.orEmpty(): Cache = + this ?: emptyCache() + } +} \ No newline at end of file diff --git a/model/data/simple/build.gradle.kts b/model/data/simple/build.gradle.kts index 8f55f64..ac5ba95 100644 --- a/model/data/simple/build.gradle.kts +++ b/model/data/simple/build.gradle.kts @@ -9,6 +9,9 @@ android { dependencies { implementation(projects.core.util) + implementation(projects.model.data.api) + implementation(projects.model.data.shared) + implementation(projects.model.datasource.airports) implementation(libs.timber) implementation(libs.gson) diff --git a/model/data/simple/src/main/kotlin/dev/adriankuta/flights/model/data/simple/AirportsDatasourceImpl.kt b/model/data/simple/src/main/kotlin/dev/adriankuta/flights/model/data/simple/AirportsDatasourceImpl.kt new file mode 100644 index 0000000..6573603 --- /dev/null +++ b/model/data/simple/src/main/kotlin/dev/adriankuta/flights/model/data/simple/AirportsDatasourceImpl.kt @@ -0,0 +1,28 @@ +package dev.adriankuta.flights.model.data.simple + +import dev.adriankuta.flights.model.data.shared.CacheImpl +import dev.adriankuta.flights.model.data.simple.mappers.toModel +import dev.adriankuta.flights.model.datasource.airports.AirportsDatasource +import dev.adriankuta.flights.model.datasource.airports.entities.AirportInfoModel +import dev.adriankuta.flights.model.datasource.shared.Cache +import dev.adriankuta.model.data.api.entities.AirportResponse +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +internal class AirportsDatasourceImpl : AirportsDatasource { + + private val _airports = MutableStateFlow(CacheImpl.emptyCache>()) + + override val airports: StateFlow>> = _airports.asStateFlow() + + override suspend fun setAirportsInfo( + airports: List, + cacheKey: String + ) { + _airports.value = CacheImpl( + cacheKey = cacheKey, + data = airports.map { it.toModel() } + ) + } +} diff --git a/model/data/simple/src/main/kotlin/dev/adriankuta/flights/model/data/simple/di/AirportsDatasourceModule.kt b/model/data/simple/src/main/kotlin/dev/adriankuta/flights/model/data/simple/di/AirportsDatasourceModule.kt new file mode 100644 index 0000000..02846fc --- /dev/null +++ b/model/data/simple/src/main/kotlin/dev/adriankuta/flights/model/data/simple/di/AirportsDatasourceModule.kt @@ -0,0 +1,30 @@ +package dev.adriankuta.flights.model.data.simple.di + +import dagger.Binds +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import dev.adriankuta.flights.model.data.simple.AirportsDatasourceImpl +import dev.adriankuta.flights.model.datasource.airports.AirportsDatasource +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +@Suppress("UnnecessaryAbstractClass") +internal abstract class AirportsDatasourceModule { + @Binds + abstract fun bindApi(impl: AirportsDatasourceImpl): AirportsDatasource + + /*@Binds + @IntoSet + abstract fun bindReset(impl: RolesDatasourceImpl): ClearDatasource*/ +} + +@Module +@InstallIn(SingletonComponent::class) +internal class AirportsDatasourceImplModule { + @Provides + @Singleton + fun provide(): AirportsDatasourceImpl = AirportsDatasourceImpl() +} \ No newline at end of file diff --git a/model/data/simple/src/main/kotlin/dev/adriankuta/flights/model/data/simple/mappers/AirportInfoModelMapper.kt b/model/data/simple/src/main/kotlin/dev/adriankuta/flights/model/data/simple/mappers/AirportInfoModelMapper.kt new file mode 100644 index 0000000..9466059 --- /dev/null +++ b/model/data/simple/src/main/kotlin/dev/adriankuta/flights/model/data/simple/mappers/AirportInfoModelMapper.kt @@ -0,0 +1,42 @@ +package dev.adriankuta.flights.model.data.simple.mappers + +import dev.adriankuta.flights.model.datasource.airports.entities.AirportInfoModel +import dev.adriankuta.model.data.api.entities.AirportResponse + +internal fun AirportResponse.toModel(): AirportInfoModel { + return AirportInfoModelImpl( + code = code.orEmpty(), + name = name.orEmpty(), + seoName = seoName.orEmpty(), + isBase = isBase ?: false, + timeZone = timeZone.orEmpty(), + cityCode = city?.code.orEmpty(), + cityName = city?.name.orEmpty(), + macCode = macCity?.macCode.orEmpty(), + regionCode = region?.code.orEmpty(), + regionName = region?.name.orEmpty(), + countryCode = country?.code.orEmpty(), + countryName = country?.name.orEmpty(), + countryCurrencyCode = country?.currencyCode.orEmpty(), + latitude = coordinates?.latitude ?: 0.0, + longitude = coordinates?.longitude ?: 0.0 + ) +} + +private data class AirportInfoModelImpl( + override val code: String, + override val name: String, + override val seoName: String, + override val isBase: Boolean, + override val timeZone: String, + override val cityCode: String, + override val cityName: String, + override val macCode: String, + override val regionCode: String, + override val regionName: String, + override val countryCode: String, + override val countryName: String, + override val countryCurrencyCode: String, + override val latitude: Double, + override val longitude: Double +) : AirportInfoModel diff --git a/model/datasource/airports/build.gradle.kts b/model/datasource/airports/build.gradle.kts new file mode 100644 index 0000000..afd96bd --- /dev/null +++ b/model/datasource/airports/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + alias(libs.plugins.flights.android.library) + alias(libs.plugins.flights.android.library.hilt) +} + +android { + namespace = "dev.adriankuta.flights.model.datasource.airports" +} + +dependencies { + api(projects.model.datasource.shared) + implementation(projects.model.data.api) + + implementation(libs.timber) +} diff --git a/model/datasource/airports/src/main/kotlin/dev/adriankuta/flights/model/datasource/airports/AirportsDatasource.kt b/model/datasource/airports/src/main/kotlin/dev/adriankuta/flights/model/datasource/airports/AirportsDatasource.kt new file mode 100644 index 0000000..8b5c190 --- /dev/null +++ b/model/datasource/airports/src/main/kotlin/dev/adriankuta/flights/model/datasource/airports/AirportsDatasource.kt @@ -0,0 +1,13 @@ +package dev.adriankuta.flights.model.datasource.airports + +import dev.adriankuta.flights.model.datasource.airports.entities.AirportInfoModel +import dev.adriankuta.flights.model.datasource.shared.Cache +import dev.adriankuta.model.data.api.entities.AirportResponse +import kotlinx.coroutines.flow.StateFlow + +interface AirportsDatasource { + + val airports: StateFlow>> + + suspend fun setAirportsInfo(airports: List, cacheKey: String) +} diff --git a/model/datasource/airports/src/main/kotlin/dev/adriankuta/flights/model/datasource/airports/entities/AirportInfoModel.kt b/model/datasource/airports/src/main/kotlin/dev/adriankuta/flights/model/datasource/airports/entities/AirportInfoModel.kt new file mode 100644 index 0000000..54f0d67 --- /dev/null +++ b/model/datasource/airports/src/main/kotlin/dev/adriankuta/flights/model/datasource/airports/entities/AirportInfoModel.kt @@ -0,0 +1,29 @@ +package dev.adriankuta.flights.model.datasource.airports.entities + +interface AirportInfoModel { + val code: String + val name: String + val seoName: String + val isBase: Boolean + val timeZone: String + + // City properties + val cityCode: String + val cityName: String + + // MacCity properties + val macCode: String + + // Region properties + val regionCode: String + val regionName: String + + // Country properties + val countryCode: String + val countryName: String + val countryCurrencyCode: String + + // Coordinates properties + val latitude: Double + val longitude: Double +} diff --git a/model/datasource/shared/build.gradle.kts b/model/datasource/shared/build.gradle.kts new file mode 100644 index 0000000..1ca54e4 --- /dev/null +++ b/model/datasource/shared/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + alias(libs.plugins.flights.android.library) + alias(libs.plugins.flights.android.library.hilt) +} + +android { + namespace = "dev.adriankuta.flights.model.datasource.shared" +} + +dependencies { + implementation(libs.timber) +} diff --git a/model/datasource/shared/config/detekt/detekt.yml b/model/datasource/shared/config/detekt/detekt.yml new file mode 100644 index 0000000..809b757 --- /dev/null +++ b/model/datasource/shared/config/detekt/detekt.yml @@ -0,0 +1,10 @@ +# Deviations from defaults +formatting: + TrailingCommaOnCallSite: + active: true + autoCorrect: true + useTrailingCommaOnCallSite: true + TrailingCommaOnDeclarationSite: + active: true + autoCorrect: true + useTrailingCommaOnDeclarationSite: true \ No newline at end of file diff --git a/model/datasource/shared/src/main/kotlin/dev/adriankuta/flights/model/datasource/shared/Cache.kt b/model/datasource/shared/src/main/kotlin/dev/adriankuta/flights/model/datasource/shared/Cache.kt new file mode 100644 index 0000000..dae808e --- /dev/null +++ b/model/datasource/shared/src/main/kotlin/dev/adriankuta/flights/model/datasource/shared/Cache.kt @@ -0,0 +1,6 @@ +package dev.adriankuta.flights.model.datasource.shared; + +public interface Cache { + public val cacheKey: String? + public val data: T? +} \ No newline at end of file diff --git a/model/repository/build.gradle.kts b/model/repository/build.gradle.kts index f339b47..ea7838f 100644 --- a/model/repository/build.gradle.kts +++ b/model/repository/build.gradle.kts @@ -8,7 +8,12 @@ android { } dependencies { - implementation(libs.timber) + implementation(projects.domain.search) + implementation(projects.model.data.api) + implementation(projects.model.datasource.airports) - testImplementation("io.mockk:mockk:1.13.8") + implementation(libs.timber) + implementation(libs.kotlinx.datetime) + + testImplementation(libs.mockk.android) } diff --git a/model/repository/src/main/kotlin/dev/adriankuta/flights/model/repository/di/ObserveAirportsUseCaseModule.kt b/model/repository/src/main/kotlin/dev/adriankuta/flights/model/repository/di/ObserveAirportsUseCaseModule.kt new file mode 100644 index 0000000..1d692bf --- /dev/null +++ b/model/repository/src/main/kotlin/dev/adriankuta/flights/model/repository/di/ObserveAirportsUseCaseModule.kt @@ -0,0 +1,19 @@ +package dev.adriankuta.flights.model.repository.di + +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import dev.adriankuta.flights.domain.search.ObserveAirportsUseCase +import dev.adriankuta.flights.model.repository.usecases.ObserveAirportsUseCaseImpl + +@Module +@InstallIn(SingletonComponent::class) +@Suppress("UnnecessaryAbstractClass") +internal abstract class ObserveAirportsUseCaseModule { + + @Binds + abstract fun bind( + observeAirportsUseCaseImpl: ObserveAirportsUseCaseImpl + ): ObserveAirportsUseCase +} diff --git a/model/repository/src/main/kotlin/dev/adriankuta/flights/model/repository/mappers/AirportInfoDomainMapper.kt b/model/repository/src/main/kotlin/dev/adriankuta/flights/model/repository/mappers/AirportInfoDomainMapper.kt new file mode 100644 index 0000000..6a84520 --- /dev/null +++ b/model/repository/src/main/kotlin/dev/adriankuta/flights/model/repository/mappers/AirportInfoDomainMapper.kt @@ -0,0 +1,39 @@ +package dev.adriankuta.flights.model.repository.mappers + +import dev.adriankuta.flights.domain.types.AirportInfo +import dev.adriankuta.flights.domain.types.City +import dev.adriankuta.flights.domain.types.Coordinates +import dev.adriankuta.flights.domain.types.Country +import dev.adriankuta.flights.domain.types.MacCity +import dev.adriankuta.flights.domain.types.Region +import dev.adriankuta.flights.model.datasource.airports.entities.AirportInfoModel + +internal fun AirportInfoModel.toDomain(): AirportInfo { + return AirportInfo( + code = code, + name = name, + seoName = seoName, + isBase = isBase, + timeZone = timeZone, + city = City( + code = cityCode, + name = cityName + ), + macCity = MacCity( + macCode = macCode + ), + region = Region( + code = regionCode, + name = regionName + ), + country = Country( + code = countryCode, + name = countryName, + currencyCode = countryCurrencyCode + ), + coordinates = Coordinates( + latitude = latitude, + longitude = longitude + ) + ) +} diff --git a/model/repository/src/main/kotlin/dev/adriankuta/flights/model/repository/usecases/ObserveAirportsUseCaseImpl.kt b/model/repository/src/main/kotlin/dev/adriankuta/flights/model/repository/usecases/ObserveAirportsUseCaseImpl.kt new file mode 100644 index 0000000..716aba2 --- /dev/null +++ b/model/repository/src/main/kotlin/dev/adriankuta/flights/model/repository/usecases/ObserveAirportsUseCaseImpl.kt @@ -0,0 +1,29 @@ +package dev.adriankuta.flights.model.repository.usecases + +import dev.adriankuta.flights.domain.search.ObserveAirportsUseCase +import dev.adriankuta.flights.domain.types.AirportInfo +import dev.adriankuta.flights.model.datasource.airports.AirportsDatasource +import dev.adriankuta.flights.model.datasource.airports.entities.AirportInfoModel +import dev.adriankuta.flights.model.repository.mappers.toDomain +import dev.adriankuta.flights.model.repository.utilities.loadData +import dev.adriankuta.model.data.api.AirportService +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +internal class ObserveAirportsUseCaseImpl @Inject constructor( + private val airportService: AirportService, + private val airportsDatasource: AirportsDatasource, +) : ObserveAirportsUseCase { + override fun invoke(): Flow?> = loadData( + onCacheInvalidated = { + val response = airportService.getAirports("PL") + airportsDatasource.setAirportsInfo(response, "PL") + }, + observeCache = { + airportsDatasource.airports + }, + mapToDomain = { + it.orEmpty().map(AirportInfoModel::toDomain) + }, + ) +} diff --git a/model/repository/src/main/kotlin/dev/adriankuta/flights/model/repository/utilities/CacheObservers.kt b/model/repository/src/main/kotlin/dev/adriankuta/flights/model/repository/utilities/CacheObservers.kt new file mode 100644 index 0000000..b614f9e --- /dev/null +++ b/model/repository/src/main/kotlin/dev/adriankuta/flights/model/repository/utilities/CacheObservers.kt @@ -0,0 +1,21 @@ +package dev.adriankuta.flights.model.repository.utilities + +import dev.adriankuta.flights.model.datasource.shared.Cache +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map +import kotlinx.datetime.Clock + +internal fun loadData( + onCacheInvalidated: suspend (cacheKey: String) -> Unit, + observeCache: () -> Flow>, + mapToDomain: (T?) -> R?, +): Flow { + return observeCache().distinctUntilChanged().map { + if (it.cacheKey == null) { + val refreshTime = Clock.System.now() + onCacheInvalidated(it.cacheKey ?: refreshTime.toEpochMilliseconds().toString()) + } + mapToDomain(it.data) + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index f2496d9..47d4459 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -24,11 +24,14 @@ rootProject.name = "Flights" enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") include(":app") include(":core:util") +include(":domain:search") include(":domain:types") include(":model:data:api") include(":model:data:room") +include(":model:data:shared") include(":model:data:simple") -include(":model:datasource:characters") +include(":model:datasource:airports") +include(":model:datasource:shared") include(":model:repository") include(":ui:designsystem") include(":ui:home") diff --git a/ui/home/build.gradle.kts b/ui/home/build.gradle.kts index 5bc10dc..b1e9d99 100644 --- a/ui/home/build.gradle.kts +++ b/ui/home/build.gradle.kts @@ -10,6 +10,7 @@ android { dependencies { implementation(projects.ui.designsystem) + implementation(projects.domain.search) implementation(libs.androidx.hilt.navigation.compose) implementation(libs.timber) diff --git a/ui/home/src/main/kotlin/dev/adriankuta/flights/ui/home/HomeScreen.kt b/ui/home/src/main/kotlin/dev/adriankuta/flights/ui/home/HomeScreen.kt index b4d2394..dcc9e79 100644 --- a/ui/home/src/main/kotlin/dev/adriankuta/flights/ui/home/HomeScreen.kt +++ b/ui/home/src/main/kotlin/dev/adriankuta/flights/ui/home/HomeScreen.kt @@ -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, + modifier: Modifier = Modifier, +) { + LazyColumn { + items(airports) { airport -> + Text(airport.name) + } + } } @PreviewDevices @Composable private fun HomeScreenPreview() { FlightsTheme { - HomeScreen() + HomeScreen( + airports = listOf() + ) } } diff --git a/ui/home/src/main/kotlin/dev/adriankuta/flights/ui/home/HomeScreenViewModel.kt b/ui/home/src/main/kotlin/dev/adriankuta/flights/ui/home/HomeScreenViewModel.kt index 03a810b..922394c 100644 --- a/ui/home/src/main/kotlin/dev/adriankuta/flights/ui/home/HomeScreenViewModel.kt +++ b/ui/home/src/main/kotlin/dev/adriankuta/flights/ui/home/HomeScreenViewModel.kt @@ -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 { + + return flow { + delay(15.seconds) + emit("") + }.flatMapLatest { useCase() }.map { + HomeUiState(it.orEmpty()) + } +} + +internal data class HomeUiState( + val airports: List = emptyList() +)