Implement core:config module

This commit is contained in:
Shcherbatykh Oleg
2026-02-18 14:02:04 +03:00
parent 341d128099
commit 705b689c58
30 changed files with 392 additions and 41 deletions

View File

@@ -0,0 +1,38 @@
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.kapt)
alias(libs.plugins.hilt)
alias(libs.plugins.kotlin.serialization)
}
android {
namespace = "ru.fincode.tsudesk.core.config"
compileSdk = libs.versions.compileSdk.get().toInt()
defaultConfig {
minSdk = libs.versions.minSdk.get().toInt()
}
val jvm = JavaVersion.toVersion(libs.versions.jvmTarget.get())
compileOptions {
sourceCompatibility = jvm
targetCompatibility = jvm
}
kotlinOptions {
jvmTarget = jvm.toString()
}
}
kapt {
correctErrorTypes = true
}
dependencies {
kapt(libs.hilt.compiler)
implementation(libs.hilt.android)
// Kotlin Serialization
implementation(libs.kotlinx.serialization.json)
implementation(project(":core:network"))
implementation(project(":core:common"))
}

View File

@@ -0,0 +1,63 @@
package ru.fincode.tsudesk.core.config.data
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import ru.fincode.tsudesk.core.common.model.DataResult
import ru.fincode.tsudesk.core.config.data.datasource.ConfigRemoteDataSource
import ru.fincode.tsudesk.core.config.domain.ConfigRepository
import ru.fincode.tsudesk.core.config.domain.model.AppConfig
import ru.fincode.tsudesk.core.network.model.NetworkResult
import javax.inject.Inject
class ConfigRepositoryImpl @Inject constructor(
private val remoteDataSource: ConfigRemoteDataSource
) : ConfigRepository {
private val defaultConfig = AppConfig()
override suspend fun fetchConfig(): DataResult<AppConfig> {
return when (val res = remoteDataSource.load()) {
is NetworkResult.Success -> {
val dto = res.data
DataResult.Data(
data = AppConfig(
newsEnabled = dto.newsEnabled ?: defaultConfig.newsEnabled,
scheduleEnabled = dto.scheduleEnabled ?: defaultConfig.scheduleEnabled,
gradesEnabled = dto.gradesEnabled ?: defaultConfig.gradesEnabled
),
refreshedFromNetwork = true
)
}
is NetworkResult.Error -> {
DataResult.Error(
error = res.error.toAppError(), data = defaultConfig
)
}
}
}
override fun getConfig(): Flow<DataResult<AppConfig>> = flow {
when (val result = remoteDataSource.load()) {
is NetworkResult.Success -> {
val dto = result.data
val appConfig = AppConfig(
newsEnabled = dto.newsEnabled ?: defaultConfig.newsEnabled,
scheduleEnabled = dto.scheduleEnabled ?: defaultConfig.scheduleEnabled,
gradesEnabled = dto.gradesEnabled ?: defaultConfig.gradesEnabled
)
emit(DataResult.Data(data = appConfig, refreshedFromNetwork = true))
}
is NetworkResult.Error -> {
emit(
DataResult.Error(
error = result.error.toAppError(),
data = defaultConfig
)
)
}
}
}
}

View File

@@ -0,0 +1,8 @@
package ru.fincode.tsudesk.core.config.data.datasource
import ru.fincode.tsudesk.core.config.data.remote.dto.RemoteConfigDto
import ru.fincode.tsudesk.core.network.model.NetworkResult
interface ConfigRemoteDataSource {
suspend fun load(): NetworkResult<RemoteConfigDto>
}

View File

@@ -0,0 +1,15 @@
package ru.fincode.tsudesk.core.config.data.datasource
import ru.fincode.tsudesk.core.config.data.remote.api.ConfigApi
import ru.fincode.tsudesk.core.config.data.remote.dto.RemoteConfigDto
import ru.fincode.tsudesk.core.network.apiCall
import ru.fincode.tsudesk.core.network.model.NetworkResult
import javax.inject.Inject
class ConfigRemoteDataSourceImpl @Inject constructor(
private val api: ConfigApi
) : ConfigRemoteDataSource {
override suspend fun load(): NetworkResult<RemoteConfigDto> =
apiCall { api.getConfig() }
}

View File

