diff --git a/app/ios-combined/src/commonMain/kotlin/club/nito/ios/combined/KmpEntryPoint.kt b/app/ios-combined/src/commonMain/kotlin/club/nito/ios/combined/KmpEntryPoint.kt index 6fbc03dc..d28c70cf 100644 --- a/app/ios-combined/src/commonMain/kotlin/club/nito/ios/combined/KmpEntryPoint.kt +++ b/app/ios-combined/src/commonMain/kotlin/club/nito/ios/combined/KmpEntryPoint.kt @@ -5,6 +5,7 @@ import club.nito.app.shared.di.featureModules import club.nito.app.shared.di.nitoDateFormatterModule import club.nito.app.shared.di.userMessageStateHolderModule import club.nito.core.data.di.dataModule +import club.nito.core.datastore.di.dataStoreModule import club.nito.core.domain.di.useCaseModule import club.nito.core.network.di.remoteDataSourceModule import club.nito.core.network.di.supabaseClientModule @@ -30,6 +31,7 @@ class KmpEntryPoint { supabaseClientModule, remoteDataSourceModule, // fakeRemoteDataSourceModule, + dataStoreModule, dataModule, useCaseModule, diff --git a/app/shared/build.gradle.kts b/app/shared/build.gradle.kts index ada5d27b..c0047acc 100644 --- a/app/shared/build.gradle.kts +++ b/app/shared/build.gradle.kts @@ -15,6 +15,7 @@ kotlin { dependencies { implementation(projects.core.common) implementation(projects.core.model) + implementation(projects.core.datastore) implementation(projects.core.data) implementation(projects.core.network) implementation(projects.core.domain) diff --git a/app/shared/src/commonMain/kotlin/club/nito/app/shared/NitoApp.kt b/app/shared/src/commonMain/kotlin/club/nito/app/shared/NitoApp.kt index 670b71dc..7090dc8c 100644 --- a/app/shared/src/commonMain/kotlin/club/nito/app/shared/NitoApp.kt +++ b/app/shared/src/commonMain/kotlin/club/nito/app/shared/NitoApp.kt @@ -11,6 +11,7 @@ import club.nito.app.shared.di.featureModules import club.nito.app.shared.di.nitoDateFormatterModule import club.nito.app.shared.di.userMessageStateHolderModule import club.nito.core.data.di.dataModule +import club.nito.core.datastore.di.dataStoreModule import club.nito.core.designsystem.theme.NitoTheme import club.nito.core.domain.di.useCaseModule import club.nito.core.network.di.remoteDataSourceModule @@ -41,6 +42,7 @@ fun NitoApp( supabaseClientModule, remoteDataSourceModule, // fakeRemoteDataSourceModule, + dataStoreModule, dataModule, useCaseModule, diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index f622e401..e208a413 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -16,6 +16,7 @@ kotlin { dependencies { implementation(libs.kotlinxCoroutinesCore) implementation(libs.kotlinxDatetime) + implementation(libs.kotlinSerializationJson) } } iosMain { diff --git a/core/common/src/commonMain/kotlin/club/nito/core/common/NitoJsonSettings.kt b/core/common/src/commonMain/kotlin/club/nito/core/common/NitoJsonSettings.kt new file mode 100644 index 00000000..24815a66 --- /dev/null +++ b/core/common/src/commonMain/kotlin/club/nito/core/common/NitoJsonSettings.kt @@ -0,0 +1,20 @@ +package club.nito.core.common + +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonNamingStrategy + +public val nitoJsonSettings: Json = Json { + encodeDefaults = true + isLenient = true + allowSpecialFloatingPointValues = true + allowStructuredMapKeys = true + prettyPrint = false + useArrayPolymorphism = false + ignoreUnknownKeys = true + coerceInputValues = true + useAlternativeNames = false + + @OptIn(ExperimentalSerializationApi::class) + namingStrategy = JsonNamingStrategy.SnakeCase +} diff --git a/core/data/build.gradle.kts b/core/data/build.gradle.kts index e3df1b52..af909e35 100644 --- a/core/data/build.gradle.kts +++ b/core/data/build.gradle.kts @@ -17,6 +17,7 @@ kotlin { implementation(projects.core.common) implementation(projects.core.model) implementation(projects.core.database) + implementation(projects.core.datastore) implementation(projects.core.network) implementation(libs.kotlinxCoroutinesCore) diff --git a/core/datastore/build.gradle.kts b/core/datastore/build.gradle.kts new file mode 100644 index 00000000..102b044a --- /dev/null +++ b/core/datastore/build.gradle.kts @@ -0,0 +1,31 @@ +plugins { + id("nito.primitive.kmp") + id("nito.primitive.kmp.android") + id("nito.primitive.kmp.ios") + id("nito.primitive.kmp.js") + id("nito.primitive.detekt") +} + +android.namespace = "club.nito.core.datastore" + +kotlin { + explicitApi() + + sourceSets { + commonMain { + dependencies { + implementation(projects.core.common) + implementation(projects.core.model) + + implementation(libs.kotlinxCoroutinesCore) + implementation(libs.kotlinSerializationJson) + + implementation(libs.koin) + implementation(libs.kermit) + + implementation(libs.multiplatformSettingsNoArg) + implementation(libs.multiplatformSettingsCoroutines) + } + } + } +} diff --git a/core/datastore/src/androidMain/kotlin/club/nito/core/datastore/.gitkeep b/core/datastore/src/androidMain/kotlin/club/nito/core/datastore/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/core/datastore/src/commonMain/kotlin/club/nito/core/datastore/DataStore.kt b/core/datastore/src/commonMain/kotlin/club/nito/core/datastore/DataStore.kt new file mode 100644 index 00000000..c3fb59a5 --- /dev/null +++ b/core/datastore/src/commonMain/kotlin/club/nito/core/datastore/DataStore.kt @@ -0,0 +1,3 @@ +package club.nito.core.datastore + +public sealed interface DataStore diff --git a/core/datastore/src/commonMain/kotlin/club/nito/core/datastore/SettingsDataStore.kt b/core/datastore/src/commonMain/kotlin/club/nito/core/datastore/SettingsDataStore.kt new file mode 100644 index 00000000..3ff5f970 --- /dev/null +++ b/core/datastore/src/commonMain/kotlin/club/nito/core/datastore/SettingsDataStore.kt @@ -0,0 +1,14 @@ +package club.nito.core.datastore + +import com.russhwolf.settings.ExperimentalSettingsApi +import com.russhwolf.settings.Settings +import com.russhwolf.settings.coroutines.SuspendSettings +import com.russhwolf.settings.coroutines.toSuspendSettings +import kotlinx.serialization.json.Json + +public class SettingsDataStore( + private val settings: Settings, + @OptIn(ExperimentalSettingsApi::class) + private val suspendSettings: SuspendSettings = settings.toSuspendSettings(), + private val json: Json, +) : DataStore diff --git a/core/datastore/src/commonMain/kotlin/club/nito/core/datastore/SettingsFactory.kt b/core/datastore/src/commonMain/kotlin/club/nito/core/datastore/SettingsFactory.kt new file mode 100644 index 00000000..18ec6400 --- /dev/null +++ b/core/datastore/src/commonMain/kotlin/club/nito/core/datastore/SettingsFactory.kt @@ -0,0 +1,9 @@ +package club.nito.core.datastore + +import com.russhwolf.settings.Settings + +internal fun createSettings() = try { + Settings() +} catch (e: Exception) { + error("Failed to create default settings") +} diff --git a/core/datastore/src/commonMain/kotlin/club/nito/core/datastore/di/DataStoreModule.kt b/core/datastore/src/commonMain/kotlin/club/nito/core/datastore/di/DataStoreModule.kt new file mode 100644 index 00000000..6a3dd9c3 --- /dev/null +++ b/core/datastore/src/commonMain/kotlin/club/nito/core/datastore/di/DataStoreModule.kt @@ -0,0 +1,19 @@ +package club.nito.core.datastore.di + +import club.nito.core.common.nitoJsonSettings +import club.nito.core.datastore.DataStore +import club.nito.core.datastore.SettingsDataStore +import club.nito.core.datastore.createSettings +import com.russhwolf.settings.ExperimentalSettingsApi +import org.koin.core.module.Module +import org.koin.dsl.module + +public val dataStoreModule: Module = module { + single { + @OptIn(ExperimentalSettingsApi::class) + SettingsDataStore( + settings = createSettings(), + json = nitoJsonSettings + ) + } +} diff --git a/core/datastore/src/iosMain/kotlin/club/nito/core/datastore/.gitkeep b/core/datastore/src/iosMain/kotlin/club/nito/core/datastore/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/core/network/src/commonMain/kotlin/club/nito/core/network/InstantSerializer.kt b/core/network/src/commonMain/kotlin/club/nito/core/network/InstantSerializer.kt deleted file mode 100644 index 31acb44e..00000000 --- a/core/network/src/commonMain/kotlin/club/nito/core/network/InstantSerializer.kt +++ /dev/null @@ -1,21 +0,0 @@ -package club.nito.core.network - -import kotlinx.datetime.Instant -import kotlinx.serialization.KSerializer -import kotlinx.serialization.descriptors.PrimitiveKind -import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.encoding.Decoder -import kotlinx.serialization.encoding.Encoder - -internal object InstantSerializer : KSerializer { - override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor( - "Date", - PrimitiveKind.LONG, - ) - - override fun serialize(encoder: Encoder, value: Instant) { - encoder.encodeString(value.toString()) - } - override fun deserialize(decoder: Decoder): Instant = Instant.parse(decoder.decodeString()) -} diff --git a/core/network/src/commonMain/kotlin/club/nito/core/network/SupabaseClient.kt b/core/network/src/commonMain/kotlin/club/nito/core/network/SupabaseClient.kt index 1eac5424..7314aad7 100644 --- a/core/network/src/commonMain/kotlin/club/nito/core/network/SupabaseClient.kt +++ b/core/network/src/commonMain/kotlin/club/nito/core/network/SupabaseClient.kt @@ -6,11 +6,7 @@ import io.github.jan.supabase.gotrue.Auth import io.github.jan.supabase.postgrest.Postgrest import io.github.jan.supabase.realtime.Realtime import io.github.jan.supabase.serializer.KotlinXSerializer -import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonNamingStrategy -import kotlinx.serialization.modules.SerializersModule -import kotlinx.serialization.modules.contextual internal fun createNitoSupabaseClient( json: Json, @@ -24,22 +20,3 @@ internal fun createNitoSupabaseClient( defaultSerializer = KotlinXSerializer(json) } - -internal fun createNitoKtorJsonSettings(): Json = Json { - encodeDefaults = true - isLenient = true - allowSpecialFloatingPointValues = true - allowStructuredMapKeys = true - prettyPrint = false - useArrayPolymorphism = false - ignoreUnknownKeys = true - coerceInputValues = true - useAlternativeNames = false - - @OptIn(ExperimentalSerializationApi::class) - namingStrategy = JsonNamingStrategy.SnakeCase - - serializersModule = SerializersModule { - contextual(InstantSerializer) - } -} diff --git a/core/network/src/commonMain/kotlin/club/nito/core/network/di/SupabaseClientModule.kt b/core/network/src/commonMain/kotlin/club/nito/core/network/di/SupabaseClientModule.kt index 3ae8925c..dc999507 100644 --- a/core/network/src/commonMain/kotlin/club/nito/core/network/di/SupabaseClientModule.kt +++ b/core/network/src/commonMain/kotlin/club/nito/core/network/di/SupabaseClientModule.kt @@ -1,6 +1,6 @@ package club.nito.core.network.di -import club.nito.core.network.createNitoKtorJsonSettings +import club.nito.core.common.nitoJsonSettings import club.nito.core.network.createNitoSupabaseClient import io.github.jan.supabase.SupabaseClient import io.github.jan.supabase.gotrue.Auth @@ -16,7 +16,7 @@ public val supabaseClientModule: Module = module { ) } single { - createNitoKtorJsonSettings() + nitoJsonSettings } single { get().auth diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3fd6ccda..9a692df9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,6 +18,7 @@ ktor = "2.3.6" ksp = "1.9.21-1.0.15" firebaseBom = "32.2.3" multiplatformFirebase = "1.10.4" +multiplatformSettings = "1.1.1" kermit = "2.0.2" okHttp = "4.12.0" ossLicensesPlugin = "0.10.6" @@ -68,6 +69,8 @@ androidxWindow = { module = "androidx.window:window", version = "1.1.0" } androidxSplashScreen = { module = "androidx.core:core-splashscreen", version.ref = "androidxSplashScreen" } javaPoet = { module = "com.squareup:javapoet", version = "1.13.0" } +multiplatformSettingsNoArg = { module = "com.russhwolf:multiplatform-settings-no-arg", version.ref = "multiplatformSettings" } +multiplatformSettingsCoroutines = { module = "com.russhwolf:multiplatform-settings-coroutines", version.ref = "multiplatformSettings" } kotlinSerializationJson = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerialization" } ktorClientCore = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 2e906dc3..6e42a269 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -28,6 +28,7 @@ include( ":app:backend", ":core:common", ":core:database", + ":core:datastore", ":core:network", ":core:data", ":core:model",