@@ -0,0 +1,7 @@
package ru.fincode.tsudesk.core.config.data.model
data class RemoteConfig(
val newsEnabled: Boolean,
val scheduleEnabled: Boolean,
val gradesEnabled: Boolean
)

View File

@@ -0,0 +1,10 @@
package ru.fincode.tsudesk.core.config.data.remote
object ConfigApiContract {
const val CONFIG_BASE_URL = "https://scherbatykh.ru/app/tsudesk/"
object Path {
const val GET_CONFIG_METHOD = "config.json"
}
}

View File

@@ -0,0 +1,12 @@
package ru.fincode.tsudesk.core.config.data.remote.api
import retrofit2.Response
import retrofit2.http.GET
import ru.fincode.tsudesk.core.config.data.remote.ConfigApiContract.Path.GET_CONFIG_METHOD
import ru.fincode.tsudesk.core.config.data.remote.dto.RemoteConfigDto
interface ConfigApi {
@GET(GET_CONFIG_METHOD)
suspend fun getConfig(): Response<RemoteConfigDto>
}

View File

@@ -0,0 +1,10 @@
package ru.fincode.tsudesk.core.config.data.remote.dto
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class RemoteConfigDto(
val newsEnabled: Boolean? = null,
val scheduleEnabled: Boolean? = null,
val gradesEnabled: Boolean? = null
)

View File

@@ -0,0 +1,35 @@
package ru.fincode.tsudesk.core.config.di
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import ru.fincode.tsudesk.core.config.data.remote.ConfigApiContract.CONFIG_BASE_URL
import ru.fincode.tsudesk.core.network.RetrofitFactory
import ru.fincode.tsudesk.core.config.data.remote.api.ConfigApi
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object ConfigNetworkModule {
@Provides
@Singleton
@ConfigRetrofit
fun provideConfigRetrofit(
factory: RetrofitFactory,
client: OkHttpClient
): Retrofit =
factory.create(
baseUrl = CONFIG_BASE_URL,
client = client
)
@Provides
@Singleton
fun provideConfigApi(
@ConfigRetrofit retrofit: Retrofit
): ConfigApi =
retrofit.create(ConfigApi::class.java)
}

View File

@@ -0,0 +1,28 @@
package ru.fincode.tsudesk.core.config.di
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import ru.fincode.tsudesk.core.config.data.ConfigRepositoryImpl
import ru.fincode.tsudesk.core.config.data.datasource.ConfigRemoteDataSource
import ru.fincode.tsudesk.core.config.data.datasource.ConfigRemoteDataSourceImpl
import ru.fincode.tsudesk.core.config.domain.ConfigRepository
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
abstract class ConfigRepositoryModule {
@Binds
@Singleton
abstract fun bindRemoteDataSource(
impl: ConfigRemoteDataSourceImpl
): ConfigRemoteDataSource
@Binds
@Singleton
abstract fun bindRepository(
impl: ConfigRepositoryImpl
): ConfigRepository
}

View File

@@ -0,0 +1,7 @@
package ru.fincode.tsudesk.core.config.di
import javax.inject.Qualifier
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class ConfigRetrofit

View File

@@ -0,0 +1,10 @@
package ru.fincode.tsudesk.core.config.domain
import kotlinx.coroutines.flow.Flow
import ru.fincode.tsudesk.core.common.model.DataResult
import ru.fincode.tsudesk.core.config.domain.model.AppConfig
interface ConfigRepository {
suspend fun fetchConfig(): DataResult<AppConfig>
fun getConfig(): Flow<DataResult<AppConfig>>
}

View File

@@ -0,0 +1,7 @@
package ru.fincode.tsudesk.core.config.domain.model
data class AppConfig(
val newsEnabled: Boolean = false,
val scheduleEnabled: Boolean = false,
val gradesEnabled: Boolean = false
)

View File

@@ -0,0 +1,13 @@
package ru.fincode.tsudesk.core.config.domain.usecase
import kotlinx.coroutines.flow.Flow
import ru.fincode.tsudesk.core.common.model.DataResult
import ru.fincode.tsudesk.core.config.domain.ConfigRepository
import ru.fincode.tsudesk.core.config.domain.model.AppConfig
import javax.inject.Inject
class GetConfigUseCase @Inject constructor(
private val repository: ConfigRepository
) {
suspend operator fun invoke(): DataResult<AppConfig> = repository.fetchConfig()
}